From 662e35d856d2a1ac3c91644f4392f7d82e24b056 Mon Sep 17 00:00:00 2001 From: bskaplou Date: Thu, 28 May 2020 13:00:18 +0300 Subject: [PATCH 01/10] Add support for cube and suqarebutton via fake_device --- miio/fake_device.py | 107 ++++++++ miio/gateway.py | 112 ++++++++- miio/gateway_scripts.py | 542 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 760 insertions(+), 1 deletion(-) create mode 100644 miio/fake_device.py create mode 100644 miio/gateway_scripts.py diff --git a/miio/fake_device.py b/miio/fake_device.py new file mode 100644 index 000000000..bc5830c4e --- /dev/null +++ b/miio/fake_device.py @@ -0,0 +1,107 @@ +import calendar +import datetime +import logging +import socket +import struct +import ifaddr +from functools import reduce +from miio.protocol import Message + +_LOGGER = logging.getLogger(__name__) + +def ipv4_nonloop_ips(): + def flatten(a, b): + a.extend(b) + return a + + return list( + filter( + lambda ip: isinstance(ip, str) and not ip.startswith("127"), + map( + lambda ip: ip.ip, + reduce( + flatten, map(lambda adapter: adapter.ips, ifaddr.get_adapters()), [] + ), + ), + ) + ) + +class FakeDevice: + _device_id = None + _address = None + _token = None + + def __init__(self, device_id, token, address="0.0.0.0"): + self._device_id = device_id + self._token = token + self._address = address + + def run(self, callback): + def build_ack(device: int): + # Original devices are using year 1970, but it seems current datetime is fine + timestamp = calendar.timegm(datetime.datetime.now().timetuple()) + # ACK packet not signed, 16 bytes header + 16 bytes of zeroes + return struct.pack(">HHIII16s", 0x2131, 32, 0, device, timestamp, bytes(16)) + + helobytes = bytes.fromhex( + "21310020ffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ) + + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) + # Gateway interacts only with port 54321 + sock.bind((self._address, 54321)) + _LOGGER.info( + "fake miio device started with address=%s device_id=%s callback=%s token=****", + self._address, + self._device_id, + callback, + ) + + while True: + data, [host, port] = sock.recvfrom(1024) + if data == helobytes: + _LOGGER.debug("%s:%s=>PING", host, port) + m = build_ack(self._device_id) + sock.sendto(m, (host, port)) + _LOGGER.debug("%s:%s<=ACK(device_id=%s)", host, port, self._device_id) + else: + request = Message.parse(data, token=self._token) + value = request.data.value + _LOGGER.debug("%s:%s=>%s", host, port, value) + action, device_call_id = value["method"].split("_") + source_device_id = ( + f"lumi.{device_call_id}" # All known devices use lumi. prefix + ) + callback(source_device_id, action, value["params"]) + # This result means OK, but some methods return ['ok'] instead of 0 + # might be necessary to use different results for different methods + result = {"result": 0, "id": value["id"]} + header = { + "length": 0, + "unknown": 0, + "device_id": self._device_id, + "ts": datetime.datetime.now(), + } + msg = { + "data": {"value": result}, + "header": {"value": header}, + "checksum": 0, + } + response = Message.build(msg, token=self._token) + _LOGGER.debug("%s:%s<=%s", host, port, result) + sock.sendto(response, (host, port)) + + +if __name__ == "__main__": + + def callback(source_device, action, params): + _LOGGER.debug(f"CALLBACK {source_device}=>{action}({params})") + + from gateway_scripts import tokens, fake_device_id + + logging.basicConfig(level="DEBUG") + + device_id = int(fake_device_id) + device_token = bytes.fromhex(tokens["real"]) + fake_device = FakeDevice(device_id, device_token) + fake_device.run(callback) diff --git a/miio/gateway.py b/miio/gateway.py index 953be0159..73a86140f 100644 --- a/miio/gateway.py +++ b/miio/gateway.py @@ -8,10 +8,26 @@ import attr import click +from .fake_device import ipv4_nonloop_ips from .click_common import EnumType, command, format_output from .device import Device from .exceptions import DeviceException from .utils import brightness_and_color_to_int, int_to_brightness, int_to_rgb +from .gateway_scripts import ( + action_id, + build_doublepress, + build_flip90, + build_flip180, + build_longpress, + build_move, + build_rotate, + build_shake, + build_shakeair, + build_singlepress, + build_taptap, + tokens, +) + _LOGGER = logging.getLogger(__name__) @@ -28,7 +44,6 @@ "purple": (128, 0, 128), } - class GatewayException(DeviceException): """Exception for the Xioami Gateway communication.""" @@ -161,6 +176,8 @@ def discover_devices(self): DeviceType.Plug: AqaraPlug, DeviceType.SensorHT: SensorHT, DeviceType.AqaraHT: AqaraHT, + DeviceType.Cube: Cube, + DeviceType.AqaraSquareButton: AqaraSquareButton, DeviceType.AqaraMagnet: AqaraMagnet, DeviceType.AqaraSwitchOneChannel: AqaraSwitchOneChannel, DeviceType.AqaraSwitchTwoChannels: AqaraSwitchTwoChannels, @@ -201,6 +218,38 @@ def discover_devices(self): return self._devices + def x_del(self, script_id): + """Delete script by id.""" + return self.send("miIO.xdel", [script_id]) + + @command(click.argument("sid"), click.argument("command")) + def zigbee_command(self, sid, command): + self.discover_devices() + target = list(filter(lambda subdevice: subdevice.sid == sid,self.devices)) + if len(target) < 1: + return f"Device with sid={sid} not found" + elif not hasattr(target[0], command): + return f"Device with sid={sid} has no method {command}" + else: + return getattr(target[0], command)() + + def install_script(self, sid, action, builder): + addresses = ipv4_nonloop_ips() + my_ip = addresses[0] # Taking first public IP ;( + _LOGGER.info("Using address %s for action %s of %s", my_ip, action, sid) + data_tkn = tokens["data_tkn"] + source = builder(sid, my_ip) + return self.send( + "send_data_frame", + { + "cur": 0, + "data": source, + "data_tkn": data_tkn, + "total": 1, + "type": "scene", + }, + ) + @command(click.argument("property")) def get_prop(self, property): """Get the value of a property for given sid.""" @@ -738,6 +787,17 @@ def get_firmware_version(self) -> Optional[int]: ) return self._fw_ver + @command() + def uninstall_scripts(self): + return dict( + map( + lambda action: ( + action, + (action_id[action](self.sid), self._gw.x_del(action_id[action](self.sid))), + ), + action_id.keys(), + ) + ) class AqaraHT(SubDevice): """Subdevice AqaraHT specific properties and methods""" @@ -921,3 +981,53 @@ def update(self): self._props.status_ch0 = values[0] self._props.status_ch1 = values[1] self._props.load_power = values[2] + +class Cube(SubDevice): + """Subdevice Cube specific properties and methods""" + + properties = [] + + @command() + def install_move_script(self): + return self._gw.install_script(self.sid, "move", build_move) + + @command() + def install_rotate_script(self): + return self._gw.install_script(self.sid, "rotate", build_rotate) + + @command() + def install_shake_script(self): + return self._gw.install_script(self.sid, "shakeair", build_shakeair) + + @command() + def install_flip90_script(self): + return self._gw.install_script(self.sid, "flip90", build_flip90) + + @command() + def install_taptap_script(self): + return self._gw.install_script(self.sid, "taptap", build_taptap) + + @command() + def install_flip180_script(self): + return self._gw.install_script(self.sid, "flip180", build_flip180) + +class AqaraSquareButton(SubDevice): + """Subdevice AqaraSquareButton specific properties and methods""" + + properties = [] + + @command() + def install_singlepress_script(self): + return self._gw.install_script(self.sid, "singlepress", build_singlepress) + + @command() + def install_doublepress_script(self): + return self._gw.install_script(self.sid, "doublepress", build_doublepress) + + @command() + def install_longpress_script(self): + return self._gw.install_script(self.sid, "longpress", build_longpress) + + @command() + def install_shake_script(self): + return self._gw.install_script(self.sid, "shake", build_shake) diff --git a/miio/gateway_scripts.py b/miio/gateway_scripts.py new file mode 100644 index 000000000..318d68fdf --- /dev/null +++ b/miio/gateway_scripts.py @@ -0,0 +1,542 @@ +from json import dumps as dumps_orig +from random import randint + +separators = (",", ":") + + +def dumps(data): + return dumps_orig(data, separators=separators) + + +# token in script doesn't match token of device which is used for (enc/dec)ryption +# but they are linked somehow +tokens = { + "real": "9bc7c7ce6291d3e443fd7708608b9892", + "encoded": "79cf21b08fb051499389f23c113477a4", + "data_tkn": 29576, +} + +fake_device_id = "120009025" +fake_device_model = "chuangmi.plug.v3" + + +def sid_to_num(sid): + lumi, hex_id = sid.split(".") + num_id = int.from_bytes(bytes.fromhex(hex_id), byteorder="big") + return str(num_id)[-6:] + + +action_prefix = "x.scene." + +# Keeping action script tail decimal int it might be used as index in some db +action_id = { + "move": lambda sid: action_prefix + "1" + sid_to_num(sid), + "rotate": lambda sid: action_prefix + "2" + sid_to_num(sid), + "singlepress": lambda sid: action_prefix + "3" + sid_to_num(sid), + "doublepress": lambda sid: action_prefix + "4" + sid_to_num(sid), + "shake": lambda sid: action_prefix + "5" + sid_to_num(sid), + "longpress": lambda sid: action_prefix + "6" + sid_to_num(sid), + "flip90": lambda sid: action_prefix + "7" + sid_to_num(sid), + "flip180": lambda sid: action_prefix + "8" + sid_to_num(sid), + "shakeair": lambda sid: action_prefix + "9" + sid_to_num(sid), + "taptap": lambda sid: action_prefix + "10" + sid_to_num(sid), +} + + +def build_move( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_cube.v1", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"move_{source_id}" + + move = [ + [ + action_id["move"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,18,2,85,[6,256],0,0]", + "key": "event." + source_model + ".move", + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + + return dumps(move) + + +def build_flip90( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_cube.v1", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"flip90_{source_id}" + + flip90 = [ + [ + action_id["flip90"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,18,2,85,[6,64],0,0]", + "key": "event." + source_model + ".flip90", + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + + return dumps(flip90) + + +def build_flip180( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_cube.v1", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"flip180_{source_id}" + + flip180 = [ + [ + action_id["flip180"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,18,2,85,[6,128],0,0]", + "key": "event." + source_model + ".flip180", + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + + return dumps(flip180) + + +def build_taptap( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_cube.v1", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"taptap_{source_id}" + + taptap = [ + [ + action_id["taptap"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,18,2,85,[6,512],0,0]", + "key": "event." + source_model + ".tap_twice", + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + + return dumps(taptap) + + +def build_shakeair( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_cube.v1", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"shakeair_{source_id}" + + shakeair = [ + [ + action_id["shakeair"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,18,2,85,[0,0],0,0]", + "key": "event." + source_model + ".shake_air", + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + + return dumps(shakeair) + + +def build_rotate( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_cube.v1", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"rotate_{source_id}" + + rotate = [ + [ + action_id["rotate"](source_sid), + [ + "1.0", # version?? + randint( + 1590161094, 1590162094 + ), # id of automation in mi home database?? + [ + "0", # just zero.. + { + "did": source_sid, # gateway subdevice sid / origin of action zigbee sid + "extra": "[1,12,3,85,[1,0],0,0]", # ??? + "key": "event." + source_model + ".rotate", # event_id + "model": source_model, + "src": "device", + "timespan": [ + "0 0 * * 0,1,2,3,4,5,6", + "0 0 * * 0,1,2,3,4,5,6", + ], # cron-style always do?? + "token": "", + }, + ], + [ + { + "command": target_model + + "." + + method_name, # part after last dot (rotate) will be used as miio method in gateway callback + "did": target_id, # device identifier used in all responses of device + "extra": "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", # ??? + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": [ + 20, + 500, + ], # don't fire event if rotation_angle < 20 and rotation_angle > 500 ?? + } + ], + ], + ] + ] + + return dumps(rotate) + + +def build_singlepress( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_switch.aq3", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"singlepress_{source_id}" + + singlepress = [ + [ + action_id["singlepress"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,13,1,85,[0,1],0,0]", + "key": "event." + source_model + ".click", # event_id + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + print(dumps(singlepress)) + return dumps(singlepress) + + +def build_doublepress( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_switch.aq3", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"doublepress_{source_id}" + + doublepress = [ + [ + action_id["doublepress"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,13,1,85,[0,2],0,0]", + "key": "event." + source_model + ".double_click", # event_id + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + return dumps(doublepress) + + +def build_longpress( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_switch.aq3", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"longpress_{source_id}" + + longpress = [ + [ + action_id["longpress"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,13,1,85,[0,16],0,0]", + "key": "event." + + source_model + + ".long_click_press", # event_id + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + return dumps(longpress) + + +def build_shake( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_switch.aq3", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + method_name = f"shake_{source_id}" + + shake = [ + [ + action_id["shake"](source_sid), + [ + "1.0", + randint(1590161094, 1590162094), + [ + "0", + { + "did": source_sid, + "extra": "[1,13,1,85,[0,18],0,0]", + "key": "event." + source_model + ".shake", # event_id + "model": source_model, + "src": "device", + "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], + "token": "", + }, + ], + [ + { + "command": target_model + "." + method_name, + "did": target_id, + "extra": "", + "id": message_id, + "ip": target_ip, + "model": target_model, + "token": tokens["encoded"], + "value": "", + } + ], + ], + ] + ] + return dumps(shake) From 485da59a71fa31f14f4aff6f74595de057cac86d Mon Sep 17 00:00:00 2001 From: bskaplou Date: Thu, 28 May 2020 13:01:53 +0300 Subject: [PATCH 02/10] cleanup --- miio/fake_device.py | 5 ++++- miio/gateway.py | 16 +++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/miio/fake_device.py b/miio/fake_device.py index bc5830c4e..28c6113e1 100644 --- a/miio/fake_device.py +++ b/miio/fake_device.py @@ -3,12 +3,14 @@ import logging import socket import struct -import ifaddr from functools import reduce + +import ifaddr from miio.protocol import Message _LOGGER = logging.getLogger(__name__) + def ipv4_nonloop_ips(): def flatten(a, b): a.extend(b) @@ -26,6 +28,7 @@ def flatten(a, b): ) ) + class FakeDevice: _device_id = None _address = None diff --git a/miio/gateway.py b/miio/gateway.py index 73a86140f..844ad17b7 100644 --- a/miio/gateway.py +++ b/miio/gateway.py @@ -8,11 +8,10 @@ import attr import click -from .fake_device import ipv4_nonloop_ips from .click_common import EnumType, command, format_output from .device import Device from .exceptions import DeviceException -from .utils import brightness_and_color_to_int, int_to_brightness, int_to_rgb +from .fake_device import ipv4_nonloop_ips from .gateway_scripts import ( action_id, build_doublepress, @@ -27,7 +26,7 @@ build_taptap, tokens, ) - +from .utils import brightness_and_color_to_int, int_to_brightness, int_to_rgb _LOGGER = logging.getLogger(__name__) @@ -44,6 +43,7 @@ "purple": (128, 0, 128), } + class GatewayException(DeviceException): """Exception for the Xioami Gateway communication.""" @@ -225,7 +225,7 @@ def x_del(self, script_id): @command(click.argument("sid"), click.argument("command")) def zigbee_command(self, sid, command): self.discover_devices() - target = list(filter(lambda subdevice: subdevice.sid == sid,self.devices)) + target = list(filter(lambda subdevice: subdevice.sid == sid, self.devices)) if len(target) < 1: return f"Device with sid={sid} not found" elif not hasattr(target[0], command): @@ -793,12 +793,16 @@ def uninstall_scripts(self): map( lambda action: ( action, - (action_id[action](self.sid), self._gw.x_del(action_id[action](self.sid))), + ( + action_id[action](self.sid), + self._gw.x_del(action_id[action](self.sid)), + ), ), action_id.keys(), ) ) + class AqaraHT(SubDevice): """Subdevice AqaraHT specific properties and methods""" @@ -982,6 +986,7 @@ def update(self): self._props.status_ch1 = values[1] self._props.load_power = values[2] + class Cube(SubDevice): """Subdevice Cube specific properties and methods""" @@ -1011,6 +1016,7 @@ def install_taptap_script(self): def install_flip180_script(self): return self._gw.install_script(self.sid, "flip180", build_flip180) + class AqaraSquareButton(SubDevice): """Subdevice AqaraSquareButton specific properties and methods""" From 0f474617a7d5e399a62aca23fd3fbed23d9707d8 Mon Sep 17 00:00:00 2001 From: bskaplou Date: Fri, 29 May 2020 01:02:11 +0300 Subject: [PATCH 03/10] cleanup --- miio/gateway_scripts.py | 479 +++++++++++----------------------------- 1 file changed, 134 insertions(+), 345 deletions(-) diff --git a/miio/gateway_scripts.py b/miio/gateway_scripts.py index 318d68fdf..083488974 100644 --- a/miio/gateway_scripts.py +++ b/miio/gateway_scripts.py @@ -43,21 +43,26 @@ def sid_to_num(sid): } -def build_move( +def _inflate( + action, + extra, source_sid, + source_model, + target_id, target_ip, - target_model=fake_device_model, - target_id=fake_device_id, - source_model="lumi.sensor_cube.v1", - message_id=0, + target_model, + message_id, + event=None, + command_extra="", ): + if event is None: + event = action lumi, source_id = source_sid.split(".") - method_name = f"move_{source_id}" - move = [ + return [ [ - action_id["move"](source_sid), + action_id[action](source_sid), [ "1.0", randint(1590161094, 1590162094), @@ -65,8 +70,8 @@ def build_move( "0", { "did": source_sid, - "extra": "[1,18,2,85,[6,256],0,0]", - "key": "event." + source_model + ".move", + "extra": extra, + "key": "event." + source_model + "." + event, "model": source_model, "src": "device", "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], @@ -75,9 +80,9 @@ def build_move( ], [ { - "command": target_model + "." + method_name, + "command": target_model + "." + action + "_" + source_id, "did": target_id, - "extra": "", + "extra": command_extra, "id": message_id, "ip": target_ip, "model": target_model, @@ -89,6 +94,27 @@ def build_move( ] ] + +def build_move( + source_sid, + target_ip, + target_model=fake_device_model, + target_id=fake_device_id, + source_model="lumi.sensor_cube.v1", + message_id=0, +): + + lumi, source_id = source_sid.split(".") + move = _inflate( + "move", + "[1,18,2,85,[6,256],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) return dumps(move) @@ -102,42 +128,16 @@ def build_flip90( ): lumi, source_id = source_sid.split(".") - method_name = f"flip90_{source_id}" - - flip90 = [ - [ - action_id["flip90"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,18,2,85,[6,64],0,0]", - "key": "event." + source_model + ".flip90", - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] - + flip90 = _inflate( + "flip90", + "[1,18,2,85,[6,64],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) return dumps(flip90) @@ -151,42 +151,16 @@ def build_flip180( ): lumi, source_id = source_sid.split(".") - method_name = f"flip180_{source_id}" - - flip180 = [ - [ - action_id["flip180"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,18,2,85,[6,128],0,0]", - "key": "event." + source_model + ".flip180", - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] - + flip180 = _inflate( + "flip180", + "[1,18,2,85,[6,128],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) return dumps(flip180) @@ -200,42 +174,17 @@ def build_taptap( ): lumi, source_id = source_sid.split(".") - method_name = f"taptap_{source_id}" - - taptap = [ - [ - action_id["taptap"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,18,2,85,[6,512],0,0]", - "key": "event." + source_model + ".tap_twice", - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] - + taptap = _inflate( + "taptap", + "[1,18,2,85,[6,512],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "tap_twice", + ) return dumps(taptap) @@ -249,42 +198,17 @@ def build_shakeair( ): lumi, source_id = source_sid.split(".") - method_name = f"shakeair_{source_id}" - - shakeair = [ - [ - action_id["shakeair"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,18,2,85,[0,0],0,0]", - "key": "event." + source_model + ".shake_air", - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] - + shakeair = _inflate( + "shakeair", + "[1,18,2,85,[0,0],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "shake_air", + ) return dumps(shakeair) @@ -298,52 +222,17 @@ def build_rotate( ): lumi, source_id = source_sid.split(".") - method_name = f"rotate_{source_id}" - - rotate = [ - [ - action_id["rotate"](source_sid), - [ - "1.0", # version?? - randint( - 1590161094, 1590162094 - ), # id of automation in mi home database?? - [ - "0", # just zero.. - { - "did": source_sid, # gateway subdevice sid / origin of action zigbee sid - "extra": "[1,12,3,85,[1,0],0,0]", # ??? - "key": "event." + source_model + ".rotate", # event_id - "model": source_model, - "src": "device", - "timespan": [ - "0 0 * * 0,1,2,3,4,5,6", - "0 0 * * 0,1,2,3,4,5,6", - ], # cron-style always do?? - "token": "", - }, - ], - [ - { - "command": target_model - + "." - + method_name, # part after last dot (rotate) will be used as miio method in gateway callback - "did": target_id, # device identifier used in all responses of device - "extra": "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", # ??? - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": [ - 20, - 500, - ], # don't fire event if rotation_angle < 20 and rotation_angle > 500 ?? - } - ], - ], - ] - ] - + rotate = _inflate( + "rotate", + "[1,12,3,85,[1,0],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "rotate" "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", + ) return dumps(rotate) @@ -357,42 +246,17 @@ def build_singlepress( ): lumi, source_id = source_sid.split(".") - method_name = f"singlepress_{source_id}" - - singlepress = [ - [ - action_id["singlepress"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,13,1,85,[0,1],0,0]", - "key": "event." + source_model + ".click", # event_id - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] - print(dumps(singlepress)) + singlepress = _inflate( + "singlepress", + "[1,13,1,85,[0,1],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "click", + ) return dumps(singlepress) @@ -406,41 +270,17 @@ def build_doublepress( ): lumi, source_id = source_sid.split(".") - method_name = f"doublepress_{source_id}" - - doublepress = [ - [ - action_id["doublepress"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,13,1,85,[0,2],0,0]", - "key": "event." + source_model + ".double_click", # event_id - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] + doublepress = _inflate( + "doublepress", + "[1,13,1,85,[0,2],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "double_click", + ) return dumps(doublepress) @@ -454,43 +294,17 @@ def build_longpress( ): lumi, source_id = source_sid.split(".") - method_name = f"longpress_{source_id}" - - longpress = [ - [ - action_id["longpress"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,13,1,85,[0,16],0,0]", - "key": "event." - + source_model - + ".long_click_press", # event_id - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] + longpress = _inflate( + "longpress", + "[1,13,1,85,[0,16],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "long_click_press", + ) return dumps(longpress) @@ -504,39 +318,14 @@ def build_shake( ): lumi, source_id = source_sid.split(".") - method_name = f"shake_{source_id}" - - shake = [ - [ - action_id["shake"](source_sid), - [ - "1.0", - randint(1590161094, 1590162094), - [ - "0", - { - "did": source_sid, - "extra": "[1,13,1,85,[0,18],0,0]", - "key": "event." + source_model + ".shake", # event_id - "model": source_model, - "src": "device", - "timespan": ["0 0 * * 0,1,2,3,4,5,6", "0 0 * * 0,1,2,3,4,5,6"], - "token": "", - }, - ], - [ - { - "command": target_model + "." + method_name, - "did": target_id, - "extra": "", - "id": message_id, - "ip": target_ip, - "model": target_model, - "token": tokens["encoded"], - "value": "", - } - ], - ], - ] - ] + shake = _inflate( + "shake", + "[1,13,1,85,[0,18],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) return dumps(shake) From a83ed86d023e91f1e3e333999a41f7a07ca15760 Mon Sep 17 00:00:00 2001 From: bskaplou Date: Fri, 29 May 2020 01:05:12 +0300 Subject: [PATCH 04/10] cleanup --- miio/gateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miio/gateway.py b/miio/gateway.py index 844ad17b7..7e67a16c6 100644 --- a/miio/gateway.py +++ b/miio/gateway.py @@ -223,7 +223,7 @@ def x_del(self, script_id): return self.send("miIO.xdel", [script_id]) @command(click.argument("sid"), click.argument("command")) - def zigbee_command(self, sid, command): + def subdevice_command(self, sid, command): self.discover_devices() target = list(filter(lambda subdevice: subdevice.sid == sid, self.devices)) if len(target) < 1: From 2849a2e0b24824abe42c46180076f05401b6a3e1 Mon Sep 17 00:00:00 2001 From: bskaplou Date: Fri, 29 May 2020 01:28:52 +0300 Subject: [PATCH 05/10] cleanup --- miio/gateway.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/miio/gateway.py b/miio/gateway.py index 7e67a16c6..0dddd7103 100644 --- a/miio/gateway.py +++ b/miio/gateway.py @@ -233,10 +233,9 @@ def subdevice_command(self, sid, command): else: return getattr(target[0], command)() - def install_script(self, sid, action, builder): + def install_script(self, sid, builder): addresses = ipv4_nonloop_ips() my_ip = addresses[0] # Taking first public IP ;( - _LOGGER.info("Using address %s for action %s of %s", my_ip, action, sid) data_tkn = tokens["data_tkn"] source = builder(sid, my_ip) return self.send( @@ -994,27 +993,27 @@ class Cube(SubDevice): @command() def install_move_script(self): - return self._gw.install_script(self.sid, "move", build_move) + return self._gw.install_script(self.sid, build_move) @command() def install_rotate_script(self): - return self._gw.install_script(self.sid, "rotate", build_rotate) + return self._gw.install_script(self.sid, build_rotate) @command() def install_shake_script(self): - return self._gw.install_script(self.sid, "shakeair", build_shakeair) + return self._gw.install_script(self.sid, build_shakeair) @command() def install_flip90_script(self): - return self._gw.install_script(self.sid, "flip90", build_flip90) + return self._gw.install_script(self.sid, build_flip90) @command() def install_taptap_script(self): - return self._gw.install_script(self.sid, "taptap", build_taptap) + return self._gw.install_script(self.sid, build_taptap) @command() def install_flip180_script(self): - return self._gw.install_script(self.sid, "flip180", build_flip180) + return self._gw.install_script(self.sid, build_flip180) class AqaraSquareButton(SubDevice): @@ -1024,16 +1023,16 @@ class AqaraSquareButton(SubDevice): @command() def install_singlepress_script(self): - return self._gw.install_script(self.sid, "singlepress", build_singlepress) + return self._gw.install_script(self.sid, build_singlepress) @command() def install_doublepress_script(self): - return self._gw.install_script(self.sid, "doublepress", build_doublepress) + return self._gw.install_script(self.sid, build_doublepress) @command() def install_longpress_script(self): - return self._gw.install_script(self.sid, "longpress", build_longpress) + return self._gw.install_script(self.sid, build_longpress) @command() def install_shake_script(self): - return self._gw.install_script(self.sid, "shake", build_shake) + return self._gw.install_script(self.sid, build_shake) From d7d6367cde73eb541ca4d740d2b19cc904ca440b Mon Sep 17 00:00:00 2001 From: bskaplou Date: Fri, 29 May 2020 01:39:24 +0300 Subject: [PATCH 06/10] cleanup --- miio/gateway_scripts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/miio/gateway_scripts.py b/miio/gateway_scripts.py index 083488974..d1f6635d3 100644 --- a/miio/gateway_scripts.py +++ b/miio/gateway_scripts.py @@ -231,7 +231,8 @@ def build_rotate( target_ip, target_model, message_id, - "rotate" "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", + "rotate", + "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", ) return dumps(rotate) From 8c23c5be192bb98ae77fcea930f029f204b6a09d Mon Sep 17 00:00:00 2001 From: bskaplou Date: Fri, 29 May 2020 01:46:03 +0300 Subject: [PATCH 07/10] cleanup --- miio/gateway_scripts.py | 234 ++++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 117 deletions(-) diff --git a/miio/gateway_scripts.py b/miio/gateway_scripts.py index d1f6635d3..1f34dea96 100644 --- a/miio/gateway_scripts.py +++ b/miio/gateway_scripts.py @@ -104,18 +104,18 @@ def build_move( message_id=0, ): - lumi, source_id = source_sid.split(".") - move = _inflate( - "move", - "[1,18,2,85,[6,256],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, + return dumps( + _inflate( + "move", + "[1,18,2,85,[6,256],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) ) - return dumps(move) def build_flip90( @@ -127,18 +127,18 @@ def build_flip90( message_id=0, ): - lumi, source_id = source_sid.split(".") - flip90 = _inflate( - "flip90", - "[1,18,2,85,[6,64],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, + return dumps( + _inflate( + "flip90", + "[1,18,2,85,[6,64],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) ) - return dumps(flip90) def build_flip180( @@ -150,18 +150,18 @@ def build_flip180( message_id=0, ): - lumi, source_id = source_sid.split(".") - flip180 = _inflate( - "flip180", - "[1,18,2,85,[6,128],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, + return dumps( + _inflate( + "flip180", + "[1,18,2,85,[6,128],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) ) - return dumps(flip180) def build_taptap( @@ -173,19 +173,19 @@ def build_taptap( message_id=0, ): - lumi, source_id = source_sid.split(".") - taptap = _inflate( - "taptap", - "[1,18,2,85,[6,512],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, - "tap_twice", + return dumps( + _inflate( + "taptap", + "[1,18,2,85,[6,512],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "tap_twice", + ) ) - return dumps(taptap) def build_shakeair( @@ -197,19 +197,19 @@ def build_shakeair( message_id=0, ): - lumi, source_id = source_sid.split(".") - shakeair = _inflate( - "shakeair", - "[1,18,2,85,[0,0],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, - "shake_air", + return dumps( + _inflate( + "shakeair", + "[1,18,2,85,[0,0],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "shake_air", + ) ) - return dumps(shakeair) def build_rotate( @@ -221,20 +221,20 @@ def build_rotate( message_id=0, ): - lumi, source_id = source_sid.split(".") - rotate = _inflate( - "rotate", - "[1,12,3,85,[1,0],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, - "rotate", - "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", + return dumps( + _inflate( + "rotate", + "[1,12,3,85,[1,0],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "rotate", + "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", + ) ) - return dumps(rotate) def build_singlepress( @@ -246,19 +246,19 @@ def build_singlepress( message_id=0, ): - lumi, source_id = source_sid.split(".") - singlepress = _inflate( - "singlepress", - "[1,13,1,85,[0,1],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, - "click", + return dumps( + _inflate( + "singlepress", + "[1,13,1,85,[0,1],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "click", + ) ) - return dumps(singlepress) def build_doublepress( @@ -270,19 +270,19 @@ def build_doublepress( message_id=0, ): - lumi, source_id = source_sid.split(".") - doublepress = _inflate( - "doublepress", - "[1,13,1,85,[0,2],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, - "double_click", + return dumps( + _inflate( + "doublepress", + "[1,13,1,85,[0,2],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "double_click", + ) ) - return dumps(doublepress) def build_longpress( @@ -294,19 +294,19 @@ def build_longpress( message_id=0, ): - lumi, source_id = source_sid.split(".") - longpress = _inflate( - "longpress", - "[1,13,1,85,[0,16],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, - "long_click_press", + return dumps( + _inflate( + "longpress", + "[1,13,1,85,[0,16],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + "long_click_press", + ) ) - return dumps(longpress) def build_shake( @@ -318,15 +318,15 @@ def build_shake( message_id=0, ): - lumi, source_id = source_sid.split(".") - shake = _inflate( - "shake", - "[1,13,1,85,[0,18],0,0]", - source_sid, - source_model, - target_id, - target_ip, - target_model, - message_id, + return dumps( + _inflate( + "shake", + "[1,13,1,85,[0,18],0,0]", + source_sid, + source_model, + target_id, + target_ip, + target_model, + message_id, + ) ) - return dumps(shake) From d8cae7546e57e073ad7fb3f7f5f5661b3f60a29c Mon Sep 17 00:00:00 2001 From: bskaplou Date: Fri, 29 May 2020 16:05:45 +0300 Subject: [PATCH 08/10] cleanup --- miio/gateway.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/miio/gateway.py b/miio/gateway.py index 0dddd7103..0f86e8a71 100644 --- a/miio/gateway.py +++ b/miio/gateway.py @@ -224,6 +224,7 @@ def x_del(self, script_id): @command(click.argument("sid"), click.argument("command")) def subdevice_command(self, sid, command): + """Send command to subdevice.""" self.discover_devices() target = list(filter(lambda subdevice: subdevice.sid == sid, self.devices)) if len(target) < 1: @@ -233,9 +234,13 @@ def subdevice_command(self, sid, command): else: return getattr(target[0], command)() - def install_script(self, sid, builder): - addresses = ipv4_nonloop_ips() - my_ip = addresses[0] # Taking first public IP ;( + def install_script(self, sid, builder, ip=None): + """Install script for by building script source and sending it with miio method. You need to run fake or real device to capture script execution results.""" + if ip is None: + addresses = ipv4_nonloop_ips() + my_ip = addresses[0] # Taking first public IP + else: + my_ip = ip data_tkn = tokens["data_tkn"] source = builder(sid, my_ip) return self.send( @@ -993,26 +998,32 @@ class Cube(SubDevice): @command() def install_move_script(self): + """Generate and install script which captures move event and sends miio package to device""" return self._gw.install_script(self.sid, build_move) @command() def install_rotate_script(self): + """Generate and install script which captures rotate event and sends miio package to device""" return self._gw.install_script(self.sid, build_rotate) @command() def install_shake_script(self): + """Generate and install script which captures shake in air event and sends miio package to device""" return self._gw.install_script(self.sid, build_shakeair) @command() def install_flip90_script(self): + """Generate and install script which captures horizontal 90 flip and sends miio package to device""" return self._gw.install_script(self.sid, build_flip90) @command() def install_taptap_script(self): + """Generate and install script which captures double tap on surface event and sends miio package to device""" return self._gw.install_script(self.sid, build_taptap) @command() def install_flip180_script(self): + """Generate and install script which captures horizontal 180 flip and sends miio package to device""" return self._gw.install_script(self.sid, build_flip180) @@ -1023,16 +1034,20 @@ class AqaraSquareButton(SubDevice): @command() def install_singlepress_script(self): + """Generate and install script which captures single press event and sends miio package to device""" return self._gw.install_script(self.sid, build_singlepress) @command() def install_doublepress_script(self): + """Generate and install script which captures double press event and sends miio package to device""" return self._gw.install_script(self.sid, build_doublepress) @command() def install_longpress_script(self): + """Generate and install script which captures loooong press event and sends miio package to device""" return self._gw.install_script(self.sid, build_longpress) @command() def install_shake_script(self): + """Generate and install script which captures shake in air event and sends miio package to device""" return self._gw.install_script(self.sid, build_shake) From 360658a0475dd8cf74dd80fb7fd6bc278a59b658 Mon Sep 17 00:00:00 2001 From: bskaplou Date: Sat, 6 Jun 2020 12:56:59 +0300 Subject: [PATCH 09/10] pass tokens as cli args --- miio/fake_device.py | 15 +++++++++-- miio/gateway.py | 57 +++++++++++++++++++++-------------------- miio/gateway_scripts.py | 54 +++++++++++++++++--------------------- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/miio/fake_device.py b/miio/fake_device.py index 28c6113e1..c90183bc7 100644 --- a/miio/fake_device.py +++ b/miio/fake_device.py @@ -5,6 +5,7 @@ import struct from functools import reduce +import sys import ifaddr from miio.protocol import Message @@ -100,11 +101,21 @@ def build_ack(device: int): def callback(source_device, action, params): _LOGGER.debug(f"CALLBACK {source_device}=>{action}({params})") - from gateway_scripts import tokens, fake_device_id + from gateway_scripts import fake_device_id logging.basicConfig(level="DEBUG") device_id = int(fake_device_id) - device_token = bytes.fromhex(tokens["real"]) + # Use real token on fake device for encryption + # encoded is used in scripts = key pair should match + # tokens = { + # "real": "9bc7c7ce6291d3e443fd7708608b9892", + # "encoded": "79cf21b08fb051499389f23c113477a4", + # } + if len(sys.argv) > 1: + device_token = bytes.fromhex(sys.argv[1]) + else: + print("WARNING kae device starting with publically known token! Pass other token next time pls...") + device_token = bytes.fromhex('9bc7c7ce6291d3e443fd7708608b9892') fake_device = FakeDevice(device_id, device_token) fake_device.run(callback) diff --git a/miio/gateway.py b/miio/gateway.py index 0f86e8a71..b5e54064b 100644 --- a/miio/gateway.py +++ b/miio/gateway.py @@ -4,6 +4,7 @@ from datetime import datetime from enum import Enum, IntEnum from typing import Optional +from random import randrange import attr import click @@ -24,7 +25,6 @@ build_shakeair, build_singlepress, build_taptap, - tokens, ) from .utils import brightness_and_color_to_int, int_to_brightness, int_to_rgb @@ -222,8 +222,8 @@ def x_del(self, script_id): """Delete script by id.""" return self.send("miIO.xdel", [script_id]) - @command(click.argument("sid"), click.argument("command")) - def subdevice_command(self, sid, command): + @command(click.argument("sid"), click.argument("command"), click.argument("encoded_token")) + def subdevice_command(self, sid, command, encoded_token): """Send command to subdevice.""" self.discover_devices() target = list(filter(lambda subdevice: subdevice.sid == sid, self.devices)) @@ -232,17 +232,18 @@ def subdevice_command(self, sid, command): elif not hasattr(target[0], command): return f"Device with sid={sid} has no method {command}" else: - return getattr(target[0], command)() + return getattr(target[0], command)(encoded_token) - def install_script(self, sid, builder, ip=None): + def install_script(self, sid, builder, encoded_token, ip=None): """Install script for by building script source and sending it with miio method. You need to run fake or real device to capture script execution results.""" if ip is None: addresses = ipv4_nonloop_ips() my_ip = addresses[0] # Taking first public IP else: my_ip = ip - data_tkn = tokens["data_tkn"] - source = builder(sid, my_ip) + + data_tkn = randrange(5000, 10000) + source = builder(sid, my_ip, encoded_token) return self.send( "send_data_frame", { @@ -792,7 +793,7 @@ def get_firmware_version(self) -> Optional[int]: return self._fw_ver @command() - def uninstall_scripts(self): + def uninstall_scripts(self, encoded_token): return dict( map( lambda action: ( @@ -997,34 +998,34 @@ class Cube(SubDevice): properties = [] @command() - def install_move_script(self): + def install_move_script(self, encoded_token): """Generate and install script which captures move event and sends miio package to device""" - return self._gw.install_script(self.sid, build_move) + return self._gw.install_script(self.sid, build_move, encoded_token) @command() - def install_rotate_script(self): + def install_rotate_script(self, encoded_token): """Generate and install script which captures rotate event and sends miio package to device""" - return self._gw.install_script(self.sid, build_rotate) + return self._gw.install_script(self.sid, build_rotate, encoded_token) @command() - def install_shake_script(self): + def install_shake_script(self, encoded_token): """Generate and install script which captures shake in air event and sends miio package to device""" - return self._gw.install_script(self.sid, build_shakeair) + return self._gw.install_script(self.sid, build_shakeair, encoded_token) @command() - def install_flip90_script(self): + def install_flip90_script(self, encoded_token): """Generate and install script which captures horizontal 90 flip and sends miio package to device""" - return self._gw.install_script(self.sid, build_flip90) + return self._gw.install_script(self.sid, build_flip90, encoded_token) @command() - def install_taptap_script(self): + def install_taptap_script(self, encoded_token): """Generate and install script which captures double tap on surface event and sends miio package to device""" - return self._gw.install_script(self.sid, build_taptap) + return self._gw.install_script(self.sid, build_taptap, encoded_token) @command() - def install_flip180_script(self): + def install_flip180_script(self, encoded_token): """Generate and install script which captures horizontal 180 flip and sends miio package to device""" - return self._gw.install_script(self.sid, build_flip180) + return self._gw.install_script(self.sid, build_flip180, encoded_token) class AqaraSquareButton(SubDevice): @@ -1033,21 +1034,21 @@ class AqaraSquareButton(SubDevice): properties = [] @command() - def install_singlepress_script(self): + def install_singlepress_script(self, encoded_token): """Generate and install script which captures single press event and sends miio package to device""" - return self._gw.install_script(self.sid, build_singlepress) + return self._gw.install_script(self.sid, build_singlepress, encoded_token) @command() - def install_doublepress_script(self): + def install_doublepress_script(self, encoded_token): """Generate and install script which captures double press event and sends miio package to device""" - return self._gw.install_script(self.sid, build_doublepress) + return self._gw.install_script(self.sid, build_doublepress, encoded_token) @command() - def install_longpress_script(self): + def install_longpress_script(self, encoded_token): """Generate and install script which captures loooong press event and sends miio package to device""" - return self._gw.install_script(self.sid, build_longpress) + return self._gw.install_script(self.sid, build_longpress, encoded_token) @command() - def install_shake_script(self): + def install_shake_script(self, encoded_token): """Generate and install script which captures shake in air event and sends miio package to device""" - return self._gw.install_script(self.sid, build_shake) + return self._gw.install_script(self.sid, build_shake, encoded_token) diff --git a/miio/gateway_scripts.py b/miio/gateway_scripts.py index 1f34dea96..ffb1ec58c 100644 --- a/miio/gateway_scripts.py +++ b/miio/gateway_scripts.py @@ -8,14 +8,6 @@ def dumps(data): return dumps_orig(data, separators=separators) -# token in script doesn't match token of device which is used for (enc/dec)ryption -# but they are linked somehow -tokens = { - "real": "9bc7c7ce6291d3e443fd7708608b9892", - "encoded": "79cf21b08fb051499389f23c113477a4", - "data_tkn": 29576, -} - fake_device_id = "120009025" fake_device_model = "chuangmi.plug.v3" @@ -51,7 +43,7 @@ def _inflate( target_id, target_ip, target_model, - message_id, + target_encoded_token, event=None, command_extra="", ): @@ -83,10 +75,10 @@ def _inflate( "command": target_model + "." + action + "_" + source_id, "did": target_id, "extra": command_extra, - "id": message_id, + "id": randint(0, 999), "ip": target_ip, "model": target_model, - "token": tokens["encoded"], + "token": target_encoded_token, "value": "", } ], @@ -98,10 +90,10 @@ def _inflate( def build_move( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_cube.v1", - message_id=0, ): return dumps( @@ -113,7 +105,7 @@ def build_move( target_id, target_ip, target_model, - message_id, + target_encoded_token, ) ) @@ -121,10 +113,10 @@ def build_move( def build_flip90( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_cube.v1", - message_id=0, ): return dumps( @@ -136,7 +128,7 @@ def build_flip90( target_id, target_ip, target_model, - message_id, + target_encoded_token, ) ) @@ -144,10 +136,10 @@ def build_flip90( def build_flip180( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_cube.v1", - message_id=0, ): return dumps( @@ -159,7 +151,7 @@ def build_flip180( target_id, target_ip, target_model, - message_id, + target_encoded_token, ) ) @@ -167,10 +159,10 @@ def build_flip180( def build_taptap( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_cube.v1", - message_id=0, ): return dumps( @@ -182,7 +174,7 @@ def build_taptap( target_id, target_ip, target_model, - message_id, + target_encoded_token, "tap_twice", ) ) @@ -191,10 +183,10 @@ def build_taptap( def build_shakeair( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_cube.v1", - message_id=0, ): return dumps( @@ -206,7 +198,7 @@ def build_shakeair( target_id, target_ip, target_model, - message_id, + target_encoded_token, "shake_air", ) ) @@ -215,10 +207,10 @@ def build_shakeair( def build_rotate( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_cube.v1", - message_id=0, ): return dumps( @@ -230,7 +222,7 @@ def build_rotate( target_id, target_ip, target_model, - message_id, + target_encoded_token, "rotate", "[1,19,7,1006,[42,[6066005667474548,12,3,85,0]],0,0]", ) @@ -240,10 +232,10 @@ def build_rotate( def build_singlepress( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_switch.aq3", - message_id=0, ): return dumps( @@ -255,7 +247,7 @@ def build_singlepress( target_id, target_ip, target_model, - message_id, + target_encoded_token, "click", ) ) @@ -264,10 +256,10 @@ def build_singlepress( def build_doublepress( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_switch.aq3", - message_id=0, ): return dumps( @@ -279,7 +271,7 @@ def build_doublepress( target_id, target_ip, target_model, - message_id, + target_encoded_token, "double_click", ) ) @@ -288,10 +280,10 @@ def build_doublepress( def build_longpress( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_switch.aq3", - message_id=0, ): return dumps( @@ -303,7 +295,7 @@ def build_longpress( target_id, target_ip, target_model, - message_id, + target_encoded_token, "long_click_press", ) ) @@ -312,10 +304,10 @@ def build_longpress( def build_shake( source_sid, target_ip, + target_encoded_token, target_model=fake_device_model, target_id=fake_device_id, source_model="lumi.sensor_switch.aq3", - message_id=0, ): return dumps( @@ -327,6 +319,6 @@ def build_shake( target_id, target_ip, target_model, - message_id, + target_encoded_token, ) ) From 1862910c77d99dbff8a2c0290997b7b27d8a66c9 Mon Sep 17 00:00:00 2001 From: bskaplou Date: Sat, 6 Jun 2020 13:07:22 +0300 Subject: [PATCH 10/10] styling --- miio/fake_device.py | 8 +++++--- miio/gateway.py | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/miio/fake_device.py b/miio/fake_device.py index c90183bc7..811215a70 100644 --- a/miio/fake_device.py +++ b/miio/fake_device.py @@ -107,7 +107,7 @@ def callback(source_device, action, params): device_id = int(fake_device_id) # Use real token on fake device for encryption - # encoded is used in scripts = key pair should match + # encoded is used in scripts = key pair should match # tokens = { # "real": "9bc7c7ce6291d3e443fd7708608b9892", # "encoded": "79cf21b08fb051499389f23c113477a4", @@ -115,7 +115,9 @@ def callback(source_device, action, params): if len(sys.argv) > 1: device_token = bytes.fromhex(sys.argv[1]) else: - print("WARNING kae device starting with publically known token! Pass other token next time pls...") - device_token = bytes.fromhex('9bc7c7ce6291d3e443fd7708608b9892') + print( + "WARNING kae device starting with publically known token! Pass other token next time pls..." + ) + device_token = bytes.fromhex("9bc7c7ce6291d3e443fd7708608b9892") fake_device = FakeDevice(device_id, device_token) fake_device.run(callback) diff --git a/miio/gateway.py b/miio/gateway.py index b5e54064b..85aa51f55 100644 --- a/miio/gateway.py +++ b/miio/gateway.py @@ -222,7 +222,11 @@ def x_del(self, script_id): """Delete script by id.""" return self.send("miIO.xdel", [script_id]) - @command(click.argument("sid"), click.argument("command"), click.argument("encoded_token")) + @command( + click.argument("sid"), + click.argument("command"), + click.argument("encoded_token"), + ) def subdevice_command(self, sid, command, encoded_token): """Send command to subdevice.""" self.discover_devices()