jasonacox / tuyapower

Python module to read status and energy monitoring data from Tuya based WiFi smart devices. This includes state (on/off), current (mA), voltage (V), and power (wattage).
MIT License
136 stars 20 forks source link

Failed to receive data from 192.168.1.29. Raising Exception. #9

Closed loaxley closed 3 years ago

loaxley commented 3 years ago

Hi @jasonacox , I have been working to resolve an issue with connecting to my Tuya cover device in relation to a Home Assistant add-on located at https://github.com/rospogrigio/localtuya-homeassistant. I've been speaking with the author, rospogrigio, who sent me some debug scripts and told me that he used your tuyapower library and integrated it with his pytuya library. I thought you might thus be able to help out with this issue.

The device I have is: https://www.aliexpress.com/item/4001045079168.html?spm=a2g0s.9042311.0.0.35ce4c4d2TbFxa

I've extracted the device's Key and ID using tuya-cli. I can also use tuya-cli successfully to both both get and set data. For example, I can run the following command: tuya-cli get --id 31xxxxxxxxxxxxxxxxf8 --key 87xxxxxxxxxxxxf8 --protocol-version 3.3 --all

And I get the following output: { devId: '31xxxxxxxxxxxxxxxxf8', dps: { '1': 'open', '2': 100, '3': 100, '5': false, '7': 'closing', '8': 'cancel', '9': 0, '10': 0, '11': 38610 } }

I've installed tuyapower via pip, and when I run: python3 -m tuyapower

My output is:

TuyaPower (Tuya compatible smart plug scanner) [0.0.22]
Scanning on UDP ports 6666 and 6667 for devices (15 retries)...
FOUND Device [Valid payload]: 192.168.1.29
    ID = 31xxxxxxxxxxxxxxxxf8, productKey = 3rxxxxxxxxxxxx1g, Version = 3.3
    Device Key required to poll for stats
Scan Complete!  Found 1 devices.

