jasonacox / tinytuya

Python API for Tuya WiFi smart devices using a direct local area network (LAN) connection or the cloud (TuyaCloud API).
MIT License
1.03k stars 183 forks source link

Nedis Zigbee Gateway can't connect using tinytuya #31

Closed khenrix closed 2 years ago

khenrix commented 3 years ago

Hello, I'm working on a project where I would like to read temperature/humidity from a sensor connected through a Nedis Zigbee Gateway. I am not able to connect the gateway. I this possible with tinytuya?

I would be very thankful if someone could take a look or at least tell me if it's supported.

Simply running it on my PC using Python 3.8.5

python -m tinytuya

TinyTuya (Tuya device scanner) [1.1.4]

Scanning on UDP ports 6666 and 6667 for devices (15 retries)...

3.3 Device Found [Valid payload]: 192.168.8.XXX
    ID = XXXXXX, Product ID = XXXXX, Version = 3.3
    No Stats - Device Key required to poll for status

Scan Complete!  Found 1 devices.

Here is a snapshot created by the wizard

Poll local devices? (Y/n): Y

Scanning local network for Tuya devices...
    1 local devices discovered

Polling local devices...
    [Temperature and humidity sensor] - 0 - Error: No IP found
    [Zigbee Smart Gateway] - 192.168.8.XXX - No Response

>> Saving device snapshot data to snapshot.json

Done.
{
    "timestamp": 1612792884.4107103,
    "devices": [
        {
            "name": "Temperature and humidity sensor",
            "ip": 0,
            "ver": 0,
            "id": "XXXX",
            "key": "XXXX"
        },
        {
            "name": "Zigbee Smart Gateway",
            "ip": "192.168.8.XXX",
            "ver": "3.3",
            "id": "XXXX",
            "key": "XXXX"
        }
    ]
}

Checking the SmartLife app I was able to find another IP-adress for the Gateway (5.240.XXX.XX) which I am able to ping at least. Pinging 192.168.8.XXX does not work.

Tested the following code on the two different IP-addresses

import tinytuya

if __name__ == "__main__":
    d = tinytuya.OutletDevice('DEVICE_ID', 'IP_ADRESS', 'DEVICE_SECRET')
    d.set_version(3.3)
    data = d.status()  
    print(data)

192.168.8.XXX

Traceback (most recent call last):
  File ".\main.py", line 6, in <module>
    data = d.status()
  File "C:\Users\Krille\git\temper\venv\lib\site-packages\tinytuya\__init__.py", line 580, in status
    data = self._send_receive(payload)
  File "C:\Users\Krille\git\temper\venv\lib\site-packages\tinytuya\__init__.py", line 364, in _send_receive
    self._get_socket(False)
  File "C:\Users\Krille\git\temper\venv\lib\site-packages\tinytuya\__init__.py", line 350, in _get_socket
    self.socket.connect((self.address, self.port))
socket.timeout: timed out
(venv) PS C:\Users\Krille\git\temper> 

5.240.XXX.XX

Traceback (most recent call last):
  File ".\main.py", line 6, in <module>
    data = d.status()
  File "C:\Users\Krille\git\temper\venv\lib\site-packages\tinytuya\__init__.py", line 613, in status
    result = json.loads(result)
  File "c:\users\krille\appdata\local\programs\python\python38-32\lib\json\__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "c:\users\krille\appdata\local\programs\python\python38-32\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "c:\users\krille\appdata\local\programs\python\python38-32\lib\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)

Tried the following as well:

import tinytuya

if __name__ == "__main__":
    d = tinytuya.OutletDevice('DEVICE_ID', 'IP_ADRESS', 'DEVICE_SECRET', 'device22')
    d.set_version(3.3)
    d.set_dpsUsed({"1": None})
    data = d.status()  
    print(data)
Traceback (most recent call last):
  File ".\main.py", line 7, in <module>
    data = d.status()
  File "C:\Users\Krille\git\temper\venv\lib\site-packages\tinytuya\__init__.py", line 580, in status
    data = self._send_receive(payload)
  File "C:\Users\Krille\git\temper\venv\lib\site-packages\tinytuya\__init__.py", line 374, in _send_receive
    data = self.socket.recv(1024)  # try again
