From d90fa1e22ba0cc47a1017b30d64c62c47233a50a Mon Sep 17 00:00:00 2001 From: Mikhail Rumyantsev Date: Mon, 3 Jul 2023 23:28:14 +0300 Subject: [PATCH 01/15] ZXiaomiBrightenssConv have to return integer brightness value. --- custom_components/xiaomi_gateway3/core/converters/zigbee.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/xiaomi_gateway3/core/converters/zigbee.py b/custom_components/xiaomi_gateway3/core/converters/zigbee.py index f8a89869..486631f9 100644 --- a/custom_components/xiaomi_gateway3/core/converters/zigbee.py +++ b/custom_components/xiaomi_gateway3/core/converters/zigbee.py @@ -462,7 +462,7 @@ class ZXiaomiBrightnessConv(Converter): """ def decode(self, device: "XDevice", payload: dict, value: Any): - payload[self.attr] = value / 100.0 * 255.0 + payload[self.attr] = max (0, min(255, round(value / 100.0 * 255.0))) def encode(self, device: "XDevice", payload: dict, value: Any): # brightness and transition in seconds From 07c5ae345247bb2d37ab376254ed90355eac01d4 Mon Sep 17 00:00:00 2001 From: SakuraKooi Date: Fri, 7 Jul 2023 17:07:39 +0000 Subject: [PATCH 02/15] Add support for opple.light.barelp --- .../xiaomi_gateway3/core/converters/devices.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index 174269d7..2e689502 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -2237,6 +2237,18 @@ # Converter("fill_light_switch", "switch", mi="3.p.21"), # MathConv("min_bri_factory", "number", mi="3.p.16", min=1, max=500), ] +}, { + # https://home.miot-spec.com/spec/opple.light.barelp + 3661: ["Opple", "Bare Light Panel", "opple.light.barelp"], + "spec": [ + Converter("light", "light", mi="2.p.1"), + BrightnessConv("brightness", mi="2.p.2", parent="light", max=100), + ColorTempKelvin("color_temp", mi="2.p.3", parent="light", mink=3000, maxk=5700), + MapConv("mode", "select", mi="2.p.4", map={ + 0: "Reception", 1: "Entertainment", 2: "Cinema", 3: "Night", 4: "Wakeup", 5: "Sleep", + 6: "Sunset", 7: "None", 8: "Invert" + }), + ], }, { "default": "mesh", # default Mesh device "spec": [ From 487ebcf110132240be04c7aa1b902eb66d368072 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 14:56:08 +0300 Subject: [PATCH 03/15] Add new ZDOCmd processing --- custom_components/xiaomi_gateway3/core/converters/silabs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/custom_components/xiaomi_gateway3/core/converters/silabs.py b/custom_components/xiaomi_gateway3/core/converters/silabs.py index 8d9066da..c4f86fb9 100644 --- a/custom_components/xiaomi_gateway3/core/converters/silabs.py +++ b/custom_components/xiaomi_gateway3/core/converters/silabs.py @@ -112,6 +112,11 @@ def decode(data: dict): "ieee": args[1], "nwk": args[2], } + elif hdr.command_id == ZDOCmd.Mgmt_Lqi_req: + # https://docs.silabs.com/zigbee/6.5/af_v2/group-zdo + return {"command": cmd, "start_index": args[0]} + elif hdr.command_id == ZDOCmd.Mgmt_Lqi_rsp: + return {"command": cmd} else: raise NotImplemented From 52ffc673c264f3d537ac1a18d188c81364beca88 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 15:01:56 +0300 Subject: [PATCH 04/15] Add support PTX Mesh Single Wall Switch and Double Wall Switch #1078 --- custom_components/xiaomi_gateway3/core/converters/devices.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index 174269d7..a4d77d2a 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -1920,6 +1920,7 @@ ], }, { 11332: ["PTX", "Mesh Double Wall Switch", "090615.switch.aksk2"], + 12471: ["PTX", "Mesh Double Wall Switch (no N)", "090615.switch.aksk2"], "spec": [ Converter("channel_1", "switch", mi="2.p.1"), Converter("channel_2", "switch", mi="3.p.1"), From a57748a8c640766660a3e36dbcfb957d99e8ee36 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 15:25:07 +0300 Subject: [PATCH 05/15] Add support linp.switch.q3s3 #1083 --- .../core/converters/devices.py | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index 6a3c6133..9a728ff7 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -1298,7 +1298,7 @@ EventConv("motion", mi="2.e.1008", value=True), Converter("battery", mi="3.p.1003"), ], -},{ +}, { # https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:remote-control:0000A021:huca-wx8:1 12382: ["H+", "Wireless Button", "huca-wx8"], "spec": [ @@ -2167,6 +2167,46 @@ Converter("compatible_mode", "switch", mi="7.p.4"), ], +}, { + # https://home.miot-spec.com/s/5045 + 2428: ["Linptech", "Lingpu Single Wall Switch", "linp.switch.q3s1"], + "spec": [ + Converter("channel_1", "switch", mi="2.p.1"), + Converter("action", "sensor", enabled=False), + + ButtonMIConv("button_1", mi="7.e.1", value=1), + + Converter("led", "switch", mi="5.p.1"), + ], +}, { + # https://home.miot-spec.com/s/5045 + 2429: ["Linptech", "Lingpu Double Wall Switch", "linp.switch.q3s2"], + "spec": [ + Converter("channel_1", "switch", mi="2.p.1"), + Converter("channel_2", "switch", mi="3.p.1"), + + Converter("action", "sensor", enabled=False), + + ButtonMIConv("button_1", mi="7.e.1", value=1), + ButtonMIConv("button_2", mi="7.e.2", value=1), + + Converter("led", "switch", mi="5.p.1"), + ], +}, { + 2274: ["Linptech", "Lingpu Triple Wall Switch", "linp.switch.q3s3"], + "spec": [ + Converter("channel_1", "switch", mi="2.p.1"), + Converter("channel_2", "switch", mi="3.p.1"), + Converter("channel_3", "switch", mi="4.p.1"), + + Converter("action", "sensor", enabled=False), + + ButtonMIConv("button_1", mi="7.e.1", value=1), + ButtonMIConv("button_2", mi="7.e.2", value=1), + ButtonMIConv("button_3", mi="7.e.3", value=1), + + Converter("led", "switch", mi="5.p.1"), + ], }, { # https://home.miot-spec.com/spec/chuangmi.switch.mesh 1350: ["Chuangmi", "Single Wall Switch K1-A (with N)", "chuangmi.switch.mesh"], From 07e25fc3daae1815538c3911c309c50e1a339438 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 15:26:58 +0300 Subject: [PATCH 06/15] Add support Linptech Intelligent Sliding Window Driver WD1 #1096 --- .../xiaomi_gateway3/core/converters/devices.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index 9a728ff7..8e8b130d 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -2232,6 +2232,16 @@ Action, ButtonMIConv("button", mi="3.e.1", value=1), ] +}, { + 10939: ["Linptech", "Sliding Window Driver WD1", "WD1"], + "spec": [ + MapConv("motor", "cover", mi="2.p.1", map={ + 0: "stop", 1: "open", 2: "close" + }), + Converter("target_position", mi="2.p.3"), + CurtainPosConv("position", mi="2.p.2", parent="motor"), + Converter("battery", "sensor", mi="3.p.1"), + ], }, { # https://home.miot-spec.com/spec/yeelink.curtain.crc1 10813: ["Yeelink", "Curtain Motor C1", "YCCBCI008"], From da91f7daaf3551ecb1a611d77a5f51136da63f6f Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 15:35:52 +0300 Subject: [PATCH 07/15] Add support Xiaomi Curtain Motor (MJZNCL02LM) #1094 --- .../xiaomi_gateway3/core/converters/devices.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index 8e8b130d..1712758f 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -2254,6 +2254,23 @@ 0: "default", 1: "doublmode", 2: "leftmode", 3: "rightmode" }, enabled=False), ] +}, { + 4722: ["Xiaomi", "Curtain Motor", "MJZNCL02LM"], + "spec": [ + MapConv("motor", "cover", mi="2.p.1", map={ + 0: "stop", 1: "open", 2: "close" + }), + Converter("target_position", mi="2.p.2"), + CurtainPosConv("position", mi="2.p.6", parent="motor"), + MapConv("run_state", mi="2.p.3", parent="motor", map={ + 0: "stop", 1: "opening", 2: "closing", 3: "busy" + }), + Converter("battery", "sensor", mi="5.p.1"), # percent + Converter("motor_reverse", "switch", mi="2.p.5", enabled=False), + MapConv("battery_charging", "binary_sensor", mi="5.p.2", map={ + 1: True, 2: False, 3: False, + }, enabled=False), + ], }, { # https://home.miot-spec.com/spec/giot.light.v5ssm 11724: ["GranwinIoT", "Mesh Light V5", "giot.light.v5ssm"], From d9071cf761ec539f32e1e020b7bb8aa708c4a94b Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 15:47:22 +0300 Subject: [PATCH 08/15] Fix mesh mosquito led switch #1088 --- custom_components/xiaomi_gateway3/core/converters/const.py | 1 + custom_components/xiaomi_gateway3/core/converters/devices.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_gateway3/core/converters/const.py b/custom_components/xiaomi_gateway3/core/converters/const.py index 14f90608..d77fca7b 100644 --- a/custom_components/xiaomi_gateway3/core/converters/const.py +++ b/custom_components/xiaomi_gateway3/core/converters/const.py @@ -69,3 +69,4 @@ BATTERY_LOW = {1: False, 2: True} SWITCH_MODE = {1: "250 ms", 2: "500 ms", 3: "750 ms", 4: "1 sec"} INVERSE = {0: True, 1: False} +INVERSE_BOOL = {False: True, True: False} diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index 1712758f..b0df31b9 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -1862,7 +1862,7 @@ Converter("switch", "switch", mi="2.p.1"), # bool Converter("battery", "sensor", mi="3.p.1"), # percentage 0-100 Converter("supply", "sensor", mi="4.p.1"), # percentage 0-100 - Converter("led", "switch", mi="9.p.1", enabled=False), # bool + MapConv("led", "switch", mi="9.p.1", map=INVERSE_BOOL, enabled=False), # bool MapConv("power_mode", "select", mi="2.p.2", map={ 0: "auto", 1: "battery", 2: "usb" }, enabled=False) From 74c58e230a224e7b30b6b55ca25fe6f70a21922c Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 17:44:59 +0300 Subject: [PATCH 09/15] Fix error on battery info for LYWSD02MMC #1095 --- custom_components/xiaomi_gateway3/core/device.py | 4 +++- tests/test_conv_ble.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_gateway3/core/device.py b/custom_components/xiaomi_gateway3/core/device.py index b9e5ac14..ec7f13c1 100644 --- a/custom_components/xiaomi_gateway3/core/device.py +++ b/custom_components/xiaomi_gateway3/core/device.py @@ -428,9 +428,11 @@ def update(self, value: dict): if self.lazy_setup: for attr in self.lazy_setup & attrs: - self.lazy_setup.remove(attr) conv = next(c for c in self.converters if c.attr == attr) gateway = self.gateways[0] + if conv.domain not in gateway.setups: + return + self.lazy_setup.remove(attr) gateway.setups[conv.domain](gateway, self, conv) for entity in self.entities.values(): diff --git a/tests/test_conv_ble.py b/tests/test_conv_ble.py index 26a6813f..8723c0c9 100644 --- a/tests/test_conv_ble.py +++ b/tests/test_conv_ble.py @@ -1,6 +1,7 @@ from homeassistant.components.sensor import DOMAIN from custom_components.xiaomi_gateway3.core.device import XDevice, BLE +from custom_components.xiaomi_gateway3.core.gateway.base import GatewayBase assert DOMAIN # fix circular import @@ -296,3 +297,18 @@ def test_10249(): ] ) assert p == {"action": "doorbell", "timestamp": 1681029598} + + +def test_lazy_setup(): + device = XDevice(BLE, 9538, DID, MAC) + assert device.info.name == "Xiaomi TH Clock Pro" + device.setup_converters() + + gw = GatewayBase() + gw.options = {} + gw.setups = {} + gw.add_device(device.did, device) + + # https://github.com/AlexxIT/XiaomiGateway3/issues/1095 + payload = device.decode("mibeacon", {"eid": 18435, "edata": "64"}) + device.update(payload) From d593f05cae4b92fc794a046db7afb3a91bbc2d57 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 17:48:24 +0300 Subject: [PATCH 10/15] Fix FTP for Xiaomi Multimode Gateway #1099 --- custom_components/xiaomi_gateway3/core/shell/base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_gateway3/core/shell/base.py b/custom_components/xiaomi_gateway3/core/shell/base.py index 8adef001..68a73433 100644 --- a/custom_components/xiaomi_gateway3/core/shell/base.py +++ b/custom_components/xiaomi_gateway3/core/shell/base.py @@ -89,8 +89,10 @@ async def check_bin(self, filename: str, md5: str, url: str) -> int: if md5 in await self.exec(cmd): return OK - # if there is an old version of the file - await self.exec("killall " + filename) + # better not to kill busybox :) + if filename != "busybox": + # if there is an old version of the file + await self.exec("killall " + filename) raw = await download(url) await self.write_file(filepath, raw) From dd9b0a59ce7f1a51ab914df141fcddc1f38666a6 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 18:12:47 +0300 Subject: [PATCH 11/15] Update support for PTX Mesh Multifunction Wireless Switch #1084 --- .../xiaomi_gateway3/core/converters/devices.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index b0df31b9..12d2078b 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -1533,8 +1533,10 @@ 10371: ["PTX", "Mesh Multifunction Wireless Switch", "090615.remote.mlsw0a"], "spec": [ MiBeacon, BLEAction, Button, # don't know is it BLE or Mesh - EventConv("action", mi="2.e.1", value="click"), - EventConv("action", mi="3.e.2", value="hold"), + EventConv("action", mi="2.e.1", value="button_1_single"), + EventConv("action", mi="2.e.2", value="button_2_single"), + EventConv("action", mi="3.e.2", value="button_1_hold"), + EventConv("action", mi="3.e.3", value="button_2_hold"), ] }, { # https://home.miot-spec.com/spec/090615.remote.btsw1 From 22781d2626d6311fcf2dce02eb45cb8c9a632b94 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 22:38:11 +0300 Subject: [PATCH 12/15] Fix available attribute for stat sensors #1087 --- custom_components/xiaomi_gateway3/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/xiaomi_gateway3/sensor.py b/custom_components/xiaomi_gateway3/sensor.py index 349dc370..b03abeac 100644 --- a/custom_components/xiaomi_gateway3/sensor.py +++ b/custom_components/xiaomi_gateway3/sensor.py @@ -133,7 +133,7 @@ def available(self): @callback def async_update_available(self): super().async_update_available() - self._attr_extra_state_attributes["available"] = self.available + self._attr_extra_state_attributes["available"] = self._attr_available async def async_added_to_hass(self): await super().async_added_to_hass() From 108bc8d40310e525616968b4d1cb2f6434a8786b Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Sun, 16 Jul 2023 22:39:30 +0300 Subject: [PATCH 13/15] Update version to 3.3.2 --- custom_components/xiaomi_gateway3/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/xiaomi_gateway3/manifest.json b/custom_components/xiaomi_gateway3/manifest.json index aa444b43..0c3128c5 100644 --- a/custom_components/xiaomi_gateway3/manifest.json +++ b/custom_components/xiaomi_gateway3/manifest.json @@ -13,6 +13,6 @@ "requirements": [ "zigpy>=0.44.1" ], - "version": "3.3.1", + "version": "3.3.2", "iot_class": "local_push" } From 6ab99a540c13be5ce245bfa4811bac2ac6e791a2 Mon Sep 17 00:00:00 2001 From: flyibeat <78073839+flyibeat@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:05:21 +0800 Subject: [PATCH 14/15] =?UTF-8?q?fix=20sensor=20proportion=20error=20for?= =?UTF-8?q?=20XMZNCXB01QM=EF=BC=88Mesh=20Power=20Strip=202=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiaomi_gateway3/core/converters/devices.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/xiaomi_gateway3/core/converters/devices.py b/custom_components/xiaomi_gateway3/core/converters/devices.py index 12d2078b..1592b9d2 100644 --- a/custom_components/xiaomi_gateway3/core/converters/devices.py +++ b/custom_components/xiaomi_gateway3/core/converters/devices.py @@ -1897,10 +1897,10 @@ Converter("mode", "switch", mi="2.p.2"), # int8 MathConv("chip_temperature", "sensor", mi="2.p.3", round=2, enabled=False), # float - MathConv("energy", "sensor", mi="3.p.1", multiply=0.001, round=2), + MathConv("energy", "sensor", mi="3.p.1", multiply=0.1, round=2), MathConv("power", "sensor", mi="3.p.2", round=2), # float - MathConv("voltage", "sensor", mi="3.p.3"), # float - MathConv("current", "sensor", mi="3.p.4"), # float + MathConv("voltage", "sensor", mi="3.p.3", multiply=0.001, round=2), # float + MathConv("current", "sensor", mi="3.p.4", multiply=0.001, round=2), # float ] }, { 3129: ["Xiaomi", "Smart Curtain Motor", "MJSGCLBL01LM"], From 2f6956fc380ecbaa50423c9af0fef7a15cd9f434 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Fri, 21 Jul 2023 21:43:00 +0300 Subject: [PATCH 15/15] Fix flash EZSP firmware for chip without firmware (broken state) --- custom_components/xiaomi_gateway3/core/ezsp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_gateway3/core/ezsp.py b/custom_components/xiaomi_gateway3/core/ezsp.py index 09563c61..5d685406 100644 --- a/custom_components/xiaomi_gateway3/core/ezsp.py +++ b/custom_components/xiaomi_gateway3/core/ezsp.py @@ -104,7 +104,8 @@ async def read_firmware(host: str) -> Optional[str]: # noinspection PyProtectedMember await asyncio.wait_for(ezsp._probe(), timeout=10) _, _, version = await ezsp.get_board_info() - except asyncio.TimeoutError: + except Exception as e: + _LOGGER.debug(f"{host} [FWUP] Read firmware error: {e}") return None finally: ezsp.close()