jasonacox / tinytuya

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

Door/Window Sensors - "ip": 0 #214

Open bernd-wechner opened 1 year ago

bernd-wechner commented 1 year ago

I tried finding my door/window sensors with the scanner and failed. I went through the whole process of using the wizard (egads, it was time-consuming finding my devices on the Tuya site, but I got there, it was down to getting the right data centre and using manual not automatic linking before I finally found them) and I now have them all in snapshot.json but Wizard failed to poll them reporting Error: No IP found (only for these sensors, it works for a power switch I have) so I scanned the code and found the clue, and checked snapshot.json, and they appear there with "ip": 0. Now they do have IP addresses, and I know what they are (as I assigned them static DHCP leases).

Now, I doubt there is anything that can be done about this easily. Because I suspect the reason is simple enough, on reflection. Namely, to extend battery life, these sensors sleep waking briefly to transmit the reed switch state changes. In fact, there's a quick red LED flash shortly after every such state change, which is probably the WiFi transmission which it sleeps again.

But of course, the question remains open, and a curio indeed, if at the moment it is transmitting it exposes a UDP port (6666 or 6667) for polling or not as one does wonder if there isn't a necessary hook these have for keeping the WiFi link up for a conversation (I mean for OTA firmware updates for example or configuration changes - like binding to a new WAP etc).

That's a big if, of course (as in, it would take some experimenting to determine if such is in fact the case), but if it is the case, it would be interesting to support it somehow. The IP address if known could be manually added to snapshot.json, for example, and then I wonder if it's possible to watch for that IP address to appear -- which may predicate running on the gateway router or at least at a mitm site (man in the middle) as I doubt the broadcast on the LAN, but send to the Tuya data centre, and you'd need to see packets fly by from the known IP ... to then quickly check those two ports for a reply.

All rather speculative and maybe a pipe dream. I do note, there's a scanner rewrite in PR and a door sensor improvement idea already: https://github.com/jasonacox/tinytuya/issues/108.

Consider this amusing, free to close if it's not remotely tenable, or leave open if there's a prospect of maybe catching door sensor IPs and polling them if lying in wait for their transmission to fly by ...

jasonacox commented 1 year ago

Hi @bernd-wechner - I appreciate the thoughtful analysis. From what I know, you are correct on several points. The battery powered Tuya devices are designed to sleep (for obvious reasons to save battery), but when they wake up (due to event or timer) they behave as a full Tuya device, sending update to the Tuya Cloud while also broadcasting UDP packets and listening on TCP 6668. However, as you predict, that window of time is extremely small.

There are other issues where members of the community have raised the possibility of running a "ping" loop to watch for these "awake" events and to quickly poll them for status (see https://github.com/jasonacox/tinytuya/issues/94). I don't know the best use case for this other than create a service as you mention that essentially watches and records the state changes and offers that state via API call or MQTT.

As to the scanner rewrite that @uzlonewolf has staged as a draft PR, that is meant more for scanning your entire network. LW's code is substantially faster than the current scanner.py code but he is working on compatibility with 3.1 devices before making it ready for merge. However, it still will not find a Tuya device that is sleeping. :)

If you are interested in helping create and test a middlware server, we could offer that up as a tool in the examples folder.

uzlonewolf commented 1 year ago

Hmm, that's an interesting idea, have something like the server listen for broadcasts and if it's been more than X seconds (or if it's in a list of "always poll" devices) then poll it. The question then becomes what to do with that status information.

jasonacox commented 1 year ago

I could add this functionality to the server tool with a slight adjustment to proactively poll when UDP traffic is seen and store state with a timestamp in memory. That data could be offered up via a set of "cached" API commands or sent as discovered via MQTT. Is there interest in something like this?

bernd-wechner commented 1 year ago

I could add this functionality to the server tool with a slight adjustment to proactively poll when UDP traffic is seen and store state with a timestamp in memory. That data could be offered up via a set of "cached" API commands or sent as discovered via MQTT. Is there interest in something like this?

That sounds awesome. I guess it would have to run on a mitm node (so likely on a gateway router - I have an OpenWRT gateway). The only catch there being, need to check if OpenWRT has Python3 yet, it's been lagging is all I can recall.

uzlonewolf commented 1 year ago

It doesn't have to be in the middle, as it's listening for network broadcasts it can be anywhere.

bernd-wechner commented 1 year ago

But I doubt these door sensors broadcast anything, suspect they open a connection directly to the Tuya data center and send their update, then shut down the Wi-Fi. Is there any evidence that they broadcast anything?

uzlonewolf commented 1 year ago

Yes, the ones I have power up and start broadcasting like a normal device when reporting status changes. It's a really short window, only a few seconds, but enough to connect and get a status request.

bernd-wechner commented 1 year ago

How interesting. How does the broadcast reach the Tuya Data Centers? Or does it both broadcast on the LAN and send to the Tuya datecenter? As a kindness for any local loggers? Which is nice, as I want to collect the stats on the open and close, to look for patterns in open duration and times of day, week, month, year ... and their app Smart Home is lame offering no such features.

uzlonewolf commented 1 year ago

It broadcasts on the local network in addition to contacting Tuya's servers. The one I have is just a normal Tuya device that the host microcontroller powers down after a few seconds, and as such it has all the normal local network stuff.

While not as nice as it requires the cloud and internet connectivity, another option you might look into is using tinytuya.Cloud to pull logs. https://developer.tuya.com/en/docs/cloud/0a30fc557f?id=Ka7kjybdo0jse

uzlonewolf commented 1 year ago

To use the Cloud to pull logs:

import tinytuya
import json

c = tinytuya.Cloud()
v = 'v1.0'
u = 'devices/{DEVICE ID HERE}/logs?end_time=1668835441&query_type=1&size=100&start_time=1668662641&type=1,2,3,4,5,6,7,8'
r = c._tuyaplatform(u, ver=v) 
print( json.dumps(r, indent=2) )

For my repurposed smoke alarm (no I'm not actually trusting this thing to detect smoke) it results in:

{
  "result": {
    "current_row_key": "<snip>==",
    "device_id": "<snip>",
    "has_next": false,
    "logs": [
      {
        "code": "battery_percentage",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668827993620,
        "status": "1",
        "value": "65"
      },
      {
        "code": "battery_state",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668827993491,
        "status": "1",
        "value": "middle"
      },
      {
        "code": "muffling",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668827993309,
        "status": "1",
        "value": "false"
      },
      {
        "code": "battery_percentage",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668827993207,
        "status": "1",
        "value": "65"
      },
      {
        "code": "battery_state",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668827993106,
        "status": "1",
        "value": "middle"
      },
      {
        "code": "smoke_sensor_value",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668827992708,
        "status": "1",
        "value": "16"
      },
      {
        "code": "smoke_sensor_status",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668827992635,
        "status": "1",
        "value": "2"
      },
      {
        "event_from": "1",
        "event_id": 1,
        "event_time": 1668827991566,
        "status": "1"
      },
<snip>
      {
        "code": "smoke_sensor_value",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668672190030,
        "status": "1",
        "value": "16"
      },
      {
        "code": "smoke_sensor_status",
        "event_from": "1",
        "event_id": 7,
        "event_time": 1668672189800,
        "status": "1",
        "value": "2"
      },
      {
        "event_from": "1",
        "event_id": 1,
        "event_time": 1668672171468,
        "status": "1"
      }
    ]
  },
  "success": true,
  "t": "<snip>",
  "tid": "<snip>"
}