socket.timeout: timed out
jasonacox commented 3 years ago

The 192.x.x.x address is your local network address. The other address (5.x.x.x) is a public internet address that your router is using to connect your local network to the internet. Tuya devices are designed to work with the Tuya Cloud. For some Tuya devices, they also provide a local network API, which is what tinytuya uses to talk to these devices.

I suspect the Zigbee Gateway does not fully support local API access. It is advertising his IP address which is why you see it in the tinytuya scan. Most Tuya sensors I have discovered are only designed to send to the Tuya Cloud, not be available locally.

One suggestion - the data may be showing up in the UDP packet that it advertises even though you cannot pull the status directly from the device. Try upgrading to the latest tinytuya release (1.2.0) and activate the DEBUG mode with scan:

python -m pip install --upgrade tinytuya
import tinytuya
tinytuya.set_debug()
tinytuya.scan()

It may also be interesting to try your scripts above with the tinytuya.set_debug() setting to see the details of the payloads and transactions.

jasonacox commented 3 years ago

Additional idea - Did you see a different key for the gateway and device or are they the same? I would try something like this:

import tinytuya

tinytuya.set_debug(True)  # or for windows command prompt tinytuya.set_debug(True,False)

d = tinytuya.OutletDevice('DEVICE_ID', 'GATEWAY_IP', 'DEVICE_SECRET')
d.set_version(3.3)
data = d.status()  
print(data)

It could be that the gateway is just a proxy for the device. You will need to use the local 192.168.8.XXX address for the GATEWAY_IP.

Also, if you haven't already, I recommend pulling latest tinytuya:

pip install --upgrade tinytuya
khenrix commented 3 years ago

Hi, sorry for the late reply and thank you for your suggestions.

I upgraded to the latest tinytuya package 1.2.2 och ran the scan again. It produced the following:

TinyTuya (Tuya device scanner) [1.2.2]

[Loaded devices.json - 2 devices]

Scanning on UDP ports 6666 and 6667 for devices (15 retries)...

Zigbee Smart Gateway [Valid payload]: 192.168.X.XXX
    ID = XXXX, Product ID = YYYY, Version = 3.3
DEBUG:status() entry (dev_type is default)
DEBUG:building payload=b'{"gwId":"XXXX","devId":"XXXX","uid":"XXXX","t":"1614675933"}'
DEBUG:payload generated=b'\x00\x00U\xaa\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x880v\xcd\x89)\x07\x99\x85"\xf61\xa5\x87YE\xc4\x1cv\xb9O?\xfaR\xb4d\xda\r\'\x9e5;\xa7{R\xf4\x99\x13\x00\x88,dRlw\xc1\x9b\xfaT\x16B"\x8a\x14\x99\xca\x98\x8b\xf8\xcbP\xd9\xc0f)\x0b\xd9\x19\rp_\xa1\x96\x95y\x96kB\xd6\xd6\xc4\x1cv\xb9O?\xfaR\xb4d\xda\r\'\x9e5;\xa7\xa7\xf8\xb2N\xbd\xea"\xfc\x99\n\x18v/\x06-w\xec{\x8fncc\xdbx\x8e1\xffa\xde\xee\x08\xd2\x88m\xd9\xeb\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\x01X\xd7\x7f\x140I\xcd)\x0c\x00l\x0et\x13\xd8\xf1\xe5\x9d\xfe\xe3\x00\x00\xaaU'
DEBUG:raw unpacked message = TuyaMessage(seqno=0, cmd=10, retcode=1, payload=b'X\xd7\x7f\x140I\xcd)\x0c\x00l\x0et\x13\xd8\xf1', crc=3852336867)
DEBUG:decode payload=b'X\xd7\x7f\x140I\xcd)\x0c\x00l\x0et\x13\xd8\xf1'
DEBUG:decrypting=b'X\xd7\x7f\x140I\xcd)\x0c\x00l\x0et\x13\xd8\xf1'
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'}
    Access rejected by 192.168.X.XXX: Invalid JSON Response from Device

