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.05k stars 184 forks source link

Sensor devices #36

Open marcowg opened 3 years ago

marcowg commented 3 years ago

How can I connect a TEmp/Hum sensor fron tuya ?. It works in my app but cant fint it in tinytuya. Is there a class for Sensor Devices such as Temp/Hum sensors?

I found the block descriptor Version 3.3 - Sensor Type.

jasonacox commented 3 years ago

So far, I have not found anyone who has been able to make a local connection to a Tuya based sensor. It appears that these only send their data to the TuyaCloud (which is what the app uses for data) and do not respond to local traffic. What device do you have?

If you have it registered, it may still show up in the wizard:

python -m tinytuya wizard

You can try connecting to the IP to see what happens. The OutletDevice class is close to the base class.

import tinytuya

tinytuya.debug(True)

d = tinytuya.OutletDevice('DEVICE_ID_HERE', 'IP_ADDRESS_HERE', 'LOCAL_KEY_HERE')
d.set_version(3.3)
data = d.status()  
marcowg commented 3 years ago

I am using

JINQII Digitale Indoor Thermometer Hygrometer, draadloze WIFI Temperatuur & Vochtigheid Sensor met Smart Alert, LCD Temp Vochtigheid Monitor voor Thui Verkocht door: JINGQIIeu

the wizard gives: C:\Users\Gebruiker>python -m tinytuya wizard -nocolor TinyTuya Setup Wizard [1.2.2]

Existing settings:
    API Key=xx
    Secret=xx
    DeviceID=xx
    Region=eu

Use existing credentials (Y/n): y

Device Listing

[ { "name": "WIFI wcd led aq beneden", "id": "002616442462ab482a16", "key": "xx" }, { "name": "T1", "id": "bf4b950a700ba29e27fmhh", "key": "xx" }, { "name": "T2", "id": "bff5774aafb742deafinka", "key": "xx" } ]

Saving list to devices.json 3 registered devices saved

Poll local devices? (Y/n): y

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

Polling local devices... [WIFI wcd led aq beneden] - 0 - Error: No IP found [T1] - 0 - Error: No IP found [T2] - 0 - Error: No IP found

Saving device snapshot data to snapshot.json

Done. (== the Wifi WCD is connected to an application)

the test script import tinytuya

tinytuya.debug(True)

tinytuya.set_debug(True, False)

d = tinytuya.OutletDevice('DEVICE_ID_HERE', 'IP_ADDRESS_HERE', 'LOCAL_KEY_HERE') d.set_version(3.3) data = d.status()

DEBUG:status() entry (dev_type is default) DEBUG:building payload=b'{"gwId":"bff5774aafb722deafinka","devId":"bff5774aafb722deafinka","uid":"bff5774aafb722deafinka","t":"1614149841"}' DEBUG:payload generated=b'\x00\x00U\xaa\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x88>o\x1f\xa1\xc8\xb5\t\xbc{\xfa\xda\xe4\xbf\x91\x9a\xdb\x0f\xbeh\x95\xbf\xd3\xda\xe6\x0bw\xaf\x93\xcbHE\xc5q\xbamN\x8e\xaf\xda9\xc8\x88N\xd6\x18#\x15\xa7\x90\xa8\xa1KH\xec+8\xcc|U\xab\xaeC\xac\xeb\xa3xdg\xfc\xa0\xbd\xd2p?uR xC\xad\x0f\xbeh\x95\xbf\xd3\xda\xe6\x0bw\xaf\x93\xcbHE\xc5\\xd1B2\x9a\x8a\x9a\xbeP\xaa\x00\x1a\xb0\x15\xae\xb3\xd0\xf1JI\x13\x88\x7f\x8f\x1c\xfc!T\x91\xc0Q\xd4/\xa0\xec$\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}

marcowg commented 3 years ago

next test was to put the script in a while loop and power of/on the sensor. then it works, but the connection drops in about 5 sec.

{'dps': {'1': 215, '2': 60, '3': 'high'}} {'dps': {'1': 215, '2': 60, '3': 'high'}} {'dps': {'1': 215, '2': 60, '3': 'high'}} {'dps': {'1': 215, '2': 60, '3': 'high'}} {'dps': {'1': 215, '2': 60, '3': 'high'}} {'dps': {'1': 215, '2': 60, '3': 'high'}} {'dps': {'1': 215, '2': 60, '3': 'high'}} {'dps': {'1': 215, '2': 60, '3': 'high'}} {'Error': 'Network Error: Device Unreachable', 'Err': '905', 'Payload': None}

