jasonacox / tinytuya

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

Support setting a string value via server #313

Closed felix-schwarz closed 1 year ago

felix-schwarz commented 1 year ago

I just found out today that my Medion MD-10444 air filter actually is a Tuya device and have been using tinytuya to explore it.

The tinytuya server would fit in perfectly with my setup, but it seems I can't set values using it, possibly because they are encoded as the wrong type by the tinytuya server.

If I request the status for the device, I get this response:

{"devId": "_my_device_id_", "dps": {"1": true, "2": 66, "4": "2", "5": 98, "7": false, "18": "cancel", "21": 0, "22": "2"}}

Now what I'd like to do is set 4 from 2 to 3.

Requesting http://localhost:8888/set/_my_device_id_/4/3 returns {"status": "OK"} but the device does not change the setting.

Upon closer inspection of server.py I noticed that the current implementation forcibly converts the value to an integer, whereas the JSON returned from the air filter encodes the value as a string ("2").

I'd think then that if the parameter was sent to the device as a string, the device would actually change the setting. Whereas right now, it probably doesn't pass a type check and is ignored.

If the server supported an additional type parameter when setting a value, that would be awesome. Ideas for how that could be expressed in the URL:

jasonacox commented 1 year ago

Thanks @felix-schwarz ! I see the problem. It is not translating the URL encoding otherwise this should work:

http://localhost:8888/set/_my_device_id/4/"3"

I'll fix that and add a bit more to help with debugging.

jasonacox commented 1 year ago

Updated... pull down the latest and let me know if this work for you use case.

Also, you can start it with debug mode:

python3 server.py -d
felix-schwarz commented 1 year ago

Thanks @jasonacox!

I pulled 825b48394cf10c597eea10e72e24cbbe00af2931, locally built a docker image from it and tested it. Unfortunately, it didn't make a difference to request

http://localhost:8888/set/_device_id_/4/%223%22

(where %22 should be the correct url encoding for ")

I then forced server.py to send a string by commenting out the conversion to int: https://github.com/jasonacox/tinytuya/blob/71a06019a5617e5745c5ff08fa253cf061028409/server/server.py#L379-L380

Subsequently I requested

http://localhost:8888/set/_device_id_/4/3

and it immediately changed the setting of the device.

I think the issue in 825b48394cf10c597eea10e72e24cbbe00af2931 is this:

This could be solved by the server checking if the string starts and ends with " quotes - and then removing them in a final step before sending the value to the device.

jasonacox commented 1 year ago

You are right! I checked the payload via debug and was sending ""3"" {"4":"\\"3\\""} instead of "3" {"4":"3"}. I added a condition to look for quoted parameters and to remove the quotes but keep it as a string. See if this works.

image
felix-schwarz commented 1 year ago

I just checked and as of commit 679df59, the issue is fixed.

Thanks for the awesome work!

jasonacox commented 1 year ago

Awesome! Thanks @felix-schwarz ! 🙏