So, good news, is that tuyapower can successfully scan and find my device. The IP is correct, the ID is correct. (However, the productKey does not match up with the Key that I extracted using tuya-cli, and with which I can set and get data using tuya-cli. But let's set that aside because I don't think it's relevant).

When I run: python test.py 31xxxxxxxxxxxxxxxxf8 192.168.1.29 87xxxxxxxxxxxxf8 3.3

I get back:

ERROR: Timeout polling device
TuyaPower (Tuya Power Stats) [0.0.22]
Device 31xxxxxxxxxxxxxxxxf8 at 192.168.1.29 key 87xxxxxxxxxxxxf8 protocol 3.3:
    Switch On: False
    Power (W): -99.000000
    Current (mA): -99.000000
    Voltage (V): -99.000000
    Projected usage (kWh):  Day: -2.376000 Week: -16.632000  Month: -72.072000
{ "datetime": "2020-09-03T09:05:29Z", "switch": "False", "power": "-99", "current": "-99", "voltage": "-99" }

This leads me to two conclusions: (1) There's a timeout, so something is wrong (2) Even if there wasn't a timeout, my device is a cover, not a switch, so the output here would be irrelevant

rospogrigio, however, has built in cover functionality to his adaption of pytuya/tuyapower. He uploaded some debug scripts for me, which you can view here: https://github.com/rospogrigio/localtuya-homeassistant/blob/master/tuyadebug.tgz

For ease of reference, here's what his updated "test.py" file looks like:

#!/usr/bin/python3
#
# TuyaPower (Tuya Power Stats)
#      Power Probe - Wattage of smart devices - JSON Output

import datetime
import os
import sys
import time
from time import sleep

import localtuya

# Read command line options or set defaults
if (len(sys.argv) < 2) and not (("DEVID" in os.environ) or ("DEVIP" in os.environ)):
    print("TuyaPower (Tuya Power Stats)\n")
    print("Usage: %s <DEVID> <DEVIP> <DEVKEY> <DEVVERS>\n" % sys.argv[0])
    print("    Required: <DEVID> is the Device ID e.g. 01234567891234567890")
    print("              <DEVIP> is the IP address of the smart device e.g. 10.0.1.99")
    print("              <DEVKEY> is the Device Key (default 0123456789abcdef)")
    print("              <DEVTYPE> is the Device Type: 's' for switch (default) or 'c' for cover\n")
    print("    Optional: <DPSUSED> is the list of dps in use\n")
    print("              <DEVVERS> is the Firmware Version 3.3 (default) or 3.1\n")
    print("    Note: You may also send values via Environmental variables: ")
    print("              DEVID, DEVIP, DEVKEY, DEVTYPE, DEVVERS\n")
    exit()
DEVID = sys.argv[1] if len(sys.argv) >= 2 else "01234567891234567890"
DEVIP = sys.argv[2] if len(sys.argv) >= 3 else "10.0.1.99"
DEVKEY = sys.argv[3] if len(sys.argv) >= 4 else "0123456789abcdef"
DEVTYPE = sys.argv[4] if len(sys.argv) >= 5 else "s"
DPSUSED = sys.argv[5] if len(sys.argv) >= 6 else ""
DEVVERS = sys.argv[6] if len(sys.argv) >= 7 else "3.3"

# Check for environmental variables and always use those if available (required for Docker)
DEVID = os.getenv("DEVID", DEVID)
DEVIP = os.getenv("DEVIP", DEVIP)
DEVKEY = os.getenv("DEVKEY", DEVKEY)
DEVTYPE = os.getenv("DEVTYPE", DEVTYPE)
DPSUSED = os.getenv("DPSUSED", DPSUSED)
DEVVERS = os.getenv("DEVVERS", DEVVERS)

now = datetime.datetime.utcnow()
iso_time = now.strftime("%Y-%m-%dT%H:%M:%SZ")

# Poll Smart Swich for Power Data
err = "OK"
# (on, w, mA, V, err) = localtuya.deviceInfo(DEVID, DEVIP, DEVKEY, DEVVERS) 
localtuya.devicePrint(DEVID, DEVIP, DEVKEY, DEVTYPE,DPSUSED,DEVVERS) 
#localtuya.turn_on()

# Check for error
if err != "OK":
    print(" ERROR: %s\n" % err)
    sys.exit(1)
sys.exit(0)

When I run the following command: python test.py 31xxxxxxxxxxxxxxxxf8 192.168.1.29 87xxxxxxxxxxxxf8 c

I get back:

INFO:localtuya:localtuya version 0.0.19
INFO:localtuya:Python 2.7.16 (default, Oct 10 2019, 22:02:15) 
[GCC 8.3.0] on linux2
INFO:localtuya:Using pytuya version '7.0.8'
COVER  DEVICE
localtuya.pytuya version 7.0.8
Python 2.7.16 (default, Oct 10 2019, 22:02:15) 
[GCC 8.3.0] on linux2
('Using PyCrypto ', (2, 6, 1, 'final', 0))
('Using PyCrypto from ', '/usr/lib/python2.7/dist-packages/Crypto/__init__.pyc')
INFO:localtuya:Requesting status of device 31xxxxxxxxxxxxxxxxf8 [192.168.1.29], protocol 3.3.
DEBUG:localtuya.pytuya:status() entry (dev_type is device22)
DEBUG:localtuya.pytuya:json_payload='{"devId":"31xxxxxxxxxxxxxxxxf8","dps":{"":null},"uid":"31xxxxxxxxxxxxxxxxf8","t":"1599124438"}'
Failed to receive data from 192.168.1.29. Raising Exception.
COVER  DEVICE
localtuya.pytuya version 7.0.8
Python 2.7.16 (default, Oct 10 2019, 22:02:15) 
[GCC 8.3.0] on linux2
('Using PyCrypto ', (2, 6, 1, 'final', 0))
('Using PyCrypto from ', '/usr/lib/python2.7/dist-packages/Crypto/__init__.pyc')
INFO:localtuya:Requesting status of device 31xxxxxxxxxxxxxxxxf8 [192.168.1.29], protocol 3.3.
DEBUG:localtuya.pytuya:status() entry (dev_type is device22)
DEBUG:localtuya.pytuya:json_payload='{"devId":"31xxxxxxxxxxxxxxxxf8","dps":{"":null},"uid":"31xxxxxxxxxxxxxxxxf8","t":"1599124451"}'
Failed to receive data from 192.168.1.29. Raising Exception.
COVER  DEVICE
localtuya.pytuya version 7.0.8
Python 2.7.16 (default, Oct 10 2019, 22:02:15) 
[GCC 8.3.0] on linux2
('Using PyCrypto ', (2, 6, 1, 'final', 0))
('Using PyCrypto from ', '/usr/lib/python2.7/dist-packages/Crypto/__init__.pyc')
INFO:localtuya:Requesting status of device 31xxxxxxxxxxxxxxxxf8 [192.168.1.29], protocol 3.3.
DEBUG:localtuya.pytuya:status() entry (dev_type is device22)
DEBUG:localtuya.pytuya:json_payload='{"devId":"31xxxxxxxxxxxxxxxxf8","dps":{"":null},"uid":"31xxxxxxxxxxxxxxxxf8","t":"1599124453"}'
Failed to receive data from 192.168.1.29. Raising Exception.
INFO:localtuya:TIMEOUT: No response from device 31xxxxxxxxxxxxxxxxf8 [192.168.1.29] after 2 attempts.

For the record, I have tried passing in the dps, and that doesn't resolve the issue, nor does using the alternative Device Key that tuyapower outputted (though I know that's the wrong Device Key, and the one I got from tuya-cli is correct, I still wanted to try it).

