Closed 3735943886 closed 6 months ago
Hi @3735943886 , without seeing more of your code, some questions, thoughts and suggestions:
tinytuya.set_debug(True)
- It would be interesting to see where it is failing in the handshake that produces the 914. It Willa also be interesting to see how many "received null payload" retries it is doing.Only 1 thread can call Device.receive()
at a time. Have 1 thread as the designated receiver and call
Device.status( nowait=True )
from the other.
I’ve attached my code (a reduced version of the dpsconsumer function). I’m monitoring multiple devices and controlling them using multithreading. In the dpsconsumer, when I receive a Zigbee switch click, I aim to toggle another switch. To achieve this, I need to retrieve the status of that switch. However, a problem arises.
TuyaDevices[GetTuyaId('DEVICE NAME')][1] is device object.
I did not tried Device.status( nowait=True ) yet because the automation script is currently running for my home automation and cannot change during daytime. I'll post another reply at midnight. Thank you.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import json
import time
import queue
import threading
import tinytuya
# Threads data
TuyaThreads = []
# Global arrays for interthread communication
TuyaDevices = {}
TuyaSensors = {}
TuyaIRQueue = queue.Queue()
# Main threads for devices
def TuyaReceiver(tuyainfo, tuyadpscallback):
# Connect to device
tuyadev = None
# Zigbee device
if tuyainfo['ip'] == '':
# No loop
TuyaDevices[tuyainfo['node_id']] = tuyainfo['name'], tuyadev
return
# WiFi device
tuyadev = tinytuya.Device(dev_id = tuyainfo['id'], address = tuyainfo['ip'], local_key = tuyainfo['key'], version = tuyainfo['version'], persist = True)
tuyadev.send(tuyadev.generate_payload(tinytuya.DP_QUERY))
TuyaDevices[tuyainfo['id']] = tuyainfo['name'], tuyadev
while True:
# Receiving DPS
dps = tuyadev.receive()
if dps != None:
if 'Error' in dps:
# Connection error
# TinyTuya does automatically reconnect.
# Devices can sometimes take a while to re-connect to the WiFi, so if you get that error you can just wait a bit and retry the send/receive.
time.sleep(5)
continue
else:
# Interprete DPS
print(tuyainfo['name'], dps)
tuyadpscallback(tuyadev, dps)
# Heartbeat
tuyadev.send(tuyadev.generate_payload(tinytuya.HEART_BEAT))
def DpsConsumer(tuyadev, data):
# Tuya gateway triggered
if '게이트웨이' in TuyaDevices[tuyadev.id][0]:
if 'data' in data:
if 'cid' in data['data']:
# Zigbee devices
# Light scene switch in entry triggered
if TuyaDevices[data['data']['cid']][0] == '현관스위치':
# 1st switch triggered
if '1' in data['dps']:
# 1st switch triggered then turn on other device
TuyaDevices[GetTuyaId('거실조명')][1].set_value(1, True, nowait = True) # WORKS FINE
print(TuyaDevices[GetTuyaId('거실조명')][1].status()) # WEIRED BEHAVIORS
if __name__ == "__main__":
for tuyadevs in json.load(open('devices.json')):
TuyaThreads.append(threading.Thread(target = TuyaReceiver, args = (tuyadevs, DpsConsumer)))
# For IR control
TuyaThreads.append(threading.Thread(target = IRSender))
# Threading all devices
for th in TuyaThreads:
th.start()
for th in TuyaThreads:
th.join()
Like I said, you're going to need to call it as TuyaDevices[GetTuyaId('거실조명')][1].status(nowait=True)
If you need the current status to decide what action to take then I recommend updating DpsConsumer()
to store it somewhere instead of calling status()
every time.
I.e.
...
# WiFi device
tuyadev = tinytuya.Device(dev_id = tuyainfo['id'], address = tuyainfo['ip'], local_key = tuyainfo['key'], version = tuyainfo['version'], persist = True)
tuyadev.current_dps = { 'cid': {} }
tuyadev.send(tuyadev.generate_payload(tinytuya.DP_QUERY))
TuyaDevices[tuyainfo['id']] = tuyainfo['name'], tuyadev
...
def DpsConsumer(tuyadev, data):
if 'data' not in data or not data['data']:
return
cid = data['cid'] if 'cid' in data and data['cid'] else False
# save the current dps value in current_dps
if 'dps' in data['data']:
if cid:
# Zigbee device, so store it under 'cid'
if cid not in tuyadev.current_dps['cid']:
tuyadev.current_dps['cid'][cid] = {}
for dp in data['data']['dps']:
tuyadev.current_dps['cid'][cid][dp] = data['data']['dps'][dp]
else:
# not a Zigbee device, so store it directly
for dp in data['data']['dps']:
tuyadev.current_dps[dp] = data['data']['dps'][dp]
# Tuya gateway triggered
if '게이트웨이' in TuyaDevices[tuyadev.id][0]:
if cid:
# Zigbee devices
# Light scene switch in entry triggered
if TuyaDevices[cid][0] == '현관스위치':
# 1st switch triggered
if '1' in data['dps']:
# 1st switch triggered then turn on other device
linked_device = TuyaDevices[GetTuyaId('거실조명')][1]
linked_device.set_value(1, True, nowait = True) # WORKS FINE
if '1' in linked_device.current_dps:
# do something based on linked_device.current_dps['1'] ?
else:
# linked device offline or in an unknown state?
Thank you!
Attempting to retrieve the status of another device in a separate thread, using
Device.status()
does not behave as intended. It occasionally returns the correct status but with a delay, or fails with an error 914, or just drops. So, what is the optimal approach to obtain the status of another device that is awaitingDevice.receive()
within another thread loop?