jasonacox commented 3 years ago

Wow! That's a great discovery. You get a response for 5s after you power it off/on? I'll need to think about that. We could try different command to see if it responds.

NOTE: you may want to consider redacting the device ID and Key from your notes.

marcowg commented 3 years ago

The sensor reacts at power-on. It seems it get connected after the first 5 sec to the cloud.

jasonacox commented 3 years ago

Does the device use the [WiFi] device as the gateway/bridge and the [T1] and [T2] devices as temperature probes? In other words, the IP address you are using to get the data, is that for the [WiFi] device, "id": "002616442462ab482a16"?

One thought would be to see if we use that as the gateway and request information for the other IDs. We attempted that with #26 for a smart lock, with little success, but this is a sensor so it may work. Something like this:


# Tuya Command to send
command = tinytuya.DP_QUERY

GATEWAY = "002616442462ab482a16"
DEVICE = "xxx"

payload=d.generate_payload(command, data=None, gwId=GATEWAY, devId=DEVICE, uid=DEVICE)
data = d._send_receive(payload)
print('Response: %r' % data)

You could try other combinations for gwId, devId and uid and even try the different Tuya commands to see if the devices responds to anything else.

jasonacox commented 3 years ago

I found some of the Tuya code for MCU which might apply to your device: here with sample code that seems to indicate the DPS indexes:


/******************************************************************************
                        1:dp数据点序列号重新定义
          **此为自动生成代码,如在开发平台有相关修改请重新下载MCU_SDK**         
******************************************************************************/
//当前温度(只上报)
//备注:
#define DPID_TEMP_CURRENT 1
//湿度数值(只上报)
//备注:
#define DPID_HUMIDITY_VALUE 2
//电池电量(只上报)
//备注:
#define DPID_BATTERY_PERCENTAGE 4

/******************************************************************************
                        1:dp数据点序列类型对照表
          **此为自动生成代码,如在开发平台有相关修改请重新下载MCU_SDK**         
******************************************************************************/
DOWNLOAD_CMD_S download_cmd[] =
{
  {DPID_TEMP_CURRENT, DP_TYPE_VALUE},
  {DPID_HUMIDITY_VALUE, DP_TYPE_VALUE},
  {DPID_BATTERY_PERCENTAGE, DP_TYPE_VALUE},
};

Which means 1 = temp(C), 2 = Humidity(%). I didn't find 3 and your example data payload didn't have 4.

