From 711c04233fc33fd27aee40b17b91ddbcb0b89d04 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 27 Sep 2020 12:32:30 +0300 Subject: [PATCH] Support adding custom devices! --- custom_components/xiaomi_gateway3/gateway3.py | 30 +++++++++++++++++++ custom_components/xiaomi_gateway3/remote.py | 16 ++++++++++ 2 files changed, 46 insertions(+) diff --git a/custom_components/xiaomi_gateway3/gateway3.py b/custom_components/xiaomi_gateway3/gateway3.py index 24a45acb..6554bdd4 100644 --- a/custom_components/xiaomi_gateway3/gateway3.py +++ b/custom_components/xiaomi_gateway3/gateway3.py @@ -16,8 +16,13 @@ _LOGGER = logging.getLogger(__name__) +RE_NWK_KEY = re.compile(r'lumi send-nwk-key (0x.+?) {(.+?)}') + class Gateway3(Thread): + pair_model = None + pair_payload = None + def __init__(self, host: str, token: str, config: dict): super().__init__(daemon=True) @@ -332,6 +337,8 @@ def on_message(self, client: Client, userdata, msg: MQTTMessage): if msg.topic == 'zigbee/send': payload = json.loads(msg.payload) self.process_message(payload) + elif self.pair_model and msg.topic.endswith('/commands'): + self.process_pair(msg.payload) def setup_devices(self, devices: list): """Add devices to hass.""" @@ -430,6 +437,29 @@ def process_message(self, data: dict): device['init'] = payload self.setup_devices([device]) + def process_pair(self, raw: bytes): + # get shortID and eui64 of paired device + if b'lumi send-nwk-key' in raw: + # create model response + payload = f"0x18010105000042{len(self.pair_model):02x}" \ + f"{self.pair_model.encode().hex()}" + m = RE_NWK_KEY.search(raw.decode()) + self.pair_payload = json.dumps({ + 'sourceAddress': m[1], + 'eui64': '0x' + m[2], + 'profileId': '0x0104', + 'clusterId': '0x0000', + 'sourceEndpoint': '0x01', + 'destinationEndpoint': '0x01', + 'APSCounter': '0x01', + 'APSPlayload': payload + }, separators=(',', ':')) + + # send model response "from device" + elif b'zdo active ' in raw: + mac = self.device['mac'][2:].upper() + self.mqtt.publish(f"gw/{mac}/MessageReceived", self.pair_payload) + def process_ble_event(self, raw: Union[bytes, str]): data = json.loads(raw[10:])['params'] \ if isinstance(raw, bytes) else json.loads(raw) diff --git a/custom_components/xiaomi_gateway3/remote.py b/custom_components/xiaomi_gateway3/remote.py index 24f8a2f3..e84fd2e8 100644 --- a/custom_components/xiaomi_gateway3/remote.py +++ b/custom_components/xiaomi_gateway3/remote.py @@ -1,5 +1,6 @@ import logging +from homeassistant.components import persistent_notification from homeassistant.components.remote import ATTR_DEVICE from homeassistant.helpers.entity import ToggleEntity @@ -32,10 +33,20 @@ def update(self, data: dict = None): if 'pairing_start' in data: self._state = True self.schedule_update_ha_state() + elif 'pairing_stop' in data: self._state = False self.schedule_update_ha_state() + self.gw.pair_model = None + + elif 'added_device' in data: + text = "New device:\n" + '\n'.join( + f"{k}: {v}" for k, v in data['added_device'].items() + ) + persistent_notification.async_create(self.hass, text, + "Xiaomi Gateway 3") + def turn_on(self): self.gw.send(self.device, {'pairing_start': 60}) @@ -48,3 +59,8 @@ async def async_send_command(self, command, **kwargs): if cmd == 'ble': raw = kwargs[ATTR_DEVICE].replace('\'', '"') self.gw.process_ble_event(raw) + elif cmd == 'pair': + model: str = kwargs[ATTR_DEVICE] + self.gw.pair_model = (model[:-3] if model.endswith('.v1') + else model) + self.turn_on()