Scan Complete!  Found 1 devices.

Running the following code with GATEWAY_IP: "192.168.X.XXX"

d = tinytuya.OutletDevice('DEVICE_ID', 'GATEWAY_IP', 'DEVICE_SECRET')
d.set_version(3.3)
data = d.status()  
print(data)

Produced the same error. From the snapshot I found another IP for the device 192.168.X.YYY. Trying that as gateway id resulted in timeouts.

DEBUG:status() entry (dev_type is default)
DEBUG:building payload=b'{"gwId":"XXXX","devId":"XXXX","uid":"XXXX","t":"1614676380"}'
DEBUG:payload generated=b'\x00\x00U\xaa\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x880v\xcd\x89)\x07\x99\x85"\xf61\xa5\x87YE\xc4\x1cv\xb9O?\xfaR\xb4d\xda\r\'\x9e5;\xa7{R\xf4\x99\x13\x00\x88,dRlw\xc1\x9b\xfaT\x16B"\x8a\x14\x99\xca\x98\x8b\xf8\xcbP\xd9\xc0f)\x0b\xd9\x19\rp_\xa1\x96\x95y\x96kB\xd6\xd6\xc4\x1cv\xb9O?\xfaR\xb4d\xda\r\'\x9e5;\xa7v\xa9\x18h\xa5|\x88\x1cZ\xc7\xa3s\xce0\x1a\x18\xec{\x8fncc\xdbx\x8e1\xffa\xde\xee\x08\xd2\xf9\x17\x91#\x00\x00\xaaU'
DEBUG:socket unable to connect - retry 1/5 timeout('timed out')
DEBUG:socket unable to connect - retry 2/5 timeout('timed out')
DEBUG:socket unable to connect - retry 3/5 timeout('timed out')
DEBUG:socket unable to connect - retry 4/5 timeout('timed out')
DEBUG:socket unable to connect - retry 5/5 timeout('timed out')
DEBUG:ERROR Network Error: Device Unreachable - 905 - payload: null
DEBUG:status received data={'Error': 'Network Error: Device Unreachable', 'Err': '905', 'Payload': None}
ahojukka5 commented 2 years ago

So, where is this "devit not found" coming from? According to debug, it's decoded. Is the message coming from the gateway? And decoding the message itself work, but as it suggests, the device id is not found.

kardoka commented 2 years ago

I can report that I have a Zigbee Gateway and I am having the exact same error when trying to check the status of the device:

{'Error': 'Network Error: Device Unreachable', 'Err': '905', 'Payload': None}

Although when I use the local IP of the device (), the status() method returns None.

The scan with the debug flag:

DEBUG:TinyTuya [1.7.1]

DEBUG:loaded=devices.json [3 devices]

TinyTuya (Tuya device scanner) [1.7.1]

[Loaded devices.json - 3 devices]

Scanning on UDP ports 6666 and 6667 for devices (18 retries)...

DEBUG:Listening for Tuya devices on UDP 6666 and 6667
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}
Smart Gateway(TYGWZ-01)   Product ID = keyyj3fy8x98arty  [Valid payload]:
    Address = 192.168.1.155,  Device ID = bf27d70310e2f30aa0u8ws, Local Key = ddb234c3ba7be258,  Version = 3.3, MAC = a0:92:08:ce:eb:fe