{'dps': {'1': 215, '2': 60, '3': 'high'}}`

I suspect that would mean: Temp = 21.5C and Humidity = 60%. Is that reasonable?

One thought... we could see what a series of heartbeat() would produce, if anything:

import tinytuya
import time

tinytuya.set_debug(True)

d = tinytuya.OutletDevice(ID,IP,KEY)
d.set_version(3.3)
d.set_socketPersistent(True)

while(True):
    data = d.heartbeat()
    print('Response: %r' % data)
    time.sleep(2)
marcowg commented 3 years ago

both test result in 905 Network Error: Device Unreachable. my old test result in {'dps': {'1': 221, '2': 58, '3': 'high'}} Temp = 22.1C and Humidity = 58%. battery = High

If I look in the app there is a 24 hr trendline, it means the sensor is connected to the cloud. Somewhere at iot.tuya.com. can we block that in a firewall?

055Richard commented 3 years ago

I also made a endless loop and values come on start up and when the temperature change 0.5 degrees.

jasonacox commented 3 years ago

Yes, from what I discovered from others, the Tuya sensors are designed to go to sleep to save battery life. They only become active on startup or when state change occurs. They power up, join WiFi and send update to Tuya Cloud. During that time, there is a window of opportunity for you to ping the device and potentially get a response. Keep in mind that these sensors are designed to report to the Cloud, so if you block their cloud access, results are undefined. Also, pinging the device even in sleep mode could shorten the life of the battery.

I don't have any Tuya sensors to experiment or test (I built my own with esp8266 modules) so any details or suggested code updates would be appreciated.

rawrxiv commented 3 years ago

Long story short but a little extra info to the puzzle, I've got an s06pro which is an IR blaster here that has temp & humidity builtin they're powered by USB, I can get a status with them at any time and get back {'dps': {'101': 164, '102': 63}}, probably not super useful information but figured I'd share.

jasonacox commented 3 years ago

Thanks @rawrxiv - So 16.4C and 63% RH?

I would love to get the full device details. If you ran wizard you should have a tuya-raw.json file that contains the full dump from Tuya IoT. Can you paste the s06pro portion of that? Remove any sensitive data (IP, ID, local_key, etc). The biz_type, category, product_id and product_name and status may be good data points to help build a repository of devices and types, especially for these non-outlet devices/sensors.

rawrxiv commented 3 years ago
{
  "result": [
    {
      "active_time": 1628634686,
      "biz_type": 18,
      "category": "wnykq",
      "create_time": 1628634686,
      "icon": "smart/icon/ay1525749833414yotNt/6b9c8b165096c8b58bb73953a95b986e.png",
      "id": "",
      "ip": "",
      "lat": "",
      "local_key": "",
      "lon": "",
      "model": "S06ProWB3S",
      "name": "",
      "online": true,
      "owner_id": "",
      "product_id": "y7sl2p1fgcbuh6lu",
      "product_name": "Smart IR",
      "status": [
        {
          "code": "va_temperature",
          "value": 188
        },
        {
          "code": "va_humidity",
          "value": 63
        }
      ],
      "sub": false,
      "time_zone": "",
      "uid": "",
      "update_time": 1628641897,
      "uuid": ""
    },
    {
      "active_time": 1628634205,
      "biz_type": 18,
      "category": "wnykq",
      "create_time": 1628634205,
      "icon": "smart/icon/ay1525749833414yotNt/6b9c8b165096c8b58bb73953a95b986e.png",
      "id": "",
      "ip": "",
      "lat": "",
      "local_key": "",
      "lon": "",
      "model": "S06ProWB3S",
      "name": "",
      "online": true,
      "owner_id": "",
      "product_id": "y7sl2p1fgcbuh6lu",
      "product_name": "Smart IR",
      "status": [
        {
          "code": "va_temperature",
          "value": 191
        },
        {
          "code": "va_humidity",
          "value": 63
        }
      ],
      "sub": false,
      "time_zone": "",
      "uid": "",
      "update_time": 1628641897,
      "uuid": ""
    }
  ],
  "success": true,
  "t": 1628765499128
}

There's two of these s06pros there.

Having messed around with it more, seems like it has a hard time consistently making the socket connection, If I loop it polling every second eventually it'll make a connection and looks like it'll persist it, but if say I try to poll every minute with it might miss the connection within 5 retries.

Hazarding a guess is that the successful connection may be when the device is or isn't making a connection elsewhere?

jasonacox commented 3 years ago

Thanks @rawrxiv ! That's very helpful.

Yes, I have seen that. Many Tuya devices are very single threaded. You could run a monitor script like this to see if it sends any updates on its own:


import tinytuya

d = tinytuya.OutletDevice('DEVICEID', 'DEVICEIP', 'DEVICEKEY')
d.set_version(3.3)
d.set_socketPersistent(True)

print(" > Send Request for Status < ")
payload = d.generate_payload(tinytuya.DP_QUERY)
d.send(payload)

print(" > Begin Monitor Loop <")
while(True):
    # See if any data is available
    data = d.receive()
    print('Received Payload: %r' % data)

    # Send keyalive heartbeat
    print(" > Send Heartbeat Ping < ")
    payload = d.generate_payload(tinytuya.HEART_BEAT)
    d.send(payload)

    # print(" > Send Request for Status < ")
    # payload = d.generate_payload(tinytuya.DP_QUERY)
    # d.send(payload)
rawrxiv commented 3 years ago

That worked a treat for these devices, thank you for the help.

make-all commented 2 years ago

Just a thought - when these battery powered sensors wake up to send to the cloud, do they send a broadcast message on port 6666 or 6667? Could this be used to trigger a poll during the 5 second window when the device is awake (or does the broadcast contain enough info to not need the poll even)?

jasonacox commented 2 years ago

Hi @make-all - I don't know if all sensors emit a broadcast when they wake up, but there is a possibility that they do. The UDP broadcast message contains the IP, Device ID, productKey (sku), and protocol version. Here is an example:

{
    "ip": "10.0.1.2",
    "gwId": "01234567891234567890",
    "active": 2,
    "ability": 0,
    "mode": 0,
    "encrypt": true,
    "productKey": "AiHXxAyyn7eAkLQY",
    "version": "3.3"
}

And yes, you could set up a script to constantly monitor for that and when it comes in, poll the device. Key would be to close that connection as soon as you get it so that the device can go back to sleep to save battery (example from server.py):

import tinytuya

# devicelist is dictionary of device keys
d = tinytuya.OutletDevice(id, ip, deviceslist[id]["key"])
d.set_version(float(version))
status = d.status()
d.close()