Open measwel opened 5 months ago
Hi @measwel,
Can you post your initialization code? It looks like version is being set as a string ("3.3"
) instead of a float (3.3
) like it should.
You do not need to disconnect the device in the SmartLife app, simply closing the app is fine. As for multiple connections, this is going to depend on what the device supports; some devices can handle 2-4 simultaneous connections while others only allow 1.
I read device from device.json based on stored uuids of devices I am interested in.
def get_device_handle(device, type):
device_id = device["id"]
device_ip = device["ip"]
device_key = device["key"]
device_version = device["version"]
if type=='outlet' : dev_handle = tinytuya.OutletDevice(device_id, device_ip, device_key)
elif type=='bulb' : dev_handle = tinytuya.BulbDevice(device_id, device_ip, device_key)
dev_handle.set_version(device_version) <-- YES THIS IS PROBABLY COMING IN AS STR FROM THE JSON
return dev_handle
I will try to float it.
It works 👍 Thank you.
dev_handle.set_version(float(device_version))
A few words about what I am building. I am trying to make an app for darkroom work. Tkinter inteface. Red icons on black background, so it should be safe. I figured I can set smart bulbs to a red light wavelength that will not influence photographic paper. I can time the enlarger. And if I manage to integrate a light intensity sensor, then I can also let the app auto calculate the optimal exposure time based on the amount of light that is projected onto the photographic paper. :)
I plan to make the app open source as it should be quite useful for people who like to develop their own black and white photos in the darkroom.
Nice project @measwel ! Keep us posted. 😁
Thanks @uzlonewolf somewhere in my "don't have time" TODO list I had planned to explore how to type-safe our parameters. There's never enough time but we could use this issue to flag that.
I thought we were casting the version to a float, but looking at the code it seems that's only done when passed into Device(..., version=N)
, d.set_version(N)
doesn't do it. Should be a simple change to add a float() to the set_version() function.
Would indeed be helpful for those who read the device params from devices.json. From a different barrel: what would be a good way to generically test if a device is online and ready to be used? The best I came up with is passing in a device handle and:
def test_device(d):
ok=True
s = d.status()
for a in s:
if a=="Error":
msg = "Device " + d.id + " at " + d.address + " : " + s["Error"]
message_to_user(msg)
ok=False
return ok
PS Would be nice to include the device name in the message too, but it's not part of the handle object. I would probably have to dig into devices.json to fetch it.
@jasonacox
3 sleepless nights later I have a prototype to show. :)
https://github.com/measwel/darkroom
Would very much appreciate your feedback.
Nice job @measwel !
@jasonacox
I can't get error handling quite right.
How to check if a device is online? Best I came up with is to d.status() and check for Error message. But getting the status takes a second and freezes up the interface.
The less I freeze up the interface, the better. Getting the exposure time right, means the devices must respond swiftly. So I currently execute the action (like switching on the enlarger) and afterwards check the device status.
Why do I check it? So in case a device went offline, I can tell the user, throw an exception and try to reconnect.
It would be better if I could check the device status in a non blocking way. Either with a callback or in a thread. As far as I know .status() does not have a callback one could use, so I will try to run it in a thread.
@jasonacox
So I made this threaded routine to check the status of a device after using it. It seems to work, but I do not really know if this makes sense or not :)
def check_device_status(d):
ok = True
status = None
def device_status(d):
return d.status()
with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(device_status, d)
status = future.result()
for a in status:
if a=="Error":
msg = f'{device_name(d)} : {status["Error"]}'
message_to_user(msg)
ok = False
if not ok:
raise Exception("Device error")
else:
return status
Here is a script to scan through a list of devices and detect which are online or offline. Notice the additional connection settings in the constructor (OutletDevice):
import json
import os
import tinytuya
# Load all device details from devices.json
print('\nLoad devices.json')
devices = []
if os.path.exists('devices.json'):
with open('devices.json') as json_file:
devices = json.load(json_file)
else:
print('devices.json not found')
# Or optionally, Set your own device details here:
devices = [
{
"name": "Switch",
"id": "xxxxxxxx",
"ip": "x.x.x.x",
"key": "xxxxxxxxxxxxxxxx",
"version": "3.3"
},
{
"name": "Light",
"id": "xxxxxxxx",
"ip": "x.x.x.x",
"key": "xxxxxxxxxxxxxxxx",
"version": "3.1"
},
{
"name": "Fan",
"id": "xxxxxxxx",
"ip": "x.x.x.x",
"key": "xxxxxxxxxxxxxxxx",
"version": "3.3"
},
]
# Loop through devices and check to see which are online
print('\nCheck Devices')
for device in devices:
if not device['id'] or not device['ip'] or not device['key']:
continue
# Connect to device
d = tinytuya.OutletDevice(dev_id=device['id'],
address=device['ip'],
local_key=device['key'],
version=device['version'],
connection_timeout=1,
connection_retry_limit=1,
connection_retry_delay=1,
)
# Check status of device
data = d.status()
if "Error" in data:
print(f"{device['name']} - OFFLINE")
else:
print(f"{device['name']} - ONLINE ({data})")
Interesting. It is doing the same thing I am currently doing with 2 differences.
I would propose:
import json
import os
import tinytuya
import concurrent.futures
# Load all device details from devices.json
print('\nLoad devices.json')
devices = []
if os.path.exists('devices.json'):
with open('devices.json') as json_file:
devices = json.load(json_file)
else:
print('devices.json not found')
# Or optionally, Set your own device details here:
devices = [
{
"name": "Switch",
"id": "xxxxxxxx",
"ip": "x.x.x.x",
"key": "xxxxxxxxxxxxxxxx",
"version": "3.3"
},
{
"name": "Light",
"id": "xxxxxxxx",
"ip": "x.x.x.x",
"key": "xxxxxxxxxxxxxxxx",
"version": "3.1"
},
{
"name": "Fan",
"id": "xxxxxxxx",
"ip": "x.x.x.x",
"key": "xxxxxxxxxxxxxxxx",
"version": "3.3"
},
]
# Loop through devices and check to see which are online
print('\nCheck Devices')
for device in devices:
if not device['id'] or not device['ip'] or not device['key']:
continue
# Connect to device
d = tinytuya.OutletDevice(dev_id=device['id'],
address=device['ip'],
local_key=device['key'],
version=device['version'],
connection_timeout=1,
connection_retry_limit=1,
connection_retry_delay=1,
)
# Check status of device
def device_status(d):
return d.status()
with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(device_status, d)
data = future.result()
if "Error" in data:
print(f"{device['name']} - OFFLINE")
else:
print(f"{device['name']} - ONLINE ({data})")
The benefit is, that checking the status runs in its own thread this way and does not block the main program. When the thread finishes, data can be read out normally.
I only do this when checking the status. For other functions I do want the tuya code to run synchronous, so the events happen in the correct sequence.
Imagine I have 10 darkroom lamps. I send commands to switch them all off then switch on the enlarger. I do not want checking the status of those 10 lamps to block the program and interfere with switching the enlarger off again at the right time.
I always first run some function on the device, then check the status of that device. Why? Because some functions will return an Error when executed, but others will not. For instance :
d.set_status(False) <-- Turn off the smart outlet of the enlarger. This does return an error when the outlet is offline.
l.turn_off() <-- Turn off rgb led lamp. This does not return an Error when the lamp is offline.
turning the lamp off, does not seem to return an error code, even when the device is offline. So I need to get the status to find out if a lamp has malfunctioned.
Well done @measwel
One note, I think you could replace d.status()
with d.turn_off()
but would need to capture the response similar to status:
data = d.turn_off()
if "Error" in data:
print(f"{device['name']} - OFFLINE ({data})")
else:
print(f"{device['name']} - ONLINE ({data})")
Also, as a note, without adding the connnection*
parameters to the OutletDevice, the defaults will mean that TinyTuya will take up to 45s to determine the device is offline based on this:
Total timeout = (connection_timeout * connection_retry_limit) +
(connection_retry_delay * (connection_retry_limit - 1))
Defaults: (5 * 5) + (5 * (5 - 1)) = 45 seconds
As you mention, this is less of an issue if you are threading the calls and not blocking the main loop.
Hi @jasonacox, thanks for your suggestions.
I think I will stick with using .status() as that works every time. Yes, I set other connection settings to speed things up.
I cannot finish the part that would do the automatic exposure time calculation as I do not have a light intensity meter yet. Perhaps you know someone who does and could maybe help out?
I do not have a light intensity meter yet. Perhaps you know someone who does and could maybe help out?
Something like this? I haven't used this but there appear to be several options out there. I don't know if this would work for your use case.
Thank you @jasonacox. That is exactly the model I was looking at. I ordered it. It has usb power and a sensitivity scale from 0 to 1000 lumen, which should give enough resolution I hope.
What I do not know, is whether the scale is linear. Will 2 times as much light produce a two times bigger sensor value? I guess I will find out. It should be easy to test, as each F-stop on my enlarger lens increases / decreases the light output by 2.
@jasonacox
I bought 5 new RGB LED bulbs and a new smart outlet switch. I paired the devices. They work in the smartlife app. I pulled their data from the cloud with the wizard. But for some reason the scan does not find the new devices on the network.
Polling local devices... [Dark 1 ] Error: No IP found [Dark 2 ] Error: No IP found [Dark 3 ] Error: No IP found [Dark 4 ] Error: No IP found [Dark 5 ] Error: No IP found [Dark Switch ] Error: No IP found
What could be wrong? Could it be that the new devices connect over a different port? I would gladly help testing / debugging to get this problem resolved.
Maybe the new devices use yet another version of the API to communicate?
Hi @measwel , sure that is all possible. Can you share a link to thee bulbs so we can get some to test?
Also, can you provide the debug dump? tinytuya.set_debug(True)
in your code or if you are doing scanner: python3 -m tinytuya scan -d
Debug scan output:
DEBUG:Listening for Tuya devices on UDP ports 6666, 6667 and 7000 DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} enlarger Product ID = keyjup78v54myhan [Valid Broadcast]: Address = 192.168.51.246 Device ID = bfadddf28e43521d4c7man (len:22) Local Key = U0-7k~0$=!'yM-z5 Version = 3.3 Type = default, MAC = 10:5a:17:dc:fd:32 Polling 192.168.51.246 Failed: DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} Scan completed in 18.0334 seconds
Scan Complete! Found 1 devices. Broadcasted: 1 Versions: 3.3: 1
It seems to find just 1 device, which is my older smart plug switch.
Interesting, both of those inks are 404 for me, but may be region locked. What is the exact name of the product I can search for?
As to the devices, please confirm they are just WiFi and not using a gateway (e.g. Zigbee)? Also, try to have the scanner do a force scan python3 -m tinytuya scan -f -d
and if it can't find them, see if your router is able to detect what DHCP IP was assigned to them.
Updated the links. Will do the forced scan now.
Sadly, link still show:
Parsed args: Namespace(debug=False, command='scan', debug2=True, max_time=None, force=[[]], no_broadcasts=False, nocolor=False, yes=False, device_file='devices.json', snapshot_file='snapshot.json') DEBUG:TinyTuya [1.14.1]
DEBUG:Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32 DEBUG:Using pyca/cryptography 42.0.8 for crypto, GCM is supported DEBUG:loaded=devices.json [19 devices]
TinyTuya (Tuya device scanner) [1.14.1]
[Loaded devices.json - 19 devices]
Scanning on UDP ports 6666 and 6667 and 7000 for devices for 18 seconds...
DEBUG:Listening for Tuya devices on UDP ports 6666, 6667 and 7000 Option: Network force scanning requested.
Running Scan...
DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} DEBUG:final payload_dict for 'bfadddf28e43521d4c7man' ('v3.3'/'default'): {1: {'command': {'gwId': '', 'devId': '', 'uid': '', 't': ''}}, 7: {'command': {'devId': '', 'uid': '', 't': ''}}, 8: {'command': {'gwId': '', 'devId': ''}}, 9: {'command': {'gwId': '', 'devId': ''}}, 10: {'command': {'gwId': '', 'devId': '', 'uid': '', 't': ''}}, 13: {'command': {'devId': '', 'uid': '', 't': ''}}, 16: {'command': {'devId': '', 'uid': '', 't': ''}}, 18: {'command': {'dpId': [18, 19, 20]}}, 64: {'command': {'reqType': '', 'data': {}}}} DEBUG:building command 10 payload=b'{"gwId":"bfadddf28e43521d4c7man","devId":"bfadddf28e43521d4c7man","uid":"bfadddf28e43521d4c7man","t":"1719260865"}' DEBUG:payload encrypted=b'000055aa000000010000000a00000088e0dde68977b3b8bf5d0f7d1512e0cf11917f13e6ebf2cc3253fce84e58712b0cfe03926d050ede078f9e3b27d31b9eb74771ba33d99c4be5bd1908e66477f53ae63d2da1c17239f6ee9c53e11928f979917f13e6ebf2cc3253fce84e58712b0ce1a1a862e02dbc599b7a4186bed57fdc17483fc9828cb6d0fb2cc1e02d473b2f0a287bf70000aa55' DEBUG:PollDevice: raw unpacked message = TuyaMessage(seqno=1, cmd=10, retcode=0, payload=b'm k\x9b\xec\xfb\xc1F+\n\x19\xf0\xd9\x01O\x02\x90\x04--\xaf\xb5ntt\xcd&\xe5\x8dsm\x0e;\x8b\x94\r\xd4u\x1a\xc6\xfd\x1c\x0b\x18\xd2\xcaL,T\xd6$ \x0c\xe8\xe7\x0f\'\xc6^\x8a1:\xbaX\x8a}\xbb\xdd\xf8\xe4\xd1\x8e\xaa\x16\xd4\xaa8\xf9\x8e"K\xfd\x8a\x7f\x18\xb2\x9ct\xaf-\xb1[\xe4}3\xa7\xc7F\xa9\x06\x0b\xd0\x00\x10\xd1\xb2~\xc3\x9a\xd3\xec\xdd\x8e\xd9\xb4\xa6\xa36\x0c/\x0c\xd3\x1c\xe4\x01j\xa3aS\xc8^\x06R\xb6\xa6Gm\xb6\xdb\x87\xe2\x99o\xac8Co\xd8\x03\xe1\x00.\xce\xb30}\xcc\xa2\x0e,V\xe7\x94:\xf5@\x9b\x00h\xecp\xf84\x96\x14O\x12\x88\xc7\x17\x9a[\xebi~.\x05\x9a\xa5R\xe7', crc=1430753997, crc_good=True, prefix=21930, iv=None) DEBUG:decode payload=b'm k\x9b\xec\xfb\xc1F+\n\x19\xf0\xd9\x01O\x02\x90\x04--\xaf\xb5ntt\xcd&\xe5\x8dsm\x0e;\x8b\x94\r\xd4u\x1a\xc6\xfd\x1c\x0b\x18\xd2\xcaL,T\xd6$ \x0c\xe8\xe7\x0f\'\xc6^\x8a1:\xbaX\x8a}\xbb\xdd\xf8\xe4\xd1\x8e\xaa\x16\xd4\xaa8\xf9\x8e"K\xfd\x8a\x7f\x18\xb2\x9ct\xaf-\xb1[\xe4}3\xa7\xc7F\xa9\x06\x0b\xd0\x00\x10\xd1\xb2~\xc3\x9a\xd3\xec\xdd\x8e\xd9\xb4\xa6\xa36\x0c/\x0c\xd3\x1c\xe4\x01j\xa3aS\xc8^\x06R\xb6\xa6Gm\xb6\xdb\x87\xe2\x99o\xac8Co\xd8\x03\xe1\x00.\xce\xb30}\xcc\xa2\x0e,V\xe7\x94:\xf5@\x9b\x00h\xecp\xf84\x96\x14O\x12\x88\xc7\x17\x9a[\xebi~.\x05\x9a\xa5R\xe7' DEBUG:decrypting=b'm k\x9b\xec\xfb\xc1F+\n\x19\xf0\xd9\x01O\x02\x90\x04--\xaf\xb5ntt\xcd&\xe5\x8dsm\x0e;\x8b\x94\r\xd4u\x1a\xc6\xfd\x1c\x0b\x18\xd2\xcaL,T\xd6$ \x0c\xe8\xe7\x0f\'\xc6^\x8a1:\xbaX\x8a}\xbb\xdd\xf8\xe4\xd1\x8e\xaa\x16\xd4\xaa8\xf9\x8e"K\xfd\x8a\x7f\x18\xb2\x9ct\xaf-\xb1[\xe4}3\xa7\xc7F\xa9\x06\x0b\xd0\x00\x10\xd1\xb2~\xc3\x9a\xd3\xec\xdd\x8e\xd9\xb4\xa6\xa36\x0c/\x0c\xd3\x1c\xe4*\x01j\xa3aS\xc8^\x06R\xb6\xa6Gm\xb6\xdb\x87\xe2\x99o\xac8Co\xd8\x03\xe1\x00.\xce\xb30}\xcc\xa2\x0e,V\xe7\x94:\xf5@\x9b\x00h\xecp\xf84\x96\x14O\x12\x88\xc7\x17\x9a[\xebi~.\x05\x9a\xa5R\xe7' DEBUG:decrypted 3.x payload='{"dps":{"1":false,"9":0,"18":0,"19":0,"20":2337,"21":1,"22":610,"23":30696,"24":17327,"25":2410,"26":0,"38":"memory","39":false,"40":"relay","41":false,"42":"","43":"","44":""}}' DEBUG:payload type = <class 'str'> DEBUG:decoded results='{"dps":{"1":false,"9":0,"18":0,"19":0,"20":2337,"21":1,"22":610,"23":30696,"24":17327,"25":2410,"26":0,"38":"memory","39":false,"40":"relay","41":false,"42":"","43":"","44":""}}' enlarger Product ID = keyjup78v54myhan [Valid Broadcast]: Address = 192.168.51.246 Device ID = bfadddf28e43521d4c7man (len:22) Local Key = U0-7k~0$=!'yM-z5 Version = 3.3 Type = default, MAC = 10:5a:17:dc:fd:32 Status: {'1': False, '9': 0, '18': 0, '19': 0, '20': 2337, '21': 1, '22': 610, '23': 30696, '24': 17327, '25': 2410, '26': 0, '38': 'memory', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''} DEBUG:Unable to get network for [], ignoring ERROR: Unable to get network for [], ignoring. Traceback (most recent call last): File "C:\Users\marek\AppData\Local\Programs\Python\Python312\Lib\site-packages\tinytuya\scanner.py", line 902, in _generate_ip network = ipaddress.ip_network(netblock) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\marek\AppData\Local\Programs\Python\Python312\Lib\ipaddress.py", line 83, in ip_network raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 network') ValueError: [] does not appear to be an IPv4 or IPv6 network
DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} DEBUG:Received valid UDP packet: {'ip': '192.168.51.246', 'gwId': 'bfadddf28e43521d4c7man', 'active': 2, 'ablilty': 0, 'encrypt': True, 'productKey': 'keyjup78v54myhan', 'version': '3.3'} Scan completed in 18.0453 seconds
Scan Complete! Found 1 devices. Broadcasted: 1 Force-Scanned: 0 - Matched GWID: 0 Matched Key: 0 Unmatched: 0 Versions: 3.3: 1
Saving device snapshot data to snapshot.json
DEBUG:Scan complete with 1 devices found
The device does not appear on the same network as your host. Do you have multiple WiFi networks set up?
Maybe the screenshots will help to find them.
Yes, I have 2 wifi networks. What is ment by the "host" ?
My main router is giving out IPs via DHCP and provides WiFi downstairs. Then I have a WiFi capable switch upstairs which just acts as a switch, providing WiFi upstairs. It has DHCP switched off.
The two smart outlets (one called 'enlarger', the other 'Dark Switch') are currently both connected to the WiFi upstairs. But for some reason, the scan finds one of them and not the other.
Maybe this will help too.
Old switch: "model": "BSD-BPEQ16" (works) New switch: "model": "\u767e\u89c6\u76fe\u8ba1\u91cf"
Old RGB LED: "model": "50-LM-025", "product_name": "E27 RGB CCT" New RGB LED: "model": "\u5bbd\u538b\u82af\u7247\u4e0a\u677f\u7403\u6ce1", "product_name": "LED BULB W5K"
Yes, I have 2 wifi networks. What is ment by the "host" ?
The computer you are running tinytuya scan
is the host. It must be on the same network as the device. Can you tell what what IP address your router assigns to these new Tuya devices? If so, are you able to ping them from your host computer?
They are on the network and I can ping them from the host.
Ok, so in the debug only 192.168.51.246
is sending broadcast message (I assume that was another one an you are only showing 4 new ones). That does seem very odd.
I would try: python3 -m tinytuya -f 192.168.51.0/24
And script with each IP (you mentioned you did get the ID and Key for these via Wizard):
d = tinytuya.OutletDevice(
dev_id=DEVICEID,
address="192.168.51.198,
local_key=DEVICEKEY,
version=3.3)
data = d.status()
print(data)
192.168.51.246 is the old outlet switch. The new outlet and the 5 new RGB Leds do not show up in the scan.
I would try direct access to see what happens:
d = tinytuya.OutletDevice(
dev_id=DEVICEID,
address="192.168.51.198,
local_key=DEVICEKEY,
version=3.3)
data = d.status()
print(data)
I will try. In the mean time, maybe this link works?
I tried both with BulbDevice and OutletDevice. I am not sure if I got the IP / deviceID / key combo right. I tried for all IPs. It all ended in:
DEBUG:status() received data={'Error': 'Check device key or version', 'Err': '914', 'Payload': None}
What version did you see from the wizard? Try changing the version in the script from 3.3 to 3.4 and 3.5.
@jasonacox, are you US or Europe based? I am thinking about reordering as one of the RGB LEDs I got is broken (only the blue led works). Ali tells me to send the whole order back for a refund which is nonsense.
I get a super deal on these devices and for 9 bucks I can get 2 more LEDs and 1 more switch. What if I let them be shipped to an address designated by you? Then you could keep one LED and switch, try them out and send one LED to me, so I can have 5 working ones?
I just checked with a bogus address in the UK. It seems they will ship to anywhere in EU for about 10 bucks.
@jasonacox
Update. I am able to connect to one of the new RGB LEDs using version 3.5 :
db = tinytuya.BulbDevice(dev_id=device_id, address=i, local_key=device_key, version=3.5, connection_timeout=1, connection_retry_limit=1, connection_retry_delay=0)
{'dps': {'20': True, '21': 'colour', '22': 10, '23': 269, '24': '006803e803e8', '25': '000e0d0000000000000000c80000', '26': 0, '34': False}}
I am also able to connect to the new smart plug, also by setting version to 3.5 and manually setting IP and key.
{'dps': {'1': True, '9': 0, '18': 0, '19': 0, '20': 2383, '21': 1, '22': 572, '23': 29257, '24': 15834, '25': 2630, '26': 0, '38': 'memory', '39': 'relay', '40': False, '41': '', '42': '', '43': '', '51': False}}
The scan does not find the devices however. Maybe these new devices broadcast on a different port?
I did notice this error when doing a forced scan:
def _generate_ip(networks, verbose, term):
for netblock in networks:
if tinytuya.IS_PY2 and type(netblock) == str:
netblock = netblock.decode('latin1')
try:
network = ipaddress.ip_network(netblock)
log.debug("Starting brute force network scan %s", network)
except:
log.debug("Unable to get network for %r, ignoring", netblock)
if verbose:
print(term.alert +
'ERROR: Unable to get network for %r, ignoring.' % netblock + term.normal)
print(traceback.format_exc())
continue
File "C:\Users\marek\AppData\Local\Programs\Python\Python312\Lib\site-packages\tinytuya\scanner.py", line 902, in _generate_ip
network = ipaddress.ip_network(netblock)
raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 network')
ValueError: [] does not appear to be an IPv4 or IPv6 network
I received the light sensor today. The scan finds it and I am able to get a reading from it. But the new bulbs and switch are not found by the scan. Here are direct links to the devices which are not found by the scanner. I hope these links work?
https://www.aliexpress.us/item/3256805811828814.html
https://www.aliexpress.us/item/3256805544389239.html
For the time being, I checked the new IPs on my network and made a small script to find working combinations of IP and device key. Then I had the script add the IP and Version in devices.json. This way all my devices work. The only problem is, that I was not able to find them with the tinytuya scanner.
Thanks @measwel - those links worked! I was able to order. I'll let you know what happens.
That's great. I am sure we will figure it out.
The force scan was broken in the last update, #511 fixes it.
You are the 2nd person to report a v3.5 device not broadcasting. I hope this is not a new trend...
I played with the light intensity sensor today. Some suprising results.
Good: it works :smile:.
Strange: with a opal light bulb the measurement is linear. Changing 1 stop on the lens halves the measured lux value. I also tried a LED bulb. In this case the measured values did not quite get halved, but it was close enough to be practical I guess.
Interesting: a cold white light gives more measured lux than a warm white light at the same brightness setting.
Bad: the sensitivity of the sensor is on the low side. The scale goes from 0 to 1000. With an average setting on my enlarger (20 x 20 cm print, F5.6) I am getting a reading of 16 lux. Making a big print or closing down the lens to F22 will bring the measured value to 0 lux.
Conclusion; it is sensible to use the strongest bulb feasible in the enlarger head. I will try with the strongest LED bulb I can find.
This should do it: https://www.ebay.de/itm/155860485532
4000K to get good contrast, opal white and a whopping 3500 lumen.
The force scan was broken in the last update, #511 fixes it.
You are the 2nd person to report a v3.5 device not broadcasting. I hope this is not a new trend...
An alternative - but maybe stupid - scanning procedure would be:
Find existing IPs in devices.json. Ping every other IP in the network : xxx.xxx.xxx.1 to xxx.xxx.xxx.255 (based on the found existing IPs). Exclude the already found IPs from the ping. Store the IPs which return the ping. (the v3.5 devices which I have do ping back). Try every combination of the pinged IPs and local device keys. If a device connects, we write its IP and version back into devices.json.
This is basically how I figured out the IP and version of the v3.5 devices which did not show up on the scan. The only difference being, that I did not ping to find the new IPs. I just exported them from my router.
Come to think of it... Probably something very similar is done by the forced scan. :)
This is the code I used:
import tinytuya
import json
tinytuya.set_debug(False)
IPs = ["192.168.51.20","192.168.51.21","192.168.51.112","192.168.51.147","192.168.51.163","192.168.51.198","192.168.51.207"]
with open('devices.json', 'r', encoding='utf-8') as f: dev = json.load(f)
for d in dev:
device_id = d["id"]
device_key = d["key"]
v = "3.5"
if not d["name"][:4] == "Dark":
continue
for i in IPs:
do = tinytuya.OutletDevice(dev_id=device_id, address=i, local_key=device_key, version=v, connection_timeout=1, connection_retry_limit=1, connection_retry_delay=0)
data = do.status()
if data and not "Error" in data:
d["version"] = v
d["ip"] = i
print(d["name"], i, v)
break
with open('devices.json', 'w', encoding='utf-8') as f: json.dump(dev, f, ensure_ascii=False, indent=2)
As said, the list of new IPs to try I got from my router. But I think one could try to find them with a ping on the same subnet. As all my device names ment for the Darkroom start with "Dark" I can skip the others. Also, I noticed it made no difference whether I used outlet or bulb for the connection test.
Good day,
The device shows up in the scan. The local key has been fetched with the wizard.
enlarger Product ID = keyjup78v54myhan [Valid Broadcast]: Address = 192.168.51.246 Device ID = bfadddf28e43521d4c7man (len:22) Local Key = xxxxxxxxx Version = 3.3 Type = default, MAC = 10:5a:17:dc:fd:32 Status: {'1': True, '9': 0, '18': 0, '19': 0, '20': 2346, '21': 1, '22': 610, '23': 30696, '24': 17327, '25': 2410, '26': 0, '38': 'memory', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}
I read through all the issues concerning 901 connection errors, but not found a solution.
DEBUG:ERROR Network Error: Unable to Connect - 901 - payload: null DEBUG:status() received data={'Error': 'Network Error: Unable to Connect', 'Err': '901', 'Payload': None}
I have 2 clues.
1 : the core.py throws an error:
File "...\AppData\Local\Programs\Python\Python312\Lib\site-packages\tinytuya\core.py", line 1009, in _get_socket if self.version >= 3.4: ^^^^^^^^^^^^^^^^^^^ TypeError: '>=' not supported between instances of 'str' and 'float'
2 : maybe other active connections are blocking access to the device?
I have a smart life app on my phone which shows the device. Disconnecting the device in the app does not work. When I disconnect without wiping data, the switch still loses its wifi settings. I have to repair it in the app to get it to show on the wifi network again.
Just closing the smart life app on the phone also does not help. Could it be, because other home members also have that app on their phone and are possibly making a connection? If so, how should I solve that? Should I remove the device from all the smart life apps?
Thank you.
So I do not know how to make sure no other active connections are present.