Do you have any idea what might be causing these timeouts & exceptions, and make it such that I can't connect to my device? I notice the json_payload doesn't include the Device Key--is it supposed to? I know you didn't author the actual code i'm having an issue with, but given that you wrote much of the code it was based off of, i'm hoping you might be able to shed some light here.

Thanks in advance,

loaxley

loaxley commented 3 years ago

@jasonacox, rospogrigio and I have resolved the issue. It turns out that when he added on support for "cover" devices, he hardcoded the device_ID as 22 characters, and my cover's device_ID is 20 characters. We added an if/else and it now works.

jasonacox commented 3 years ago

I know you closed this, but this was a GREAT find. The default pytuya module only supports Device IDs of 20 characters. All of the Tuya devices I have only have 20 characters so I never encountered this issue. However, looking back over other open issues, several people reporting problems also have Tuya devices with 22 characters.

Since the pytuya module is no longer maintained, I have created a forked version called tinytuya and have updated tuyapower to support both. The tinytuya module incorporates the great device type edits from rospogrigio.

I would be interested to know if this works now for you:

pip install tinytuya
pip install --upgrade tuyapower
python test.py 31xxxxxxxxxxxxxxxxf8 192.168.1.29 87xxxxxxxxxxxxf8 3.3

Hopefully you now see something more like this (notice versions) - though I know your cover won't have power data:

TuyaPower (Tuya Power Stats) [0.0.23] tinytuya [1.0.0]

Device 31xxxxxxxxxxxxxxxxf8 at 192.168.1.29 key 87xxxxxxxxxxxxf8 protocol 3.3:
    Switch On: True
    Power (W): 54.300000
    Current (mA): 469.000000
    Voltage (V): 118.500000
    Projected usage (kWh):  Day: 1.303200 Week: 9.122400  Month: 39.530400

{ "datetime": "2020-09-05T00:22:51Z", "switch": "True", "power": "54.3", "current": "469.0", "voltage": "118.5" }

Thank you for opening the issue.

Cheers! Jason

cwttdb70 commented 3 years ago

Hi @jasonacox ,

When I run the test.py file when cd'd to the tuyapower-master folder: python test.py 31xxxxxxxxxxxxxxxxf8 192.168.1.29 87xxxxxxxxxxxxf8 3.3

I get:

 ERROR: Timeout polling device

TuyaPower (Tuya Power Stats) [0.0.23] tinytuya [1.0.0]

Device 31xxxxxxxxxxxxxxxxf8 at 192.168.1.29 key 87xxxxxxxxxxxxf8 protocol 3.3:
    Switch On: False
    Power (W): 0.000000
    Current (mA): -99.000000
    Voltage (V): -99.000000
    Projected usage (kWh):  Day: 0.000000 Week: 0.000000  Month: 0.000000

