tinytuya
tinytuya copied to clipboard
Yeeuu K1 Smart Lock Box
Hi,
I'm trying to use tinytuya to check status and open a Smart Lock Box (https://www.yeeuu.com/products/yeeuu-k1-smart-lock-box ) connected to Wifi via a Bluetooth Bridge (https://www.yeeuu.com/products/yeeuu-h1-gateway ). I have imported both of them via tuya.com and see both in devices.json
{ "name": "YEEUU K1 Smart Lock Box", "id": "id-made-of-22-chars", "key": "mykey" }, { "name": "YEEUU H1 WiFi Bridge", "id": "id-made-of-22-chars", "key": "same key as above" },
so I have tried using the suggestion to handle 22-chars-id's: `import tinytuya
DEVICEID = "the id of the K1" DEVICEIP = "the IP" DEVICEKEY = "mykey" DEVICEVERS = "3.3"
a = tinytuya.CoverDevice(DEVICEID, DEVICEIP, DEVICEKEY, 'device22') a.set_version(3.3) a.set_dpsUsed({"1": None}) # This needs to be a datapoint available on the device data = a.status() print(data)`
but when I run this I get a timeout
python3 k1test.py Exceeded tinytuya retry limit (5) Traceback (most recent call last): File "/usr/local/lib/python3.7/dist-packages/tinytuya/__init__.py", line 353, in _send_receive data = self.socket.recv(1024) # try again socket.timeout: timed out Traceback (most recent call last): File "k1test.py", line 11, in <module> data = a.status() File "/usr/local/lib/python3.7/dist-packages/tinytuya/__init__.py", line 556, in status data = self._send_receive(payload) File "/usr/local/lib/python3.7/dist-packages/tinytuya/__init__.py", line 353, in _send_receive data = self.socket.recv(1024) # try again socket.timeout: timed out
Any clue of what I am doing wrong? I also haven't tried "open_cover(switch=1):" yet because I am physically far away in VPN...
Thanks! R
Did you try without 'device22'? Not all 22 character IDs require that modification. Also what response do you get from the device when you run these:
python -m tinytuya wizard # pulls key from Tuya cloud and attempt to scan your Tuya devices
python -m tinytuya scan
Hi thanks for helping - without "device22" I get
Traceback (most recent call last): File "k1test.py", line 11, in <module> data = a.status() File "/usr/local/lib/python3.7/dist-packages/tinytuya/__init__.py", line 589, in status result = json.loads(result) File "/usr/lib/python3.7/json/__init__.py", line 348, in loads return _default_decoder.decode(s) File "/usr/lib/python3.7/json/decoder.py", line 337, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/usr/lib/python3.7/json/decoder.py", line 355, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
and if I change DEVICEVERS to 3.1 then I get
Unexpected status() payload=b'\xb7_\x9e\x0c\x0cY\xbd(|\xb9\xc6R\x02sxz\xe2%?>\x10\x01\xe03a\x08\xab\xb3M]\xf7\x8c' b'\xb7_\x9e\x0c\x0cY\xbd(|\xb9\xc6R\x02sxz\xe2%?>\x10\x01\xe03a\x08\xab\xb3M]\xf7\x8c'
As for the 2 commands above: `root@erdogliani:~# python3 -m tinytuya wizard TinyTuya Setup Wizard [1.1.3]
Existing settings:
API Key=mykey
Secret=mysecret
DeviceID=mydevice
Region=eu
Use existing credentials (Y/n): Y
Device Listing
[ { "name": "YEEUU K1 Smart Lock Box", "id": "k1id", "key": "key" }, { "name": "YEEUU H1 WiFi Bridge", "id": "h1key", "key": "key" } ]
Saving list to devices.json 2 registered devices saved
Poll local devices? (Y/n): Scanning local network for Tuya devices... 0 local devices discovered
Polling local devices... [YEEUU K1 Smart Lock Box] - 0 - Error: No IP found [YEEUU H1 WiFi Bridge] - 0 - Error: No IP found
Saving device snapshot data to snapshot.json
Done.
root@erdogliani:~# python3 -m tinytuya scan
TinyTuya (Tuya device scanner) [1.1.3]
[Loaded devices.json - 2 devices]
Scanning on UDP ports 6666 and 6667 for devices (15 retries)...
Scan Complete! Found 0 devices.
root@erdogliani:~# `
But from RPi I can ping the static IP of the Bluetooth bridge (and in fact there is dialogue with tinituya as you see above)
Thanks!! R
I agree, you are getting something back from the bridge. You can set tinytuya to try longer to detect device on the network with something like python3 -m tinytuya scan 50
- It would be interesting to see if the bridge is broadcasting.
Pull the latest tinytuya 1.1.4 code (git pull) and try your code again but set the debug mode:
import tinytuya
print("TinyTuya (Tuya Interface) [%s]\n"%tinytuya.__version__)
tinytuya.set_debug(True)
DEVICEID = "the id of the K1"
DEVICEIP = "the IP"
DEVICEKEY = "mykey"
DEVICEVERS = "3.3"
a = tinytuya.CoverDevice(DEVICEID, DEVICEIP, DEVICEKEY, 'device22')
a.set_version(3.3)
a.set_dpsUsed({"1": None}) # This needs to be a datapoint available on the device
data = a.status()
print(data)
I don't know what DPS index values we should see from a lock. You can try changing the number in the set_dpsUsed() function.
a.set_dpsUsed({"8": None})
Hi I tried DPS from 0 to 9 and no change...
TinyTuya (Tuya Interface) [1.1.4]
Exceeded tinytuya retry limit (5)
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/tinytuya-1.1.4-py3.7.egg/tinytuya/__init__.py", line 374, in _send_receive
data = self.socket.recv(1024) # try again
socket.timeout: timed out
Traceback (most recent call last):
File "test.py", line 13, in <module>
data = a.status()
File "/usr/local/lib/python3.7/dist-packages/tinytuya-1.1.4-py3.7.egg/tinytuya/__init__.py", line 580, in status
data = self._send_receive(payload)
File "/usr/local/lib/python3.7/dist-packages/tinytuya-1.1.4-py3.7.egg/tinytuya/__init__.py", line 374, in _send_receive
data = self.socket.recv(1024) # try again
socket.timeout: timed out
root@rpi-1:/home/pi#
What is surprising to me is that if I change version to 3.1 I get immediate answer>
TinyTuya (Tuya Interface) [1.1.4]
Unexpected status() payload=b'z\xe2%?>\x10\x01\xe03a\x08\xab\xb3M]\xf7\x8c'
b'z\xe2%?>\x10\x01\xe03a\x08\xab\xb3M]\xf7\x8c'
root@rpi-1:/home/pi#
What do you think?
Thanks!! R
It seems you already ran the wizard above, but I have seen that when the key is incorrect or has changed (the device is not responding to a encrypted payload that is encoded with the wrong key). Verify that you have the right local key, but I suspect you already tried this.
I may have some changes in the next release, 1.1.5 that could help, including additional debugging. I haven't been able to find much about this particular Tuya device, so I appreciate your feedback and details.
Hi thanks again. First of all, I realised a mistake - I was trying to scan from a RPi which is in a different network (DMZ). Here is the result of a proper scan
ID = <deviceid>, Product ID = euthqsxg, Version = 3.3
No Stats for 10.16.3.233: Unable to poll
This said, I noticed that with 'device22' it goes into timeout, without 'device22' the result is different:
TinyTuya (Tuya Interface) [1.1.4]
Traceback (most recent call last):
File "test.py", line 13, in <module>
data = a.status()
File "/usr/local/lib/python3.7/dist-packages/tinytuya-1.1.4-py3.7.egg/tinytuya/__init__.py", line 613, in status
result = json.loads(result)
File "/usr/lib/python3.7/json/__init__.py", line 348, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.7/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.7/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
root@rpi-1:/home/pi#
Does this ring a bell to you?
Thanks R
Thanks for the update. If you remove 'device22' make sure you also remove the set_dpsUsed() call. Also, I would try swtich to the more generic OutletDevice class. Are you using something similar to this code?
import tinytuya
print("TinyTuya (Tuya Interface) [%s]\n"%tinytuya.__version__)
tinytuya.set_debug(True)
DEVICEID = "the id of the K1"
DEVICEIP = "the IP"
DEVICEKEY = "mykey"
DEVICEVERS = "3.3"
a = tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
a.set_version(3.3)
data = a.status()
print(data)
Also, just to check in again because this seems to always trip us up, make sure the local DEVICEKEY is correct. If it is not, the error you are getting is related to that.
python -m tinytuya wizard
Finally, there is a chance that the device does not respond to local Tuya commands and is only cloud based. I just can't find much information about Tuya Smart Locks. If you happen to find anything, please post here so we can continue to research.
Jason!
I wanted to thank you because I am using you tinytuya to build a polyglot node server on Universal devices ISY.
I have taken your code and made a tester for authentication and refresh token and am able to control the TreatLife product for switches and led lights.
I am only 20 months old in Python years and 60 in human years and wanted once again to THANK YOU for your HARD WORK!
Nothing to show yet as there is always another project to do lol!
Highest Regards, Steve Bailey
On Feb 6, 2021, at 9:45 AM, Jason Cox [email protected] wrote:
Thanks for the update. If you remove 'device22' make sure you also remove the set_dpsUsed() call. Also, I would try swtich to the more generic OutletDevice class. Are you using something similar to this code?
import tinytuya
print("TinyTuya (Tuya Interface) [%s]\n"%tinytuya.version) tinytuya.set_debug(True) DEVICEID = "the id of the K1" DEVICEIP = "the IP" DEVICEKEY = "mykey" DEVICEVERS = "3.3"
a = tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY) a.set_version(3.3) data = a.status() print(data) Also, just to check in again because this seems to always trip us up, make sure the local DEVICEKEY is correct. If it is not, the error you are getting is related to that.
python -m tinytuya wizard Finally, there is a chance that the device does not respond to local Tuya commands and is only cloud based. I just can't find much information about Tuya Smart Locks. If you happen to find anything, please post here so we can continue to research.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/jasonacox/tinytuya/issues/26#issuecomment-774513869, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMRVB3OUFYCC7FIFZUPTMGLS5V53DANCNFSM4WXEVXDA.
Hi I have done more tests using the latest 1.2.1 release, here are my findings
Using device22
a.set_version(3.3)
a.set_dpsUsed({"1": None}) # This needs to be a datapoint available on the device
data = a.status()
print(data)
returns
DEBUG:received null payload (b'\x00\x00U\xaa\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\x0c\x00\x00\x00\x00\xed\xbd\xfa\x0f\x00\x00\xaaU'), fetch new one
DEBUG:Timeout or exception fetching payload - retry 6/5
DEBUG:Exceeded tinytuya retry limit (5)
DEBUG:ERROR Timeout Waiting for Device - 902 - payload: "Check Device Key"
DEBUG:status received data={'Error': 'Timeout Waiting for Device', 'Err': '902', 'Payload': 'Check Device Key'}
{'Error': 'Timeout Waiting for Device', 'Err': '902', 'Payload': 'Check Device Key'}
root
both when I use the SmartLock device key and also if I try with the device key of the WifiBluetoothBridge.
If on the other hand I remove the device22 option, I get
root@rpi-1:/home/pi# python3 test.py
TinyTuya (Tuya Interface) [1.2.1]
DEBUG:status() entry (dev_type is default)
DEBUG:building payload=b'{"gwId":"deviceid","devId":"deviceid","uid":"deviceid","t":"1613213025"}'
DEBUG:payload generated=b'\x00\x00U\xaa\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x88nE\x9d2\x83~@<f\xa3\x0e\x8eY\x05G\xa3\x99\xda\x03\xb6#\xd27=*\x13\xcf\xb8\xcc\x1cO\xfd\x07\x05\x81\xe1"Gu4tm\x9e\xa7\xe2F\x8207\x8e9\xf7t\xd6{9)\xa7,k\x11{\xcbnx\x06\xc7o0\x8c\xecQ\n\xea\xbd\xa5\xfd\xdc`\x1c\x99\xda\x03\xb6#\xd27=*\x13\xcf\xb8\xcc\x1cO\xfdB\xcb\xde<b~bj,LbR\xc4\xdc\x93\xaf%\xc6\xfe\xb0l#\xc0_\xc1\x93\x18\xcbPE\xf0\xc4\xadJ\x87\xbf\x00\x00\xaaU'
DEBUG:received data=b'\x00\x00U\xaa\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x1c\x00\x00\x00\x019m\xac\x84"\x13\xcfO3\x88\xb1\xb5\x0e\x1382|ca\x9e\x00\x00\xaaU'
DEBUG:raw unpacked message = TuyaMessage(seqno=0, cmd=10, retcode=1, payload=b'9m\xac\x84"\x13\xcfO3\x88\xb1\xb5\x0e\x1382', crc=2086887838)
DEBUG:decode payload=b'9m\xac\x84"\x13\xcfO3\x88\xb1\xb5\x0e\x1382'
DEBUG:decrypting=b'9m\xac\x84"\x13\xcfO3\x88\xb1\xb5\x0e\x1382'
DEBUG:decrypted 3.3 payload='devid not found'
DEBUG:decoded results='devid not found'
DEBUG:ERROR Invalid JSON Response from Device - 900 - payload: "devid not found"
DEBUG:status received data={'Error': 'Invalid JSON Response from Device', 'Err': '900', 'Payload': 'devid not found'}
{'Error': 'Invalid JSON Response from Device', 'Err': '900', 'Payload': 'devid not found'}
root@rpi-1:/home/pi#
Again, same result with deviceid of the lock and of the wifi bridge...
What do you think?
Thanks! R
This is progress! I have a hypothesis.
- The response you are getting "devid not found" is coming from the wifi bridge which means that it is communicating correctly if you do not use the 'device22' and the payload is decrypting correctly. That means you do not want to use device22 and the key is right.
- The response tells us that it doesn't like the DevID you are sending (it cannot find it). I assume you tried the devid of the lock but used the IP address of the wifi bridge? If so, that could mean that the wifi-bridge wants to see itself as the gwID. The command payload looks like this:
"command": {"gwId": "", "devId": "", "uid": "", "t": ""},
The generate_payload()
function assumes that all the IDs are the same DEVICEID so it loads them in that for that payload. I suspect it wants a different gwId
than the devId
, as a way to tell the wifi-bridge (the gateway?) which device we want to poll.
If you want to experiment, try to edit generate_payload()
to allow setting gwId
and devId
independently and see if that works. If that works, I could add gwId
as an optional parameter to generate_payload()
for this type of use case.
FYI manually generating payload and sending (after you edit generate_payload()):
# Connect to the device - replace with real values
d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(3.3)
# Generate the payload to send - likely want to send gwId as optional param
payload=d.generate_payload(tinytuya.DP_QUERY, gwId)
# Send the payload to the device
d._send_receive(payload)
wow I start to see light at the end of the tunnel, thanks! :-)
So I tried the above
d.set_version(3.3)
# Generate the payload to send - likely want to send gwId as optional param
payload=d.generate_payload(tinytuya.DP_QUERY, 'MygwID')
# Send the payload to the device
d._send_receive(payload)
#a.set_dpsUsed({"1": None}) # This needs to be a datapoint available on the device
data = d.status()
print(data)
and still got error because gwId is still the deviceID while the gwID ended up in the dps:
DEBUG:building` payload=b'{"gwId":"LockID","devId":"LockID","uid":"LockID","t":"1613298400","dps":"MygwID"}'
Thanks!! R
That's correct, I haven't updated generate_payload() to handle the gwID option. I may have time to do that today.
If you git pull
the latest version (1.2.2) I have added those parameters to generate_payload(). You should be able to do something like this:
import tinytuya
print(tinytuya.version)
tinytuya.set_debug(True)
print('\Connecting...')
d = tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
print(d)
d.set_version(3.3)
# Generate the payload (try different device or wifi gateway IDs here)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId='1234', devId='ef12', uid='fab0')
# Send the payload to the device
data = d._send_receive(payload)
print('\nResponse from DP_QUERY: %r' % data)
unfortunately we're not there yet :-(
I tried with both devID and gwID in the uid field but I always get the same error DEBUG:ERROR Invalid JSON Response from Device - 900 - payload: "devid not found"
Thanks! R
TinyTuya (Tuya Interface) [1.2.2] DEBUG:building payload=b'{"gwId":"mygwID","devId":"mydevID","uid":"mydevID","t":"1613333557"}'
Ok, if you haven't already, you might try all the permutation just in case it is looking for something else, like...
a = 'DeviceID'
b = 'BridgeID'
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=a, devId=a, uid=a)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=a, devId=a, uid=b)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=a, devId=b, uid=a)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=a, devId=b, uid=b)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=b, devId=a, uid=a)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=b, devId=a, uid=b)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=b, devId=b, uid=a)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=b, devId=b, uid=b)
It seems like the OutletDevice is right since you are getting a response (even if it is an error), but you may even want to try the same with the connection in combination with the above, something like...
d = tinytuya.OutletDevice(a, b_DEVICEIP, a_DEVICEKEY)
d = tinytuya.OutletDevice(b, b_DEVICEIP, a_DEVICEKEY)
d = tinytuya.OutletDevice(a, b_DEVICEIP, b_DEVICEKEY)
d = tinytuya.OutletDevice(b, b_DEVICEIP, b_DEVICEKEY)
Hi, I tried the 8 permutations above and unfortunately always got back the same error
{'Error': 'Invalid JSON Response from Device', 'Err': '900', 'Payload': 'devid not found'}
As for the other attempt, devicekey is the same in bridge and lock, only deviceid changes...
Thanks! R
Drats! I was hoping we were on to something. Logically, since this is a bridge and the device key is same for both, I could imagine that it could work by sending the DeviceID to the Bridge (IP address) - Basically treating it as if it was just a proxy for the device:
d = tinytuya.OutletDevice(DEVICE_ID, BRIDGE_IP, DEVICE_KEY)
d.set_version(3.3)
payload=d.generate_payload(tinytuya.DP_QUERY, data=None, gwId=DEVICE_ID, devId=DEVICE_ID, uid=DEVICE_ID)
data = d._send_receive(payload)
# or essentially
d = tinytuya.OutletDevice(DEVICE_ID, BRIDGE_IP, DEVICE_KEY)
d.set_version(3.3)
data = d.status()
One additional idea I had, try sending the different known Tuya commands to the bridge to see if any of them provide any other response - these are the different commands tinytuya supports (there are others):
CONTROL: { # Set Control Values on Device
"hexByte": "07",
"command": {"devId": "", "uid": "", "t": ""}
},
STATUS: { # Get Status from Device
"hexByte": "08",
"command": {"gwId": "", "devId": ""}
},
HEART_BEAT: {
"hexByte": "09",
"command": {"gwId": "", "devId": ""}
},
UPDATEDPS: {
"hexByte": "12",
"command": {"dpId": [18, 19, 20]},
},
DP_QUERY: { # Get Data Points from Device
"hexByte": "0a",
"command": {"gwId": "", "devId": "", "uid": "", "t": ""},
},
CONTROL_NEW: {
"hexByte": "0d",
"command": {"devId": "", "uid": "", "t": ""}
},
DP_QUERY_NEW: {
"hexByte": "0f",
"command": {"devId": "", "uid": "", "t": ""}
},
command = tinytuya.CONTROL
payload=d.generate_payload(command, data=None, gwId='1234', devId='ef12', uid='fab0')
data = d._send_receive(payload)
print('Response: %r' % data)
The fact that the bridge is responding with a readable payload seems to indicate there is some support for local Tuya commands but it is also possible that it is designed to only work with the Tuya Cloud.
Hi thanks again, here is the feedback when using payload=d.generate_payload(command, data=None, gwId='TheGWid', devId='TheDeviceID', uid='TheDeviceID')
- CONTROL: data format error
- STATUS: parse data error
- HEART_BEAT: Check Device Key
- UPDATEDPS: query dp failed
- DP_QUERY: devid not found
- CONTROL_NEW: AttributeError: 'OutletDevice' object has no attribute 'dpsUsed'
- DP_QUERY_NEW: parse data error
so I tried the 4 more pomising ones with gwID in the uid field but nothing changed:
- CONTROL: data format error
- STATUS: parse data error
- UPDATEDPS: query dp failed
- DP_QUERY_NEW: parse data error
Thanks! R
Thanks for trying. I would try
payload=d.generate_payload(command, data=None, gwId='TheDeviceID', devId='TheDeviceID', uid='TheDeviceID')
Using the above commands again.
Also, the CONTROL_NEW response indicated we need to set dpsUsed() - so you could try adding this:
payload=d.generate_payload(tinytuya.CONTROL_NEW, data=None, gwId='TheDeviceID', devId='TheDeviceID', uid='TheDeviceID')
d.set_dpsUsed({"1": None})
data = d._send_receive(payload)
print('Response: %r' % data)
Might even try that one with the different gwId's.
Hi unfortunately I get error
File "/root/tinytuya/tinytuya/__init__.py", line 756, in generate_payload
json_data['dps'] = self.dpsUsed
AttributeError: 'OutletDevice' object has no attribute 'dpsUsed'
I tried also with CoverDevice but same error...
Thanks! R
I'm sorry, my error. The set_dpsUsed() should be before generate payload since the dps values need to be encoded into the payload:
d.set_dpsUsed({"1": None})
payload=d.generate_payload(tinytuya.CONTROL_NEW, data=None, gwId='TheDeviceID', devId='TheDeviceID', uid='TheDeviceID')
data = d._send_receive(payload)
print('Response: %r' % data)
ok but now I'm stuck with Response: {'Error': 'Timeout Waiting for Device', 'Err': '902', 'Payload': 'Check Device Key'}
I tried all 8 combinations of device/gateway id in the 3 fields...
Thanks! R