Open lmcd opened 6 months ago
Ok I've managed to capture packets from the iOS app, and noticed that when scrubbing through colours with your finger held down on the wheel, it uses DP ID 28
- which I suspect is a more efficient path for sending lots of values in quick succession.
When you just tap a single colour, it instead sets the mode with DP 21
then then colour with DP 24
Ok, can confirm DP 28
looks way better visually when quickly moving between colours. The interpolation/transition is different and looks visually a lot nicer.
With DP 28
, the device doesn't send any response or acknowledgement.
But there still seems to be a build of packets that are getting delayed in their delivery to the device. Is this something to do with the way TCP/IP works in Python? Is there some kind of bottleneck in the library?
I'm also throttling colour changes to at most 1 every 50ms, so as to not overload the device. This doesn't help with the delay.
Ok - it seems that the Smart Life device is throttling every 150ms. This now seems to create a smooth result.
Here is the payload structure for DP 28
:
Example: 1002803e803e800000000
Structure: THHHHSSSSVVVVWWWWBBBB
T = Transition (1 = Fade, 0 = Instant)
H = Hue (0-1000)
S = Saturation (0-1000)
V = Value (0-1000)
W = Warmth (0-1000)
B = Brightness (0-1000)
I'll submit a PR when I have time.
WWWW
and BBBB
are all zero if controlling the white/warmth level. HHHH
, SSSS
, VVVV
are all zero if controlling the colour.
Also, values set through DP 28
are not persisted to memory and aren't restored when the bulb is switched off and on again. To have a colour value 'saved', and broadcast out to the Smart Life app, DP 24
must be used.
28 in the readme is listed as 'Debugger'. This is not true at all, and is used often in the Smart Life app as a way of interactively controlling the light.
Hi @lmcd - thanks for this!
Can you post an example of your code?
Have you tried using the nowait=True
settings on the d.set_colour()
call?
import tinytuya
# Connect to Tuya BulbDevice
d = tinytuya.BulbDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(float(DEVICEVERS))
# Keep socket connection open between commands
d.set_socketPersistent(True)
# Flip through colors of rainbow - set_colour(r, g, b):
print('\nColor Test - Cycle through rainbow')
rainbow = {"red": [255, 0, 0], "orange": [255, 127, 0], "yellow": [255, 200, 0],
"green": [0, 255, 0], "blue": [0, 0, 255], "indigo": [46, 43, 95],
"violet": [139, 0, 255]}
for x in range(10):
for i in rainbow:
r = rainbow[i][0]
g = rainbow[i][1]
b = rainbow[i][2]
print(' %s (%d,%d,%d)' % (i, r, g, b))
d.set_colour(r, g, b, nowait=True)
time.sleep(0.05) # 50 ms
print('')
Yes I was already using that. All the lag has now been resolved, and my app is now doing exactly what the Smart Life app is doing:
With these changes I'm getting identical behaviour to that of the Smart Life iOS experience.
Here's roughly what the code is on the tinytuya
side looks like. I'll clean it up as a PR soon:
def set_colour_fast(self, r, g, b):
"""
Set colour of an rgb bulb.
Args:
r(int): Value for the colour Red as int from 0-255.
g(int): Value for the colour Green as int from 0-255.
b(int): Value for the colour Blue as int from 0-255.
"""
if not self.has_colour:
log.debug("set_colour: Device does not appear to support color.")
# return error_json(ERR_FUNCTION, "set_colour: Device does not support color.")
if not 0 <= r <= 255:
return error_json(
ERR_RANGE,
"set_colour: The value for red needs to be between 0 and 255.",
)
if not 0 <= g <= 255:
return error_json(
ERR_RANGE,
"set_colour: The value for green needs to be between 0 and 255.",
)
if not 0 <= b <= 255:
return error_json(
ERR_RANGE,
"set_colour: The value for blue needs to be between 0 and 255.",
)
hexvalue = "1" + BulbDevice._rgb_to_hexvalue(r, g, b, self.bulb_type) + "00000000"
payload = self.generate_payload(
CONTROL,
{
self.DPS_INDEX_CONTROL[self.bulb_type]: hexvalue,
},
)
self._send_receive_quick(payload, None)
I'm actually interacting with Python via Swift as I'm building a user interface for the Mac.
Also, I figured this out by doing a packet capture on an actual iOS device whilst playing with the Smart Life app. You can do this on a physically tethered iPhone over USB using the instructions here: https://developer.apple.com/documentation/network/recording_a_packet_trace
Then open the pcap in Wireshark and resave again as pcap. Then you can parse the file with pcap_parse.py
This is brilliant @lmcd !! Great research!
Yes, please submit a PR. 🙏
I've built a similar GUI application to the Tuya Smart Life app with a hue colour wheel to quickly scrub through values.
Setting a single colour via
tinytuya
transitions immediately on the bulb, but when I scrub through many values quickly, it starts to lag and doesn't transition gracefully. The Smart Life app on the other hand can handle super-fast scrubbing, and the transitions are very smooth. Is the Smart Life app doing something different to achieve a more graceful effect?In my code I'm opening a persistent connection, then sending
set_colour
for each colour change.