Welcome!
I’ve had good success using Jason Cox’s tinytuya package to explore and control Tuya devices locally. Link and a short example for querying a Atomi Desktop Heater.
Tuya is a real wild west, tinytuya does a good job of mashing together the data from the Tuya developer cloud and the device to display somewhat useful names, types and ranges for those data points that the vendor has documented with Tuya. Unfortunately for some devices, this is incomplete. The Atomi Heater example below shows this. If I use the tinytuya functions to query the heater locally, there are three data points that are undocumented on the Tuya web site. So you now have to do you best reverse engineering / sherlock holmes work to figure out these additional entities. Often you can ‘operate’ the device using tinytuya with just the data points that are documented at the Tuya cloud. But the other ones can be useful. For example, the undocumented data points for the Atomi heater control/report: turn off count down (11) maybeswitching heat range between fahrenheit and celsius (12) , reporting that the heater had fallen over (103) and something I have yet to decode (102)…
Additionally, I have found that the reported data point values can be incomplete, so there may be more ‘setable’ values for a data point that is shown.
A bit of warning, I have yet to brick a Tuya device by setting a data point wrong, but that is not inconceivable to occur. So far the worst I have done is make it necessary to power cycle the Tuya device to get it back and happy after a incorrect setting of a dps value.
Good hunting!
Example of data points returned by a query to the device locally:
Received Payload: {'dps': {'1': False, '2': 83, '3': 58, '4': 'high', '8': False, '10': True, '11': '0h', '12': 0, '102': False, '103': False}}
-----
Example program to document data points know by the Tuya developer cloud:
#! /usr/bin/env python3
# or force unbuffered stdout output for piping to another program
####! /usr/bin/env -S python3 -u
# q.py
#
import tinytuya
import json
import pprint
c = tinytuya.Cloud(
apiRegion="us",
apiKey="aaaaaaaaaaaaa",
apiSecret="bbbbbbbbbbb",
apiDeviceID="cccccccccccc") # any of your enabled devices
# Display list of devices
# devices = c.getdevices()
# print("Device List: %r" % devices)
# Select a Device ID to Test
id = "dddddddddddddddd"
# Display DPS IDs of Device
result = c.getdps(id)
print(json.dumps(result, sort_keys=False, indent=4))
----------
Output from above program for Atomi Desktop Heater :
https://www.amazon.com/Atomi-Smart-Portable-Tabletop-Heater/dp/B08B1W7ZY7/
user@macmini2012:~/tinytuya$ ./q.py
{
"result": {
"category": "qn",
"functions": [
{
"code": "switch",
"dp_id": 1,
"type": "Boolean",
"values": "{}"
},
{
"code": "temp_set",
"dp_id": 2,
"type": "Integer",
"values": "{\"unit\":\"\u2109\",\"min\":60,\"max\":86,\"scale\":0,\"step\":1}"
},
{
"code": "mode",
"dp_id": 4,
"type": "Enum",
"values": "{\"range\":[\"auto\"]}"
},
{
"code": "shake",
"dp_id": 8,
"type": "Boolean",
"values": "{}"
},
{
"code": "light",
"dp_id": 10,
"type": "Boolean",
"values": "{}"
}
],
"status": [
{
"code": "switch",
"dp_id": 1,
"type": "Boolean",
"values": "{}"
},
{
"code": "temp_set",
"dp_id": 2,
"type": "Integer",
"values": "{\"unit\":\"\u2109\",\"min\":60,\"max\":86,\"scale\":0,\"step\":1}"
},
{
"code": "temp_current",
"dp_id": 3,
"type": "Integer",
"values": "{\"unit\":\"\u2109\",\"min\":32,\"max\":122,\"scale\":0,\"step\":1}"
},
{
"code": "mode",
"dp_id": 4,
"type": "Enum",
"values": "{\"range\":[\"auto\"]}"
},
{
"code": "shake",
"dp_id": 8,
"type": "Boolean",
"values": "{}"
},
{
"code": "light",
"dp_id": 10,
"type": "Boolean",
"values": "{}"
},
{
"code": "countdown_left",
"dp_id": 12,
"type": "Integer",
"values": "{\"unit\":\"h\",\"min\":0,\"max\":12,\"scale\":0,\"step\":1}"
}
]
},
"success": true,
"t": 1643664658906
}