DEBUG:status() entry (dev_type is default)
DEBUG:building command 10 payload=b'{"gwId":"bf27d70310e2f30aa0u8ws","devId":"bf27d70310e2f30aa0u8ws","uid":"bf27d70310e2f30aa0u8ws","t":"1666865553"}'
DEBUG:sending payload
DEBUG:payload encrypted=b'000055aa000000010000000a000000884df57e74a96a4603e0b2555d545e98bf8a4efcbeaf99ca17e048914627915b650493fc2c4ab9fdce63890687e8cee7c03ced163f2a3ab0c01ab30ce5cc5c7558d924d40498ef762af63740c345a9f0d68a4efcbeaf99ca17e048914627915b65b03567a5f621c54ea1c46083cf8a9337ccbcd7fc987a5c6a7ec490c8f591794097455bb20000aa55'
DEBUG:received data=b'000055aa000000010000000a0000002c00000001f0a5b1c4a55f33cebc893ac2dbe917912ed2bca1e9237827ac51ad6560f51112786822ed0000aa55'
DEBUG:received message=TuyaMessage(seqno=1, cmd=10, retcode=1, payload=b"\xf0\xa5\xb1\xc4\xa5_3\xce\xbc\x89:\xc2\xdb\xe9\x17\x91.\xd2\xbc\xa1\xe9#x'\xacQ\xade`\xf5\x11\x12", crc=2020090605, crc_good=True)
DEBUG:raw unpacked message = TuyaMessage(seqno=1, cmd=10, retcode=1, payload=b"\xf0\xa5\xb1\xc4\xa5_3\xce\xbc\x89:\xc2\xdb\xe9\x17\x91.\xd2\xbc\xa1\xe9#x'\xacQ\xade`\xf5\x11\x12", crc=2020090605, crc_good=True)
DEBUG:decode payload=b"\xf0\xa5\xb1\xc4\xa5_3\xce\xbc\x89:\xc2\xdb\xe9\x17\x91.\xd2\xbc\xa1\xe9#x'\xacQ\xade`\xf5\x11\x12"
DEBUG:decrypting=b"\xf0\xa5\xb1\xc4\xa5_3\xce\xbc\x89:\xc2\xdb\xe9\x17\x91.\xd2\xbc\xa1\xe9#x'\xacQ\xade`\xf5\x11\x12"
DEBUG:decrypted 3.x payload='json obj data unvalid'
DEBUG:payload type = <class 'str'>
DEBUG:'data unvalid' error detected: switching to dev_type 'device22'
DEBUG:_decode_payload() failed!
DEBUG:Device22 detected and updated (default -> device22) - Update payload and try again
DEBUG:ERROR Device22 Detected: Retry Command - 908 - payload: null
DEBUG:status() received data={'Error': 'Device22 Detected: Retry Command', 'Err': '908', 'Payload': None}
DEBUG:status() rebuilding payload for device22
DEBUG:building command 10 payload=b'{"devId":"bf27d70310e2f30aa0u8ws","uid":"bf27d70310e2f30aa0u8ws","t":"1666865553","dps":{"1":null}}'
DEBUG:sending payload
DEBUG:payload encrypted=b'000055aa000000020000000d00000087332e33000000000000000000000000ba8c24eb72315196c112d43b266175ee3ced163f2a3ab0c01ab30ce5cc5c7558d924d40498ef762af63740c345a9f0d68a4efcbeaf99ca17e048914627915b65b03567a5f621c54ea1c46083cf8a9337c89ec1ffca0c01f4b56e3bb90b3ec0be2b4860235aad81d01b63e585eafa19eeb7c48f3e0000aa55'
DEBUG:received data=b'000055aa000000020000000d0000000c000000008de14f440000aa55'
DEBUG:received null payload (TuyaMessage(seqno=2, cmd=13, retcode=0, payload=b'', crc=2380353348, crc_good=True)), fetch new one - retry 0 / 5
    Unexpected error for 192.168.1.155: Unable to poll
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}
DEBUG:Received valid UDP packet: {'ip': '192.168.1.155', 'gwId': 'bf27d70310e2f30aa0u8ws', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyyj3fy8x98arty', 'version': '3.3', 'lan_cap': 5000, 'lan_seq': 8, 'token': True}

Scan Complete!  Found 1 devices.

>> Saving device snapshot data to snapshot.json

DEBUG:Scan complete with 1 devices found

EDIT: I am able to retrieve the data using the tuyapi library by passing the Zigbee device child ID to the device.get() method.

jasonacox commented 2 years ago

Thanks @uzlonewolf !

Everyone, please test! I'll plan a version drop to include this merge this weekend unless there are issues.