Open uzlonewolf opened 1 year ago
I love this! Going to play with it a bit more and drop any comments in-line...
Got almost everything implemented. Json/array handling got a bit convoluted as things like bulb scene data are integers inside a dict inside a list inside another dict, but it seems to be functional. Still needs a lot more testing and cleanup.
SmartBulb Product ID = keycuag84ttsx3fm [Valid Broadcast]:
Address = 10.0.1.83 Device ID = x (len:20) Local Key = x Version = 3.3 Type = default, MAC = b8:f0:09:01:3c:c3
Status: {'switch_led': True, 'work_mode': 'white', 'bright_value_v2': 1000, 'colour_data_v2': {'h': 0, 's': 0, 'v': 3}, 'scene_data_v2': {'scene_num': 0, 'scene_units': [{'unit_change_mode': 7, 'unit_switch_duration': 4, 'unit_gradient_duration': 6, 'bright': 70, 'temperature': 2, 'h': 0, 's': 0, 'v': 3}]}, 'countdown_1': 0}
Dining Room Product ID = MShdslm9Uw7Q59nN [Valid Broadcast]:
Address = 10.0.1.45 Device ID = x (len:20) Local Key = x Version = 3.3 Type = default, MAC = 2c:f4:32:a1:87:91
Status: {'switch_1': False, 'countdown_1': 0}
Plug Product ID = jllxx3xzvgweahib [Valid Broadcast]:
Address = 10.0.1.48 Device ID = x (len:20) Local Key = x Version = 3.3 Type = default, MAC = bc:dd:c2:3d:1b:11
Status: {'switch': True, 'countdown_1': 0, 'cur_current': 20, 'cur_power': 13, 'cur_voltage': 1192}
❤️ I absolutely love seeing the translated (human readable) DPS keys. I do wonder if we should provide an option to show DPS index numbers instead, maybe -raw
or -dps
?
In a scanner run... likely debug, but some lines like this show up:
read_data() failed, retrying 10.0.1.96
read_data() failed, retrying 10.0.1.32
read_data() failed, retrying 10.0.1.46
And...
parsing JSON json {'code': 'colour_data_v2', 'type': 'Json', 'raw_values': '{"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}', 'values': {'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 6 {'code': 'colour_data_v2', 'type': 'Json', 'raw_values': '{"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}', 'values': {'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
parsing JSON json {'code': 'scene_data_v2', 'type': 'Json', 'raw_values': '{"scene_num":{"min":1,"scale":0,"max":8,"step":1},"scene_units": {"unit_change_mode":{"range":["static","jump","gradient"]},"unit_switch_duration":{"min":0,"scale":0,"max":100,"step":1},"unit_gradient_duration":{"min":0,"scale":0,"max":100,"step":1},"bright":{"min":0,"scale":0,"max":1000,"step":1},"temperature":{"min":0,"scale":0,"max":1000,"step":1},"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}}', 'values': {'scene_num': {'min': 1, 'scale': 0, 'max': 8, 'step': 1}, 'scene_units': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}}
JSON key scene_num subtype Integer
JSON key scene_units subtype Array
parsing Array array {'type': 'Array', 'values': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
parsing JSON json {'type': 'Json', 'values': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
JSON key unit_change_mode subtype Enum_Integer
JSON key unit_switch_duration subtype Integer
JSON key unit_gradient_duration subtype Integer
JSON key bright subtype Integer
JSON key temperature subtype Integer
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 13 {'type': 'Json', 'values': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
Value len: 14 {'code': 'scene_data_v2', 'type': 'Json', 'raw_values': '{"scene_num":{"min":1,"scale":0,"max":8,"step":1},"scene_units": {"unit_change_mode":{"range":["static","jump","gradient"]},"unit_switch_duration":{"min":0,"scale":0,"max":100,"step":1},"unit_gradient_duration":{"min":0,"scale":0,"max":100,"step":1},"bright":{"min":0,"scale":0,"max":1000,"step":1},"temperature":{"min":0,"scale":0,"max":1000,"step":1},"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}}', 'values': {'scene_num': {'min': 1, 'scale': 0, 'max': 8, 'step': 1}, 'scene_units': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}}
parsing JSON json {'code': 'music_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
JSON key change_mode subtype Enum_Integer
JSON key bright subtype Integer
JSON key temperature subtype Integer
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 9 {'code': 'music_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
parsing JSON json {'code': 'control_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
JSON key change_mode subtype Enum_Integer
JSON key bright subtype Integer
JSON key temperature subtype Integer
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 9 {'code': 'control_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
parsing JSON json {'code': 'colour_data_v2', 'type': 'Json', 'raw_values': '{"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}', 'values': {'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 6 {'code': 'colour_data_v2', 'type': 'Json', 'raw_values': '{"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}', 'values': {'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
parsing JSON json {'code': 'scene_data_v2', 'type': 'Json', 'raw_values': '{"scene_num":{"min":1,"scale":0,"max":8,"step":1},"scene_units": {"unit_change_mode":{"range":["static","jump","gradient"]},"unit_switch_duration":{"min":0,"scale":0,"max":100,"step":1},"unit_gradient_duration":{"min":0,"scale":0,"max":100,"step":1},"bright":{"min":0,"scale":0,"max":1000,"step":1},"temperature":{"min":0,"scale":0,"max":1000,"step":1},"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}}', 'values': {'scene_num': {'min': 1, 'scale': 0, 'max': 8, 'step': 1}, 'scene_units': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}}
JSON key scene_num subtype Integer
JSON key scene_units subtype Array
parsing Array array {'type': 'Array', 'values': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
parsing JSON json {'type': 'Json', 'values': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
JSON key unit_change_mode subtype Enum_Integer
JSON key unit_switch_duration subtype Integer
JSON key unit_gradient_duration subtype Integer
JSON key bright subtype Integer
JSON key temperature subtype Integer
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 13 {'type': 'Json', 'values': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}
Value len: 14 {'code': 'scene_data_v2', 'type': 'Json', 'raw_values': '{"scene_num":{"min":1,"scale":0,"max":8,"step":1},"scene_units": {"unit_change_mode":{"range":["static","jump","gradient"]},"unit_switch_duration":{"min":0,"scale":0,"max":100,"step":1},"unit_gradient_duration":{"min":0,"scale":0,"max":100,"step":1},"bright":{"min":0,"scale":0,"max":1000,"step":1},"temperature":{"min":0,"scale":0,"max":1000,"step":1},"h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":1000,"step":1},"v":{"min":0,"scale":0,"unit":"","max":1000,"step":1}}}', 'values': {'scene_num': {'min': 1, 'scale': 0, 'max': 8, 'step': 1}, 'scene_units': {'unit_change_mode': {'range': ['static', 'jump', 'gradient']}, 'unit_switch_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'unit_gradient_duration': {'min': 0, 'scale': 0, 'max': 100, 'step': 1}, 'bright': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}}}}
parsing JSON json {'code': 'music_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
JSON key change_mode subtype Enum_Integer
JSON key bright subtype Integer
JSON key temperature subtype Integer
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 9 {'code': 'music_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
parsing JSON json {'code': 'control_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
JSON key change_mode subtype Enum_Integer
JSON key bright subtype Integer
JSON key temperature subtype Integer
JSON key h subtype Integer
JSON key s subtype Integer
JSON key v subtype Integer
Value len: 9 {'code': 'control_data', 'type': 'Json', 'raw_values': '{"change_mode":{"range":["direct","gradient"]}, "bright":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "temperature":{"min":0,"scale":0,"unit":"","max":1000,"step":1}, "h":{"min":0,"scale":0,"unit":"","max":360,"step":1},"s":{"min":0,"scale":0,"unit":"","max":255,"step":1},"v":{"min":0,"scale":0,"unit":"","max":255,"step":1}}', 'values': {'change_mode': {'range': ['direct', 'gradient']}, 'bright': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'temperature': {'min': 0, 'scale': 0, 'unit': '', 'max': 1000, 'step': 1}, 'h': {'min': 0, 'scale': 0, 'unit': '', 'max': 360, 'step': 1}, 's': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}, 'v': {'min': 0, 'scale': 0, 'unit': '', 'max': 255, 'step': 1}}}
I can definitely add that, but I'm not really sure it's needed for the scanner. I'm probably going to put the raw values in a "dps_raw" key and modify the "changed" list to return the dps object instead of just the name, and people can then check snapshot.json after running the scanner if they really need the raw values.
Sold! 😄 We can always add it later. Thanks LW! This is awesome.
Done. I also pulled out a bunch of debugging print()s to help with the flood while scanning.
#!/usr/bin/env python
import tinytuya
device = tinytuya.MappedDevice(
dev_id="XXX", local_key="XXX", persist=True
)
device.set_version(3.3)
device["switch"] = True
Results in:
Traceback (most recent call last):
File "./test.py", line 9, in <module>
device["switch"] = True
File "/home/xxx/source/tinytuya/tinytuya/MappedDevice.py", line 800, in __setitem__
return self.set_value( key, new_value )
File "/home/xxx/source/tinytuya/tinytuya/MappedDevice.py", line 826, in set_value
new_value = obj.encode_value( value, False )
TypeError: encode_value() takes 2 positional arguments but 3 were given
I believe here it should be changed from this:
def encode_value( self, new_value ):
return self.obj.encode_value( new_value, False )
to this:
def encode_value( self, new_value , pack = False ):
return self.obj.encode_value( new_value, pack=pack )
Thank you for the bug report, @spinza ! Yes, that does look like a good way to fix it.
I've written a Tuya MQTT device bridge using the MappedDevice class in this PR. It works on any device found in a local devices.json
file and publishes the device information to MQTT using the Homie convention. It listens for messages for the fields that are settable. So also allows MQTT control of Tuya devices. This bridge publishes Tuya messages as they are received to MQTT and vice versa. It also regularly polls the status of Tuya devices and publishes them to MQTT.
I am not sure if bitmaps are settable but that's the only function that won't work (if possible). I.e. you cannot set bitmap values from MQTT to Tuya but Tuya to MQTT flows work.
This class was very useful to generalise this.
My use case for this is OpenHAB. OpenHAB now automatically discovers Tuya devices as Things on the MQTT OpenHab integration. It also sets up appropriate channels with the right units etc.
Some of the unit functionality won't work as well but I don't have enough devices (I only have two) to test units.
Hmm, I wonder if it would be a good idea to expand bitmasks into an array of true/false values which you could set like d["somebitmask"].someflag = True
or d["somebitmask"]["someflag"] = False
(setting everything in 1 go with d["somebitmask"] = ["someflag", "anotherflag"]
would still work too of course). I would make each flag its own top-level item as if it were a DP, but then we run the risk of having bit names collide with actual DP names or a different bitmask DP with the same bit names. Maybe if we used an unused character as a separator such as d["somebitmask.someflag"] = False
?
That is essentially what I've done in the MQTT bridge. It takes the bitmap and publishes it as a series of boolean topics. With the bridge there is a risk of name collisions though.
The code I mention is available here
Can you make the above fix to your code, so if someone install it they can at least get my server working?
@spinza Fixed.
It's not functional yet, but I also started on expanding bitmaps. d = MappedDevice( ..., expand_bitmaps='.' )
(the default) will expand them to "dp_name.bit_option", expand_bitmaps=True
makes them just "bit_option" (watch out for name collisions with legit DPs!), and any value which evaluates to False (None, '', 0, etc) shuts it off.
Cool. Is the default behaviour unchanged with regard to bitmaps?
Kinda? I haven't finished implementing it yet so at the moment nothing has changed. When I'm done the "parent" DP bitmap will remain the same as it is now, but there will be additional pseudo-DPs added for each bit option. To shut that off (thereby keeping everything exactly the same as it is now) set d = MappedDevice( ..., expand_bitmaps=False )
Can a bitmap be settable?
Assuming the device allows it, yes. Currently you can set it either with an int (i.e. d['some_bitmap'] = 0xC0
) or with a list (i.e. d['some_bitmap'] = ['degrees_f', 'fan_auto']
). It will throw a ValueError if the int is too large or an unknown bit flag is included in the list.
Draft for now as there are still a few FIXME's and errant print()'s, but I wanted some feedback on the direction I was going.
The scanner has also been updated to use it. Instead of
you now get
As you can see, scaled ints are automatically turned into floats, enums are changed to their labels, and bitmasks are now lists of labels. When setting, everything is converted back as needed.
When updates are received either via a status() response or an async update, the dps dict keys are mapped to the DP names. If the received value is different than the last received value then the name is also added to a 'changed' list:
In addition to this, the last received value for any particular DP can be retrieved at any time:
The d.dps[...] object can also be used to get the int_min/int_max/enum_range/bitmask values for those data types as well as the DP ID and any name(s) associated with that DP, in addition to the last known (parsed) value and raw value.
Example d.dps[...] object for a bitmap:
Setting new values can be done in a similar dict-like manner; all of these are equivalent:
Since dict-like access cannot pass the
nowait
flag, there is now a device-level switch for it:Todo:
Implement the 'Json' data type, implementcleanup and documentation.d.set_multiple_values()
, implementd.set_timer()
, andCloses #185