{ "datetime": "2020-09-06T02:11:56Z", "switch": "False", "power": "0.0", "current": "-99", "voltage": "-99" }

Still returning ERROR: Timeout polling device.

When I run the test.py file when cd'd to the tinytuya-master directory: python test.py 31xxxxxxxxxxxxxxxxf8 192.168.1.29 87xxxxxxxxxxxxf8 3.3

I get:


TESTING: Device 31xxxxxxxxxxxxxxxxf8 at 192.168.1.29 with key 87xxxxxxxxxxxxf8 version 3.3

READING TEST: Response {u'devId': u'31xxxxxxxxxxxxxxxxf8', u'dps': {u'11': 38610, u'10': 0, u'1': u'stop', u'3': 0, u'2': 14, u'5': False, u'7': u'opening', u'9': 0, u'8': u'cancel'}}
State (bool, True is ON) u'stop'

CONTROL TEST: Attempting to toggle power state of device
Setting state to: False
TIMEOUT trying to toggle device power.
Setting state to: u'stop'

So running test.py from TinyaTuya directly does successfully connect to my device and output my dps, but running test.py from the updated tuyapower still returns a timeout.

jasonacox commented 3 years ago

Thanks @andrewmeepos - This is very helpful!

The good news: It worked correctly and I'm happy to see the tinytuya test is working with the cover device.

The error message from tuyapower is misleading. The exception (error) occurred because the tuyapower deviceInfo() call was expecting to get power data in the DPS response. The cover device won't have that so it threw an exception and thought it was a timeout . I have updated the script to better handle this condition (I hope). I also add a raw data response from the switch in the test.py output for tuyapower:

TuyaPower (Tuya Power Stats) [0.0.24] tinytuya [1.0.0]

Device 12xxxxxxxxxxxxxxxx at 10.0.1.187 key 34xxxxxxxxxxxxab protocol 3.3:
    Response Data: {'devId': '12xxxxxxxxxxxxxxxx', 'dps': {'1': True, '2': 0, '4': 469, '5': 543, '6': 1185}}
    Switch On: True
    Power (W): 54.300000
    Current (mA): 469.000000
    Voltage (V): 118.500000
    Projected usage (kWh):  Day: 1.303200 Week: 9.122400  Month: 39.530400

{ "datetime": "2020-09-06T04:38:03Z", "switch": "True", "power": "54.3", "current": "469.0", "voltage": "118.5" }
cwttdb70 commented 3 years ago

Aha, makes sense. The new version outputs correctly from my device and doesn't throw an exception.

FYI, we're making good progress over at https://github.com/rospogrigio/localtuya-homeassistant. Let's keep in touch to ensure both repositories are benefitting from each other's work. I think you said you perused through rospogrigio's fork, does it look like we need to incorporate any recent updates that you've made? I don't have any Tuya lights/switches/plugs, so I unfortunately can't test any of those devices with localtuya and compare to tuyapower for feature parity.

jasonacox commented 3 years ago

Awesome! Thanks for the update.

Great work over there! My intention for tinytuya is to have a simple to use but current library for accessing all Tuya devices, similar to pytuya which is no longer being supported. I set it up as a PyPi module (https://pypi.org/project/tinytuya/) to make it easy to use in any project via pip install and import tinytuya (simliar to pytuya).

Ideally, I would love to see localtuya-homeassistant be able to import and use tinytuya instead of the local forked version of pytuya you are using, if that ever made sense. In any case, I do want to collaborate and will continue to incorporate any addition you discover in the tinytuya. I would accept any updates as a PR from you and rospogrigio as contributors to the project, as well.

Thanks again for opening this issue and the great feedback throughout. Let me know if there is anything I can do to help you as well.

Stay well! Jason

cwttdb70 commented 3 years ago

Jason, sounds good. I'll suggest to rospogrigio that we import tinytuya. I did a diff between the current pytuya init.py in localtuya and the init.py in tinytuya: https://www.diffchecker.com/UgMIu71D

They're very similar right now, which is good. I'll give the diff a deeper dive later and see if there's any reason we can't just switch over to tinytuya entirely now. That way, going forward, if we need to fork, we can fork an active repo, as opposed to an abandoned one.

-Andrew