From f11b2f2f6828a4aa44f3487b68612cfc8c38491a Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 1 Aug 2025 14:54:33 +0800 Subject: [PATCH 01/35] fix: hide sensitive info in printing logs (#1328) --- custom_components/xiaomi_home/miot/miot_client.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_home/miot/miot_client.py b/custom_components/xiaomi_home/miot/miot_client.py index c48d058..f8f3141 100644 --- a/custom_components/xiaomi_home/miot/miot_client.py +++ b/custom_components/xiaomi_home/miot/miot_client.py @@ -253,7 +253,18 @@ class MIoTClient: if not self._user_config: # Integration need to be add again raise MIoTClientError('load_user_config_async error') - _LOGGER.debug('user config, %s', json.dumps(self._user_config)) + # Hide sensitive info in printing + p_user_config: dict = deepcopy(self._user_config) + p_access_token: str = p_user_config['auth_info']['access_token'] + p_refresh_token: str = p_user_config['auth_info']['refresh_token'] + p_mac_key: str = p_user_config['auth_info']['mac_key'] + p_user_config['auth_info'][ + 'access_token'] = f"{p_access_token[:5]}***{p_access_token[-5:]}" + p_user_config['auth_info'][ + 'refresh_token'] = f"{p_refresh_token[:5]}***{p_refresh_token[-5:]}" + p_user_config['auth_info'][ + 'mac_key'] = f"{p_mac_key[:5]}***{p_mac_key[-5:]}" + _LOGGER.debug('user config, %s', json.dumps(p_user_config)) # MIoT i18n client self._i18n = MIoTI18n( lang=self._entry_data.get( From 3f2c2a648bb58593e2e363e7a25a729ba3a0a743 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 1 Aug 2025 14:56:42 +0800 Subject: [PATCH 02/35] Fix specs (#1329) * fix: cuco.plug.cp2d electric current (#1279) * fix: xiaomi.fan.p45 fan level (#1291) * docs: add necessary notices * fix: xiaomi.aircondition.c17 humidity-range unit (#1308) * fix: xiaomi.airc.h40h00 humidity-range unit * fix: sanmei.valve.s1 power consumption, current and voltage (#1327) * fix: xiaomi.aircondition.m16 humidity-range unit --- .github/ISSUE_TEMPLATE/bug_report.yaml | 4 +-- .../xiaomi_home/miot/specs/spec_modify.yaml | 30 +++++++++++++++++-- doc/README_zh.md | 2 +- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 1fca160..4be50f8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -5,8 +5,8 @@ body: attributes: label: Describe the Bug / 描述问题 description: | - > A clear and concise description of what the bug is. - > 清晰且简明地描述问题。 + > A clear and concise description of what the bug is. Please include the device model information (Like xiaomi.gateway.hub1 which can be found in Device info page). + > 清晰且简明地描述问题。请注明设备 model 信息(例如 xiaomi.gateway.hub1,可在设备详情页查询)。 validations: required: true diff --git a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml index 16fbb56..183879a 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml @@ -1,6 +1,10 @@ urn:miot-spec-v2:device:air-condition-outlet:0000A045:lumi-mcn04:1: prop.3.4: format: uint8 +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:1: + prop.10.6: + unit: none +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:1 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1: prop.10.6: unit: none @@ -15,6 +19,15 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1: prop.10.6: unit: none urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1 +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h40h00:1: + prop.10.6: + unit: none +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1: + prop.10.6: + unit: none +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1 +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1 +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:1: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 @@ -86,6 +99,13 @@ urn:miot-spec-v2:device:bath-heater:0000A028:xiaomi-s1:1: urn:miot-spec-v2:device:curtain:0000A00C:bjkcz-kczble:1:0000D031: prop.2.2: name: status-a +urn:miot-spec-v2:device:electronic-valve:0000A0A7:sanmei-s1:1: + prop.3.1: + expr: round(src_value/100, 2) + prop.3.2: + expr: round(src_value/100, 2) + prop.3.3: + expr: round(src_value/10, 1) urn:miot-spec-v2:device:fan:0000A005:dmaker-p33:1: prop.2.2: name: fan-level-a @@ -101,6 +121,9 @@ urn:miot-spec-v2:device:fan:0000A005:dmaker-p5:1: urn:miot-spec-v2:device:fan:0000A005:xiaomi-p43:1: prop.2.2: name: fan-level-a +urn:miot-spec-v2:device:fan:0000A005:xiaomi-p45:1:0000D062: + prop.2.4: + name: fan-level-a urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1: prop.2.2: name: fan-level-a @@ -172,7 +195,7 @@ urn:miot-spec-v2:device:magnet-sensor:0000A016:linp-m1:1: description: open - value: 1 description: closed - expr: src_value!=1 + expr: (src_value!=1) urn:miot-spec-v2:device:motion-sensor:0000A014:lumi-acn001:1: prop.3.2: access: @@ -213,6 +236,9 @@ urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2: unit: mA prop.3.2: expr: round(src_value/10, 1) +urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2d:1: + prop.3.2: + expr: round(src_value/1000, 2) urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1: prop.11.1: name: power-consumption @@ -257,7 +283,7 @@ urn:miot-spec-v2:device:router:0000A036:xiaomi-rd08:1: urn:miot-spec-v2:device:safe-box:0000A042:loock-v1:1: prop.5.1: name: contact-state - expr: src_value!=1 + expr: (src_value!=1) urn:miot-spec-v2:device:switch:0000A003:090615-x1tpm:1:0000D042: prop.27.3: name: light-on diff --git a/doc/README_zh.md b/doc/README_zh.md index 2895984..cb76272 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -93,7 +93,7 @@ git checkout v1.0.0 - 米家集成是否支持本地化控制? - 米家集成支持通过[小米中枢网关](https://www.mi.com/shop/buy/detail?product_id=15755&cfrom=search)(固件版本 3.4.0_000 以上)或内置中枢网关(软件版本 0.8.0 以上)的米家设备实现本地化控制。如果没有小米中枢网关或其他带中枢网关功能的设备,那么所有控制指令都会通过小米云发送。支持 Home Assistant 本地化控制的小米中枢网关(含内置中枢网关)的固件尚未发布,固件升级计划请参阅 MIoT 团队的通知。 + 米家集成支持通过[小米中枢网关](https://www.mi.com/shop/buy/detail?product_id=15755&cfrom=search)(固件版本 3.3.0_0023 及以上)或内置中枢网关(软件版本 0.8.9 及以上)的米家设备实现本地化控制。如果没有小米中枢网关或其他带中枢网关功能的设备,那么所有控制指令都会通过小米云发送。支持 Home Assistant 本地化控制的小米中枢网关(含内置中枢网关)的固件尚未发布,固件升级计划请参阅 MIoT 团队的通知。 小米中枢网关仅在中国大陆可用,在其他地区不可用。 From 7c97b85f02c96ca72ac763538c3d6f7f3a3615ca Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Mon, 4 Aug 2025 09:44:12 +0800 Subject: [PATCH 03/35] docs: update changelog and version to v0.4.1 (#1336) --- CHANGELOG.md | 10 ++++++++++ custom_components/xiaomi_home/manifest.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9af7631..75c7caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,14 @@ # CHANGELOG +## v0.4.1 +### Changed +- The setting option "Cover closed position" in CONFIGURE is changed to "Cover dead zone width". [#1301](https://github.com/XiaoMi/ha_xiaomi_home/pull/1301) +- Add an alongside switch entity for 090615.aircondition.ktf and juhl.aircondition.hvac. [#1303](https://github.com/XiaoMi/ha_xiaomi_home/pull/1303) +### Fixed +- Fix the vacuum status so that the vacuum activity will not always be idle. [#1299](https://github.com/XiaoMi/ha_xiaomi_home/pull/1299) +- Set the device on when the switch status is False or None. [#1303](https://github.com/XiaoMi/ha_xiaomi_home/pull/1303) +- Hide sensitive info in printing logs. [#1328](https://github.com/XiaoMi/ha_xiaomi_home/pull/1328) +- Fix the MIoT-Spec-V2 of cuco.plug.cp2d electric current, xiaomi.fan.p45 fan level, sanmei.valve.s1 power consumption, current and voltage, xiaomi.aircondition.c17, xiaomi.aircondition.m16 and xiaomi.airc.h40h00 humidity-range unit. [#1329](https://github.com/XiaoMi/ha_xiaomi_home/pull/1329) + ## v0.4.0 ### Added - Add the watch as the device tracker entity. [#1189](https://github.com/XiaoMi/ha_xiaomi_home/pull/1189) diff --git a/custom_components/xiaomi_home/manifest.json b/custom_components/xiaomi_home/manifest.json index a313a36..e444e30 100644 --- a/custom_components/xiaomi_home/manifest.json +++ b/custom_components/xiaomi_home/manifest.json @@ -25,7 +25,7 @@ "cryptography", "psutil" ], - "version": "v0.4.0", + "version": "v0.4.1", "zeroconf": [ "_miot-central._tcp.local." ] From 58c671483e32bfcdfac4c3508b615168596484f7 Mon Sep 17 00:00:00 2001 From: Brandon Chen Date: Tue, 19 Aug 2025 10:15:01 +0800 Subject: [PATCH 04/35] fix: add RETURN_HOME functionality for new vacuum cleaner model (#1344) --- .../xiaomi_home/miot/specs/specv2entity.py | 5 +++++ custom_components/xiaomi_home/vacuum.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/custom_components/xiaomi_home/miot/specs/specv2entity.py b/custom_components/xiaomi_home/miot/specs/specv2entity.py index 48d56c2..b239d63 100644 --- a/custom_components/xiaomi_home/miot/specs/specv2entity.py +++ b/custom_components/xiaomi_home/miot/specs/specv2entity.py @@ -170,6 +170,11 @@ SPEC_DEVICE_TRANS_MAP: dict = { 'properties': { 'battery-level': {'read'} } + }, + 'optional': { + 'actions': { + 'start-charge' + } } } }, diff --git a/custom_components/xiaomi_home/vacuum.py b/custom_components/xiaomi_home/vacuum.py index 3957f86..7a61596 100644 --- a/custom_components/xiaomi_home/vacuum.py +++ b/custom_components/xiaomi_home/vacuum.py @@ -203,6 +203,16 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity): self._attr_supported_features |= VacuumEntityFeature.LOCATE self._action_identify = action + # Use start-charge from battery service as fallback + # if stop-and-gocharge is not available + if self._action_stop_and_gocharge is None: + for action in entity_data.actions: + if action.name == 'start-charge': + self._attr_supported_features |= ( + VacuumEntityFeature.RETURN_HOME) + self._action_stop_and_gocharge = action + break + async def async_start(self) -> None: """Start or resume the cleaning task.""" if self._prop_status is not None: From c29f7eecbd2fba9698ee7d12c39d3575425d75a8 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Tue, 19 Aug 2025 14:12:35 +0800 Subject: [PATCH 05/35] fix: delete all unsupported MIoT-Spec-V2 instances of Narwa vacuum (#1355) --- .../xiaomi_home/miot/miot_mips.py | 3 + .../xiaomi_home/miot/specs/spec_add.json | 1419 +++++++++++++++++ .../xiaomi_home/miot/specs/spec_filter.yaml | 6 + 3 files changed, 1428 insertions(+) diff --git a/custom_components/xiaomi_home/miot/miot_mips.py b/custom_components/xiaomi_home/miot/miot_mips.py index 4d43c27..8c51c8b 100644 --- a/custom_components/xiaomi_home/miot/miot_mips.py +++ b/custom_components/xiaomi_home/miot/miot_mips.py @@ -513,6 +513,7 @@ class _MipsClient(ABC): """ self.__thread_check() if not self._mqtt or not self._mqtt.is_connected(): + self.log_error(f'mips sub when not connected, {topic}') return try: if topic not in self._mips_sub_pending_map: @@ -531,6 +532,7 @@ class _MipsClient(ABC): """ self.__thread_check() if not self._mqtt or not self._mqtt.is_connected(): + self.log_debug(f'mips unsub when not connected, {topic}') return try: result, mid = self._mqtt.unsubscribe(topic=topic) @@ -639,6 +641,7 @@ class _MipsClient(ABC): _LOGGER.error('__on_connect, but mqtt is None') return if not self._mqtt.is_connected(): + _LOGGER.error('__on_connect, but mqtt is disconnected') return self.log_info(f'mips connect, {flags}, {rc}, {props}') self.__reset_reconnect_time() diff --git a/custom_components/xiaomi_home/miot/specs/spec_add.json b/custom_components/xiaomi_home/miot/specs/spec_add.json index 2a5bba3..1119fa0 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_add.json +++ b/custom_components/xiaomi_home/miot/specs/spec_add.json @@ -241,6 +241,1425 @@ ] } ], + "urn:miot-spec-v2:device:vacuum:0000A006:narwa-001:1": [ + { + "iid": 2, + "type": "urn:miot-spec-v2:service:vacuum:00007810:narwa-001:1", + "description": "Robot Cleaner", + "properties": [ + { + "iid": 2, + "type": "urn:miot-spec-v2:property:status:00000007:narwa-001:1", + "description": "Status", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 1, + "description": "Idle" + }, + { + "value": 2, + "description": "Charging" + }, + { + "value": 3, + "description": "BreakCharging" + }, + { + "value": 4, + "description": "Sweeping" + }, + { + "value": 5, + "description": "Paused" + }, + { + "value": 6, + "description": "Go Charging" + }, + { + "value": 7, + "description": "GoWash" + }, + { + "value": 8, + "description": "Remote" + }, + { + "value": 9, + "description": "Charging" + }, + { + "value": 10, + "description": "BuildingMap" + }, + { + "value": 11, + "description": "Updating" + }, + { + "value": 12, + "description": "Sleeping" + }, + { + "value": 13, + "description": "Relocation" + }, + { + "value": 14, + "description": "StationWorking" + }, + { + "value": 15, + "description": "Error" + }, + { + "value": 16, + "description": "Sweeping and Mopping" + }, + { + "value": 17, + "description": "Mopping" + }, + { + "value": 18, + "description": "Paused" + }, + { + "value": 19, + "description": "GoChargeBreak" + }, + { + "value": 20, + "description": "WashBreak" + }, + { + "value": 21, + "description": "IdleInOutside" + } + ] + }, + { + "iid": 3, + "type": "urn:miot-spec-v2:property:fault:00000009:narwa-001:1", + "description": "Device Fault", + "format": "uint32", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 0, + "description": "No Faults" + }, + { + "value": 16777248, + "description": "16777248" + }, + { + "value": 34603008, + "description": "34603008" + }, + { + "value": 34668608, + "description": "34668608" + }, + { + "value": 16842848, + "description": "16842848" + }, + { + "value": 16842849, + "description": "16842849" + }, + { + "value": 16842850, + "description": "16842850" + }, + { + "value": 16842851, + "description": "16842851" + }, + { + "value": 33751106, + "description": "33751106" + }, + { + "value": 16842852, + "description": "16842852" + }, + { + "value": 33751105, + "description": "33751105" + }, + { + "value": 16842853, + "description": "16842853" + }, + { + "value": 33751104, + "description": "33751104" + }, + { + "value": 34603121, + "description": "34603121" + }, + { + "value": 34603120, + "description": "34603120" + }, + { + "value": 34603123, + "description": "34603123" + }, + { + "value": 34603122, + "description": "34603122" + }, + { + "value": 33685523, + "description": "33685523" + }, + { + "value": 34603125, + "description": "34603125" + }, + { + "value": 33685524, + "description": "33685524" + }, + { + "value": 34603124, + "description": "34603124" + }, + { + "value": 34668592, + "description": "34668592" + }, + { + "value": 36765959, + "description": "36765959" + }, + { + "value": 34603126, + "description": "34603126" + }, + { + "value": 36765952, + "description": "36765952" + }, + { + "value": 36765954, + "description": "36765954" + }, + { + "value": 36765953, + "description": "36765953" + }, + { + "value": 36765956, + "description": "36765956" + }, + { + "value": 36765955, + "description": "36765955" + }, + { + "value": 36765958, + "description": "36765958" + }, + { + "value": 36765957, + "description": "36765957" + }, + { + "value": 33685525, + "description": "33685525" + }, + { + "value": 16843026, + "description": "16843026" + }, + { + "value": 33751122, + "description": "33751122" + }, + { + "value": 33751121, + "description": "33751121" + }, + { + "value": 16843024, + "description": "16843024" + }, + { + "value": 33751120, + "description": "33751120" + }, + { + "value": 16843025, + "description": "16843025" + }, + { + "value": 33685602, + "description": "33685602" + }, + { + "value": 34603104, + "description": "34603104" + }, + { + "value": 34603105, + "description": "34603105" + }, + { + "value": 33751078, + "description": "33751078" + }, + { + "value": 34668576, + "description": "34668576" + }, + { + "value": 34603107, + "description": "34603107" + }, + { + "value": 33685600, + "description": "33685600" + }, + { + "value": 33685601, + "description": "33685601" + }, + { + "value": 33751076, + "description": "33751076" + }, + { + "value": 33751075, + "description": "33751075" + }, + { + "value": 33751073, + "description": "33751073" + }, + { + "value": 36765745, + "description": "36765745" + }, + { + "value": 34603089, + "description": "34603089" + }, + { + "value": 34603088, + "description": "34603088" + }, + { + "value": 34603090, + "description": "34603090" + }, + { + "value": 16842832, + "description": "16842832" + }, + { + "value": 16842833, + "description": "16842833" + }, + { + "value": 16842834, + "description": "16842834" + }, + { + "value": 16842841, + "description": "16842841" + }, + { + "value": 16842835, + "description": "16842835" + }, + { + "value": 16842836, + "description": "16842836" + }, + { + "value": 33685568, + "description": "33685568" + }, + { + "value": 36765995, + "description": "36765995" + }, + { + "value": 33685569, + "description": "33685569" + }, + { + "value": 36765994, + "description": "36765994" + }, + { + "value": 33685570, + "description": "33685570" + }, + { + "value": 34668545, + "description": "34668545" + }, + { + "value": 34668544, + "description": "34668544" + }, + { + "value": 33685585, + "description": "33685585" + }, + { + "value": 34799648, + "description": "34799648" + }, + { + "value": 33685586, + "description": "33685586" + }, + { + "value": 33685584, + "description": "33685584" + }, + { + "value": 36765733, + "description": "36765733" + }, + { + "value": 36765735, + "description": "36765735" + }, + { + "value": 36765734, + "description": "36765734" + }, + { + "value": 36765736, + "description": "36765736" + }, + { + "value": 16842805, + "description": "16842805" + }, + { + "value": 16842806, + "description": "16842806" + }, + { + "value": 16842801, + "description": "16842801" + }, + { + "value": 36766016, + "description": "36766016" + }, + { + "value": 16842802, + "description": "16842802" + }, + { + "value": 16842804, + "description": "16842804" + }, + { + "value": 34603040, + "description": "34603040" + }, + { + "value": 34603042, + "description": "34603042" + }, + { + "value": 34603041, + "description": "34603041" + }, + { + "value": 34603044, + "description": "34603044" + }, + { + "value": 34668641, + "description": "34668641" + }, + { + "value": 34603043, + "description": "34603043" + }, + { + "value": 34668640, + "description": "34668640" + }, + { + "value": 34603045, + "description": "34603045" + }, + { + "value": 36765969, + "description": "36765969" + }, + { + "value": 36765968, + "description": "36765968" + }, + { + "value": 33685544, + "description": "33685544" + }, + { + "value": 33685545, + "description": "33685545" + }, + { + "value": 33685552, + "description": "33685552" + }, + { + "value": 33685553, + "description": "33685553" + }, + { + "value": 33685554, + "description": "33685554" + }, + { + "value": 36765996, + "description": "36765996" + }, + { + "value": 33685555, + "description": "33685555" + }, + { + "value": 33685556, + "description": "33685556" + }, + { + "value": 36765993, + "description": "36765993" + }, + { + "value": 34603029, + "description": "34603029" + }, + { + "value": 34603031, + "description": "34603031" + }, + { + "value": 34603030, + "description": "34603030" + }, + { + "value": 34603033, + "description": "34603033" + }, + { + "value": 16842899, + "description": "16842899" + }, + { + "value": 34603032, + "description": "34603032" + }, + { + "value": 36765696, + "description": "36765696" + }, + { + "value": 16842897, + "description": "16842897" + }, + { + "value": 36765992, + "description": "36765992" + }, + { + "value": 16842771, + "description": "16842771" + }, + { + "value": 16842769, + "description": "16842769" + }, + { + "value": 16842770, + "description": "16842770" + } + ] + }, + { + "iid": 17, + "type": "urn:miot-spec-v2:property:last-clean-time:00000280:narwa-001:1", + "description": "Last Clean Time", + "format": "uint32", + "access": [ + "notify", + "read" + ], + "value-range": [ + 0, + 4294967295, + 1 + ] + }, + { + "iid": 18, + "type": "urn:miot-spec-v2:property:base-station-working-status:00000281:narwa-001:1", + "description": "Base Station Working Status", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 0, + "description": "NULL" + }, + { + "value": 1, + "description": "Mop Clean" + }, + { + "value": 2, + "description": "Mop Air Dry" + } + ] + }, + { + "iid": 71, + "type": "urn:miot-spec-v2:property:on:00000006:narwa-001:1", + "description": "Switch Status", + "format": "bool", + "access": [ + "read", + "write", + "notify" + ] + } + ], + "actions": [ + { + "iid": 1, + "type": "urn:miot-spec-v2:action:start-sweep:00002804:narwa-001:1", + "description": "Start Sweep", + "in": [], + "out": [] + }, + { + "iid": 2, + "type": "urn:miot-spec-v2:action:stop-sweeping:00002805:narwa-001:1", + "description": "Stop Sweeping", + "in": [], + "out": [] + }, + { + "iid": 3, + "type": "urn:miot-spec-v2:action:stop-and-gocharge:000028B4:narwa-001:1", + "description": "Stop And Gocharge", + "in": [], + "out": [] + }, + { + "iid": 7, + "type": "urn:miot-spec-v2:action:pause-sweeping:00002863:narwa-001:1", + "description": "Pause Sweeping", + "in": [], + "out": [] + }, + { + "iid": 8, + "type": "urn:miot-spec-v2:action:continue-sweep:000028AA:narwa-001:1", + "description": "Continue Sweep", + "in": [], + "out": [] + } + ] + }, + { + "iid": 11, + "type": "urn:miot-spec-v2:service:battery:00007805:narwa-001:1", + "description": "Battery", + "properties": [ + { + "iid": 1, + "type": "urn:miot-spec-v2:property:battery-level:00000014:narwa-001:1", + "description": "Battery Level", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "unit": "percentage", + "value-range": [ + 0, + 100, + 1 + ] + }, + { + "iid": 2, + "type": "urn:miot-spec-v2:property:charging-state:00000015:narwa-001:1", + "description": "Charging State", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 1, + "description": "Charging" + }, + { + "value": 2, + "description": "Not Charging" + }, + { + "value": 3, + "description": "Not Chargeable" + } + ] + } + ] + } + ], + "urn:miot-spec-v2:device:vacuum:0000A006:narwa-ax11:1": [ + { + "iid": 2, + "type": "urn:miot-spec-v2:service:vacuum:00007810:narwa-ax11:1", + "description": "Robot Cleaner", + "properties": [ + { + "iid": 2, + "type": "urn:miot-spec-v2:property:status:00000007:narwa-ax11:1", + "description": "Status", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 1, + "description": "Idle" + }, + { + "value": 2, + "description": "Charging" + }, + { + "value": 3, + "description": "BreakCharging" + }, + { + "value": 4, + "description": "Sweeping" + }, + { + "value": 5, + "description": "Paused" + }, + { + "value": 6, + "description": "Go Charging" + }, + { + "value": 7, + "description": "GoWash" + }, + { + "value": 8, + "description": "Remote" + }, + { + "value": 9, + "description": "Charging" + }, + { + "value": 10, + "description": "BuildingMap" + }, + { + "value": 11, + "description": "Updating" + }, + { + "value": 12, + "description": "Sleeping" + }, + { + "value": 13, + "description": "Relocation" + }, + { + "value": 14, + "description": "StationWorking" + }, + { + "value": 15, + "description": "Error" + }, + { + "value": 16, + "description": "Sweeping and Mopping" + }, + { + "value": 17, + "description": "Mopping" + }, + { + "value": 18, + "description": "Paused" + }, + { + "value": 19, + "description": "GoChargeBreak" + }, + { + "value": 20, + "description": "WashBreak" + }, + { + "value": 21, + "description": "IdleInOutside" + } + ] + }, + { + "iid": 3, + "type": "urn:miot-spec-v2:property:fault:00000009:narwa-ax11:1", + "description": "Device Fault", + "format": "uint32", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 0, + "description": "No Faults" + }, + { + "value": 16777248, + "description": "16777248" + }, + { + "value": 34603008, + "description": "34603008" + }, + { + "value": 34668608, + "description": "34668608" + }, + { + "value": 16842848, + "description": "16842848" + }, + { + "value": 16842849, + "description": "16842849" + }, + { + "value": 16842850, + "description": "16842850" + }, + { + "value": 16842851, + "description": "16842851" + }, + { + "value": 33751106, + "description": "33751106" + }, + { + "value": 16842852, + "description": "16842852" + }, + { + "value": 33751105, + "description": "33751105" + }, + { + "value": 16842853, + "description": "16842853" + }, + { + "value": 33751104, + "description": "33751104" + }, + { + "value": 34603121, + "description": "34603121" + }, + { + "value": 34603120, + "description": "34603120" + }, + { + "value": 34603123, + "description": "34603123" + }, + { + "value": 34603122, + "description": "34603122" + }, + { + "value": 33685523, + "description": "33685523" + }, + { + "value": 34603125, + "description": "34603125" + }, + { + "value": 33685524, + "description": "33685524" + }, + { + "value": 34603124, + "description": "34603124" + }, + { + "value": 34668592, + "description": "34668592" + }, + { + "value": 36765959, + "description": "36765959" + }, + { + "value": 34603126, + "description": "34603126" + }, + { + "value": 36765952, + "description": "36765952" + }, + { + "value": 36765954, + "description": "36765954" + }, + { + "value": 36765953, + "description": "36765953" + }, + { + "value": 36765956, + "description": "36765956" + }, + { + "value": 36765955, + "description": "36765955" + }, + { + "value": 36765958, + "description": "36765958" + }, + { + "value": 36765957, + "description": "36765957" + }, + { + "value": 33685525, + "description": "33685525" + }, + { + "value": 16843026, + "description": "16843026" + }, + { + "value": 33751122, + "description": "33751122" + }, + { + "value": 33751121, + "description": "33751121" + }, + { + "value": 16843024, + "description": "16843024" + }, + { + "value": 33751120, + "description": "33751120" + }, + { + "value": 16843025, + "description": "16843025" + }, + { + "value": 33685602, + "description": "33685602" + }, + { + "value": 34603104, + "description": "34603104" + }, + { + "value": 34603105, + "description": "34603105" + }, + { + "value": 33751078, + "description": "33751078" + }, + { + "value": 34668576, + "description": "34668576" + }, + { + "value": 34603107, + "description": "34603107" + }, + { + "value": 33685600, + "description": "33685600" + }, + { + "value": 33685601, + "description": "33685601" + }, + { + "value": 33751076, + "description": "33751076" + }, + { + "value": 33751075, + "description": "33751075" + }, + { + "value": 33751073, + "description": "33751073" + }, + { + "value": 36765745, + "description": "36765745" + }, + { + "value": 34603089, + "description": "34603089" + }, + { + "value": 34603088, + "description": "34603088" + }, + { + "value": 34603090, + "description": "34603090" + }, + { + "value": 16842832, + "description": "16842832" + }, + { + "value": 16842833, + "description": "16842833" + }, + { + "value": 16842834, + "description": "16842834" + }, + { + "value": 16842841, + "description": "16842841" + }, + { + "value": 16842835, + "description": "16842835" + }, + { + "value": 16842836, + "description": "16842836" + }, + { + "value": 33685568, + "description": "33685568" + }, + { + "value": 36765995, + "description": "36765995" + }, + { + "value": 33685569, + "description": "33685569" + }, + { + "value": 36765994, + "description": "36765994" + }, + { + "value": 33685570, + "description": "33685570" + }, + { + "value": 34668545, + "description": "34668545" + }, + { + "value": 34668544, + "description": "34668544" + }, + { + "value": 33685585, + "description": "33685585" + }, + { + "value": 34799648, + "description": "34799648" + }, + { + "value": 33685586, + "description": "33685586" + }, + { + "value": 33685584, + "description": "33685584" + }, + { + "value": 36765733, + "description": "36765733" + }, + { + "value": 36765735, + "description": "36765735" + }, + { + "value": 36765734, + "description": "36765734" + }, + { + "value": 36765736, + "description": "36765736" + }, + { + "value": 16842805, + "description": "16842805" + }, + { + "value": 16842806, + "description": "16842806" + }, + { + "value": 16842801, + "description": "16842801" + }, + { + "value": 36766016, + "description": "36766016" + }, + { + "value": 16842802, + "description": "16842802" + }, + { + "value": 16842804, + "description": "16842804" + }, + { + "value": 34603040, + "description": "34603040" + }, + { + "value": 34603042, + "description": "34603042" + }, + { + "value": 34603041, + "description": "34603041" + }, + { + "value": 34603044, + "description": "34603044" + }, + { + "value": 34668641, + "description": "34668641" + }, + { + "value": 34603043, + "description": "34603043" + }, + { + "value": 34668640, + "description": "34668640" + }, + { + "value": 34603045, + "description": "34603045" + }, + { + "value": 36765969, + "description": "36765969" + }, + { + "value": 36765968, + "description": "36765968" + }, + { + "value": 33685544, + "description": "33685544" + }, + { + "value": 33685545, + "description": "33685545" + }, + { + "value": 33685552, + "description": "33685552" + }, + { + "value": 33685553, + "description": "33685553" + }, + { + "value": 33685554, + "description": "33685554" + }, + { + "value": 36765996, + "description": "36765996" + }, + { + "value": 33685555, + "description": "33685555" + }, + { + "value": 33685556, + "description": "33685556" + }, + { + "value": 36765993, + "description": "36765993" + }, + { + "value": 34603029, + "description": "34603029" + }, + { + "value": 34603031, + "description": "34603031" + }, + { + "value": 34603030, + "description": "34603030" + }, + { + "value": 34603033, + "description": "34603033" + }, + { + "value": 16842899, + "description": "16842899" + }, + { + "value": 34603032, + "description": "34603032" + }, + { + "value": 36765696, + "description": "36765696" + }, + { + "value": 16842897, + "description": "16842897" + }, + { + "value": 36765992, + "description": "36765992" + }, + { + "value": 16842771, + "description": "16842771" + }, + { + "value": 16842769, + "description": "16842769" + }, + { + "value": 16842770, + "description": "16842770" + } + ] + }, + { + "iid": 4, + "type": "urn:miot-spec-v2:property:sweep-mop-type:00000135:narwa-ax11:1", + "description": "Sweep Mop Type", + "format": "uint8", + "access": [ + "read", + "write", + "notify" + ], + "value-list": [ + { + "value": 1, + "description": "Sweep" + }, + { + "value": 2, + "description": "Mop" + }, + { + "value": 3, + "description": "Sweep Mop" + }, + { + "value": 4, + "description": "Sweep Before Mopping" + } + ] + }, + { + "iid": 17, + "type": "urn:miot-spec-v2:property:last-clean-time:00000280:narwa-ax11:1", + "description": "Last Clean Time", + "format": "uint32", + "access": [ + "notify", + "read" + ], + "value-range": [ + 0, + 4294967295, + 1 + ] + }, + { + "iid": 18, + "type": "urn:miot-spec-v2:property:base-station-working-status:00000281:narwa-ax11:1", + "description": "Base Station Working Status", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 0, + "description": "NULL" + }, + { + "value": 1, + "description": "Mop Clean" + }, + { + "value": 2, + "description": "Mop Air Dry" + } + ] + }, + { + "iid": 71, + "type": "urn:miot-spec-v2:property:on:00000006:narwa-ax11:1", + "description": "Switch Status", + "format": "bool", + "access": [ + "read", + "write", + "notify" + ] + } + ], + "actions": [ + { + "iid": 1, + "type": "urn:miot-spec-v2:action:start-sweep:00002804:narwa-ax11:1", + "description": "Start Sweep", + "in": [], + "out": [] + }, + { + "iid": 2, + "type": "urn:miot-spec-v2:action:stop-sweeping:00002805:narwa-ax11:1", + "description": "Stop Sweeping", + "in": [], + "out": [] + }, + { + "iid": 3, + "type": "urn:miot-spec-v2:action:stop-and-gocharge:000028B4:narwa-ax11:1", + "description": "Stop And Gocharge", + "in": [], + "out": [] + }, + { + "iid": 7, + "type": "urn:miot-spec-v2:action:pause-sweeping:00002863:narwa-ax11:1", + "description": "Pause Sweeping", + "in": [], + "out": [] + }, + { + "iid": 8, + "type": "urn:miot-spec-v2:action:continue-sweep:000028AA:narwa-ax11:1", + "description": "Continue Sweep", + "in": [], + "out": [] + } + ] + }, + { + "iid": 11, + "type": "urn:miot-spec-v2:service:battery:00007805:narwa-ax11:1", + "description": "Battery", + "properties": [ + { + "iid": 1, + "type": "urn:miot-spec-v2:property:battery-level:00000014:narwa-ax11:1", + "description": "Battery Level", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "unit": "percentage", + "value-range": [ + 0, + 100, + 1 + ] + }, + { + "iid": 2, + "type": "urn:miot-spec-v2:property:charging-state:00000015:narwa-ax11:1", + "description": "Charging State", + "format": "uint8", + "access": [ + "read", + "notify" + ], + "value-list": [ + { + "value": 1, + "description": "Charging" + }, + { + "value": 2, + "description": "Not Charging" + }, + { + "value": 3, + "description": "Not Chargeable" + } + ] + } + ] + } + ], "urn:miot-spec-v2:device:water-heater:0000A02A:viomi-m1:2": [ { "iid": 2, diff --git a/custom_components/xiaomi_home/miot/specs/spec_filter.yaml b/custom_components/xiaomi_home/miot/specs/spec_filter.yaml index a7116fb..9b2d19e 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_filter.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_filter.yaml @@ -48,3 +48,9 @@ urn:miot-spec-v2:device:thermostat:0000A031:tofan-wk01: services: - '2' - '4' +urn:miot-spec-v2:device:vacuum:0000A006:narwa-001: + services: + - '*' +urn:miot-spec-v2:device:vacuum:0000A006:narwa-ax11: + services: + - '*' From 65a7a6d22a1d0c8bb70830b7195804a70e642af9 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Tue, 19 Aug 2025 14:36:30 +0800 Subject: [PATCH 06/35] fix: correct the property value format after expression calculation (#1366) --- custom_components/xiaomi_home/miot/miot_device.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_home/miot/miot_device.py b/custom_components/xiaomi_home/miot/miot_device.py index 500734c..671dd7c 100644 --- a/custom_components/xiaomi_home/miot/miot_device.py +++ b/custom_components/xiaomi_home/miot/miot_device.py @@ -1108,6 +1108,8 @@ class MIoTServiceEntity(Entity): ): continue value: Any = prop.value_format(params['value']) + value = prop.eval_expr(value) + value = prop.value_format(value) self._prop_value_map[prop] = value if prop in self._prop_changed_subs: self._prop_changed_subs[prop](prop, value) @@ -1279,8 +1281,9 @@ class MIoTPropertyEntity(Entity): def __on_value_changed(self, params: dict, ctx: Any) -> None: _LOGGER.debug('property changed, %s', params) - self._value = self.spec.value_format(params['value']) - self._value = self.spec.eval_expr(self._value) + value: Any = self.spec.value_format(params['value']) + value = self.spec.eval_expr(value) + self._value = self.spec.value_format(value) if not self._pending_write_ha_state_timer: self.async_write_ha_state() From 947169f18ddbc7557a3f35bf1a856e4c12de956d Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Tue, 19 Aug 2025 17:49:27 +0800 Subject: [PATCH 07/35] Fix specs (#1367) * fix: xiaomi.fan.p70 fan level * fix: xiaomi.fan.p76 fan level * fix: ignore hmpace.motion.v6nfc * fix: xiaomi.airc.rr0r00 humidity-range * fix: xiaomi.airc.h43h00 humidity-range * fix: zhimi.humidifier.ca4 water level --- custom_components/xiaomi_home/miot/const.py | 1 + .../xiaomi_home/miot/specs/spec_modify.yaml | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/custom_components/xiaomi_home/miot/const.py b/custom_components/xiaomi_home/miot/const.py index 8a0e44b..2b8be01 100644 --- a/custom_components/xiaomi_home/miot/const.py +++ b/custom_components/xiaomi_home/miot/const.py @@ -89,6 +89,7 @@ SUPPORTED_PLATFORMS: list = [ UNSUPPORTED_MODELS: list = [ 'chuangmi.ir.v2', + 'hmpace.motion.v6nfc', 'xiaomi.router.rd03' ] diff --git a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml index 183879a..bf697a4 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml @@ -22,6 +22,10 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:2: urn:miot-spec-v2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h40h00:1: prop.10.6: unit: none +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:1: + prop.10.6: + unit: none +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:1 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1: prop.10.6: unit: none @@ -41,6 +45,12 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1: prop.10.6: unit: none urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1 +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1: + prop.10.6: + unit: none +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1 +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1 +urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1 urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgd1st:1: prop.3.7: value-range: @@ -130,6 +140,12 @@ urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1: urn:miot-spec-v2:device:fan:0000A005:xiaomi-p69:1:0000D062: prop.2.4: name: fan-level-a +urn:miot-spec-v2:device:fan:0000A005:xiaomi-p70:1:0000D062: + prop.2.4: + name: fan-level-a +urn:miot-spec-v2:device:fan:0000A005:xiaomi-p76:1:0000D062: + prop.2.4: + name: fan-level-a urn:miot-spec-v2:device:fan:0000A005:zhimi-sa1:3: prop.2.2: name: fan-level-a @@ -175,6 +191,10 @@ urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:2: urn:miot-spec-v2:device:hood urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:3: prop.3.1: name: on-ventilation +urn:miot-spec-v2:device:humidifier:0000A00E:zhimi-ca4:2: + prop.2.7: + unit: percentage + expr: round(src_value*0.83) urn:miot-spec-v2:device:kettle:0000A009:yunmi-r3:1: prop.3.1: unit: ppm From 0f65635342bb2b1cb475b943a18049379cb53050 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Wed, 20 Aug 2025 09:15:31 +0800 Subject: [PATCH 08/35] docs: update changelog and version to v0.4.2 (#1368) --- CHANGELOG.md | 9 +++++++++ custom_components/xiaomi_home/manifest.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c7caf..ffe6e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ # CHANGELOG +## v0.4.2 +### Changed +- Set the battery service's start-charge action as the fallback action to support RETURN_HOME feature of the vacuum entity. [#1344](https://github.com/XiaoMi/ha_xiaomi_home/pull/1344) +### Fixed +- Correct the property value format after expression calculation. [#1366](https://github.com/XiaoMi/ha_xiaomi_home/pull/1366) +- Fix the MIoT-Spec-V2 of fix: xiaomi.fan.p70 and fix: xiaomi.fan.p76 fan level, xiaomi.airc.rr0r00 and xiaomi.airc.h43h00 humidity-range, and zhimi.humidifier.ca4 water level. [#1367](https://github.com/XiaoMi/ha_xiaomi_home/pull/1367) +- Ignore the unsupported model hmpace.motion.v6nfc. +- Delete all unsupported MIoT-Spec-V2 instances of narwa.vacuum.001 and narwa.vacuum.ax11. [#1355](https://github.com/XiaoMi/ha_xiaomi_home/pull/1355) + ## v0.4.1 ### Changed - The setting option "Cover closed position" in CONFIGURE is changed to "Cover dead zone width". [#1301](https://github.com/XiaoMi/ha_xiaomi_home/pull/1301) diff --git a/custom_components/xiaomi_home/manifest.json b/custom_components/xiaomi_home/manifest.json index e444e30..7bb99de 100644 --- a/custom_components/xiaomi_home/manifest.json +++ b/custom_components/xiaomi_home/manifest.json @@ -25,7 +25,7 @@ "cryptography", "psutil" ], - "version": "v0.4.1", + "version": "v0.4.2", "zeroconf": [ "_miot-central._tcp.local." ] From 073cdf2dcb9c623cb72721c00f680092e3312ef8 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 29 Aug 2025 17:35:46 +0800 Subject: [PATCH 09/35] fix: integer value step (#1388) --- custom_components/xiaomi_home/miot/miot_spec.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_home/miot/miot_spec.py b/custom_components/xiaomi_home/miot/miot_spec.py index 9cabdcb..7f6a5b2 100644 --- a/custom_components/xiaomi_home/miot/miot_spec.py +++ b/custom_components/xiaomi_home/miot/miot_spec.py @@ -601,7 +601,10 @@ class MIoTSpecProperty(_MIoTSpecBase): if value is None: return None if self.format_ == int: - return int(round(value)) + if self.value_range is None: + return int(round(value)) + return int( + round(value / self.value_range.step) * self.value_range.step) if self.format_ == float: return round(value, self.precision) if self.format_ == bool: From f2200ba003947ad93dbd2a63e76d83a2f19e6d4b Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 29 Aug 2025 17:36:25 +0800 Subject: [PATCH 10/35] fix: contact-state value format (#1387) --- custom_components/xiaomi_home/binary_sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/xiaomi_home/binary_sensor.py b/custom_components/xiaomi_home/binary_sensor.py index 8019104..b2d00b6 100644 --- a/custom_components/xiaomi_home/binary_sensor.py +++ b/custom_components/xiaomi_home/binary_sensor.py @@ -70,8 +70,8 @@ async def async_setup_entry( for miot_device in device_list: if miot_device.miot_client.display_binary_bool: for prop in miot_device.prop_list.get('binary_sensor', []): - new_entities.append(BinarySensor( - miot_device=miot_device, spec=prop)) + new_entities.append( + BinarySensor(miot_device=miot_device, spec=prop)) if new_entities: async_add_entities(new_entities) @@ -90,7 +90,7 @@ class BinarySensor(MIoTPropertyEntity, BinarySensorEntity): def is_on(self) -> bool: """On/Off state. True if the binary sensor is on, False otherwise.""" if self.spec.name == 'contact-state': - return self._value is False + return bool(self._value) is False elif self.spec.name == 'occupancy-status': return bool(self._value) return self._value is True From ec833b6539963aeabe4f03ce929d429f3009c620 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Tue, 2 Sep 2025 17:22:40 +0800 Subject: [PATCH 11/35] feat: subscribe the proxy gateway child device up messages even though the device is offline (#1393) * feat: subscribe the proxy gateway child device up messages even though the device is offline (#1313) * feat: do not subscribe proxy gateway child device online/offline state message --- custom_components/xiaomi_home/miot/miot_client.py | 7 +++++-- custom_components/xiaomi_home/miot/miot_mips.py | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/custom_components/xiaomi_home/miot/miot_client.py b/custom_components/xiaomi_home/miot/miot_client.py index f8f3141..e6fe6a8 100644 --- a/custom_components/xiaomi_home/miot/miot_client.py +++ b/custom_components/xiaomi_home/miot/miot_client.py @@ -1374,10 +1374,13 @@ class MIoTClient: """Update cloud devices. NOTICE: This function will operate the cloud_list """ - # MIoT cloud service may not publish the online state updating message + # MIoT cloud may not publish the online state updating message # for the BLE device. Assume that all BLE devices are online. + # MIoT cloud does not publish the online state updating message for the + # child device under the proxy gateway (eg, VRF air conditioner + # controller). Assume that all proxy gateway child devices are online. for did, info in cloud_list.items(): - if did.startswith('blt.'): + if did.startswith('blt.') or did.startswith('proxy.'): info['online'] = True for did, info in self._device_list_cache.items(): if filter_dids and did not in filter_dids: diff --git a/custom_components/xiaomi_home/miot/miot_mips.py b/custom_components/xiaomi_home/miot/miot_mips.py index 8c51c8b..6e7186d 100644 --- a/custom_components/xiaomi_home/miot/miot_mips.py +++ b/custom_components/xiaomi_home/miot/miot_mips.py @@ -998,9 +998,11 @@ class MipsCloudClient(_MipsClient): did, MIoTDeviceState.ONLINE if msg['event'] == 'online' else MIoTDeviceState.OFFLINE, ctx) - if did.startswith('blt.'): - # MIoT cloud may not publish BLE device online/offline state message. - # Do not subscribe BLE device online/offline state. + if did.startswith('blt.') or did.startswith('proxy.'): + # MIoT cloud may not publish BLE device or proxy gateway child device + # online/offline state message. + # Do not subscribe BLE device or proxy gateway child device + # online/offline state. return True return self.__reg_broadcast_external( topic=topic, handler=on_state_msg, handler_ctx=handler_ctx) From 739998211ef20a6fa7269eef765852cd2381e663 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 10 Oct 2025 16:31:02 +0800 Subject: [PATCH 12/35] feat: remove VacuumEntityFeature.BATTERY from the vacuum entity (#1433) --- .../xiaomi_home/miot/specs/specv2entity.py | 5 ----- custom_components/xiaomi_home/vacuum.py | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/custom_components/xiaomi_home/miot/specs/specv2entity.py b/custom_components/xiaomi_home/miot/specs/specv2entity.py index b239d63..ff7849d 100644 --- a/custom_components/xiaomi_home/miot/specs/specv2entity.py +++ b/custom_components/xiaomi_home/miot/specs/specv2entity.py @@ -167,11 +167,6 @@ SPEC_DEVICE_TRANS_MAP: dict = { }, 'battery': { 'required': { - 'properties': { - 'battery-level': {'read'} - } - }, - 'optional': { 'actions': { 'start-charge' } diff --git a/custom_components/xiaomi_home/vacuum.py b/custom_components/xiaomi_home/vacuum.py index 7a61596..242d96b 100644 --- a/custom_components/xiaomi_home/vacuum.py +++ b/custom_components/xiaomi_home/vacuum.py @@ -90,7 +90,6 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity): # pylint: disable=unused-argument _prop_status: Optional[MIoTSpecProperty] _prop_fan_level: Optional[MIoTSpecProperty] - _prop_battery_level: Optional[MIoTSpecProperty] _prop_status_cleaning: Optional[list[int]] _prop_status_docked: Optional[list[int]] _prop_status_paused: Optional[list[int]] @@ -117,7 +116,6 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity): self._prop_status = None self._prop_fan_level = None - self._prop_battery_level = None self._prop_status_cleaning = [] self._prop_status_docked = [] self._prop_status_paused = [] @@ -180,9 +178,6 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity): self._attr_fan_speed_list = list(self._fan_level_map.values()) self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED self._prop_fan_level = prop - elif prop.name == 'battery-level': - self._attr_supported_features |= VacuumEntityFeature.BATTERY - self._prop_battery_level = prop # action for action in entity_data.actions: if action.name == 'start-sweep': @@ -251,11 +246,6 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity): """Name of the vacuum entity.""" return self._device_name - @property - def battery_level(self) -> Optional[int]: - """The current battery level of the vacuum cleaner.""" - return self.get_prop_value(prop=self._prop_battery_level) - @property def fan_speed(self) -> Optional[str]: """The current fan speed of the vacuum cleaner.""" From a11c3e2fda4fcd8c9dda38b8c9aae833bae8c98b Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Sat, 11 Oct 2025 11:33:07 +0800 Subject: [PATCH 13/35] Fix specs (#1394) * fix: xiaomi.airc.rr0r00 swing mode (#1386) * fix: hyd.airer.lyjpro current-position (#1376) * feat: add an alongside button entity for xiaomi.wifispeaker.l05b play action (#1372) * fix: ignore the unsupported property 2.3 of 759413.aircondition.iez (#1391) * fix: ignore unsupported properties of xiaomi.wifispeaker.l15a * feat: zhimi.fan.za1 fan mode description in zh_Hans (#1424) * fix: roidmi.vacuum.v60 siid=2 aiid=3 out field format (#1437) --- .../xiaomi_home/miot/specs/multi_lang.json | 10 ++++-- .../xiaomi_home/miot/specs/spec_add.json | 34 +++++++++++++++++++ .../xiaomi_home/miot/specs/spec_filter.yaml | 13 +++++++ .../xiaomi_home/miot/specs/spec_modify.yaml | 14 ++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_home/miot/specs/multi_lang.json b/custom_components/xiaomi_home/miot/specs/multi_lang.json index 68d2fb9..becd9e1 100644 --- a/custom_components/xiaomi_home/miot/specs/multi_lang.json +++ b/custom_components/xiaomi_home/miot/specs/multi_lang.json @@ -5,6 +5,12 @@ "service:003:property:001:valuelist:001": "Dry" } }, + "urn:miot-spec-v2:device:fan:0000A005:zhimi-za1": { + "zh-Hans": { + "service:002:property:005:valuelist:000": "自然风", + "service:002:property:005:valuelist:001": "直吹风" + } + }, "urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1": { "de": { "service:001": "Geräteinformationen", @@ -274,13 +280,13 @@ "service:002:property:002": "Air Conditioner Mode", "service:004": "Air Conditioner" }, - "zh_cn": { + "zh-Hans": { "service:002": "地暖", "service:004": "空调" } }, "urn:miot-spec-v2:device:vacuum:0000A006:ijai-v1": { - "zh_cn": { + "zh-Hans": { "service:007:property:005:valuelist:000": "安静", "service:007:property:005:valuelist:001": "标准", "service:007:property:005:valuelist:002": "中档", diff --git a/custom_components/xiaomi_home/miot/specs/spec_add.json b/custom_components/xiaomi_home/miot/specs/spec_add.json index 1119fa0..c161f9d 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_add.json +++ b/custom_components/xiaomi_home/miot/specs/spec_add.json @@ -82,6 +82,22 @@ ] } ], + "urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l05b:1": [ + { + "iid": 3, + "type": "urn:miot-spec-v2:service:play:0000781D:xiaomi-l05b:1", + "description": "Play Control", + "actions": [ + { + "iid": 2, + "type": "urn:miot-spec-v2:action:play:0000280B:xiaomi-l05b:1", + "description": "Play", + "in": [], + "out": [] + } + ] + } + ], "urn:miot-spec-v2:device:thermostat:0000A031:tofan-wk01:1:0000C822": [ { "iid": 2, @@ -1660,6 +1676,24 @@ ] } ], + "urn:miot-spec-v2:device:vacuum:0000A006:roidmi-v60:3": [ + { + "iid": 2, + "type": "urn:miot-spec-v2:service:vacuum:00007810:roidmi-v60:1", + "description": "Robot Cleaner", + "actions": [ + { + "iid": 3, + "type": "urn:miot-spec-v2:action:start-room-sweep:00002826:roidmi-v60:1", + "description": "Start Room Sweep", + "in": [ + 9 + ], + "out": [] + } + ] + } + ], "urn:miot-spec-v2:device:water-heater:0000A02A:viomi-m1:2": [ { "iid": 2, diff --git a/custom_components/xiaomi_home/miot/specs/spec_filter.yaml b/custom_components/xiaomi_home/miot/specs/spec_filter.yaml index 9b2d19e..7c172ab 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_filter.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_filter.yaml @@ -1,6 +1,9 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:090615-ktf: services: - '4' +urn:miot-spec-v2:device:air-conditioner:0000A004:759413-iez: + properties: + - '2.3' urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-ma4: properties: - 9.* @@ -44,6 +47,13 @@ urn:miot-spec-v2:device:motion-sensor:0000A014:xiaomi-pir1: services: - '1' - '5' +urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l15a: + properties: + - '3.3' + - '6.1' + - '6.2' + - '6.3' + - '6.4' urn:miot-spec-v2:device:thermostat:0000A031:tofan-wk01: services: - '2' @@ -54,3 +64,6 @@ urn:miot-spec-v2:device:vacuum:0000A006:narwa-001: urn:miot-spec-v2:device:vacuum:0000A006:narwa-ax11: services: - '*' +urn:miot-spec-v2:device:vacuum:0000A006:roidmi-v60: + actions: + - '2.3' diff --git a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml index bf697a4..48f1327 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml @@ -48,6 +48,18 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:2: urn:miot-spec-v2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1: prop.10.6: unit: none + prop.3.12: + name: vertical-swing-left-up + prop.3.13: + name: vertical-swing-left-down + prop.3.14: + name: vertical-swing-right-up + prop.3.15: + name: vertical-swing-right-down + prop.3.20: + name: horizontal-swing-left + prop.3.22: + name: horizontal-swing-right urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1 @@ -72,6 +84,8 @@ urn:miot-spec-v2:device:airer:0000A00D:hyd-lyjpro:1: name: target-position-a prop.2.9: name: target-position-b + prop.2.11: + expr: (100-src_value) urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:1: prop.2.3: value-range: From fbddaf80a73fd24eeb6101388e0de992154e86d9 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Mon, 13 Oct 2025 17:11:30 +0800 Subject: [PATCH 14/35] fix: pylint 4.0.0 (#1455) --- custom_components/xiaomi_home/miot/web_pages.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/custom_components/xiaomi_home/miot/web_pages.py b/custom_components/xiaomi_home/miot/web_pages.py index d6ffd9f..c86bdb4 100644 --- a/custom_components/xiaomi_home/miot/web_pages.py +++ b/custom_components/xiaomi_home/miot/web_pages.py @@ -49,7 +49,7 @@ MIoT redirect web pages. import os import asyncio -_template = '' +web_template = '' def _load_page_template(): @@ -57,18 +57,18 @@ def _load_page_template(): os.path.dirname(os.path.abspath(__file__)), 'resource/oauth_redirect_page.html') with open(path, 'r', encoding='utf-8') as f: - global _template - _template = f.read() + global web_template + web_template = f.read() async def oauth_redirect_page( title: str, content: str, button: str, success: bool ) -> str: """Return oauth redirect page.""" - if _template == '': + if web_template == '': await asyncio.get_running_loop().run_in_executor( None, _load_page_template) - web_page = _template.replace('TITLE_PLACEHOLDER', title) + web_page = web_template.replace('TITLE_PLACEHOLDER', title) web_page = web_page.replace('CONTENT_PLACEHOLDER', content) web_page = web_page.replace('BUTTON_PLACEHOLDER', button) web_page = web_page.replace( From c04fa542a345e22e56b7db492b8a75c2987d575e Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Tue, 14 Oct 2025 08:33:36 +0800 Subject: [PATCH 15/35] docs: update changelog and version to v0.4.3 (#1453) --- CHANGELOG.md | 16 +++++++++++++++- custom_components/xiaomi_home/manifest.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffe6e48..1093ab5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,24 @@ # CHANGELOG +## v0.4.3 +### Changed +- Remove `VacuumEntityFeature.BATTERY` from the vacuum entity. [#1433](https://github.com/XiaoMi/ha_xiaomi_home/pull/1433) +- Subscribe the proxy gateway child device up messages even though the device is offline. [#1393](https://github.com/XiaoMi/ha_xiaomi_home/pull/1393) +### Fixed +- Fix the integer value step. [#1388](https://github.com/XiaoMi/ha_xiaomi_home/pull/1388) +- Fix the contact-state property value format. [#1387](https://github.com/XiaoMi/ha_xiaomi_home/pull/1387) +- Fix the MIoT-Spec-V2 of xiaomi.airc.rr0r00 swing mode and hyd.airer.lyjpro current-position. [#1394](https://github.com/XiaoMi/ha_xiaomi_home/pull/1394) +- Fix roidmi.vacuum.v60 siid=2 aiid=3 out field format. +- Ignore unsupported properties of xiaomi.wifispeaker.l15a and 759413.aircondition.iez. +- Add an alongside button entity for xiaomi.wifispeaker.l05b play action. +- Add zhimi.fan.za1 fan mode description in zh_Hans. +- Fix the error reported by pylint-4.0.0. [#1455](https://github.com/XiaoMi/ha_xiaomi_home/pull/1455) + ## v0.4.2 ### Changed - Set the battery service's start-charge action as the fallback action to support RETURN_HOME feature of the vacuum entity. [#1344](https://github.com/XiaoMi/ha_xiaomi_home/pull/1344) ### Fixed - Correct the property value format after expression calculation. [#1366](https://github.com/XiaoMi/ha_xiaomi_home/pull/1366) -- Fix the MIoT-Spec-V2 of fix: xiaomi.fan.p70 and fix: xiaomi.fan.p76 fan level, xiaomi.airc.rr0r00 and xiaomi.airc.h43h00 humidity-range, and zhimi.humidifier.ca4 water level. [#1367](https://github.com/XiaoMi/ha_xiaomi_home/pull/1367) +- Fix the MIoT-Spec-V2 of xiaomi.fan.p70 and xiaomi.fan.p76 fan level, xiaomi.airc.rr0r00 and xiaomi.airc.h43h00 humidity-range, and zhimi.humidifier.ca4 water level. [#1367](https://github.com/XiaoMi/ha_xiaomi_home/pull/1367) - Ignore the unsupported model hmpace.motion.v6nfc. - Delete all unsupported MIoT-Spec-V2 instances of narwa.vacuum.001 and narwa.vacuum.ax11. [#1355](https://github.com/XiaoMi/ha_xiaomi_home/pull/1355) diff --git a/custom_components/xiaomi_home/manifest.json b/custom_components/xiaomi_home/manifest.json index 7bb99de..0a9ada4 100644 --- a/custom_components/xiaomi_home/manifest.json +++ b/custom_components/xiaomi_home/manifest.json @@ -25,7 +25,7 @@ "cryptography", "psutil" ], - "version": "v0.4.2", + "version": "v0.4.3", "zeroconf": [ "_miot-central._tcp.local." ] From 1ec325c9c74e7ff7510f42e0a9a22c63473e8a67 Mon Sep 17 00:00:00 2001 From: vshijiav Date: Thu, 6 Nov 2025 18:18:25 -0800 Subject: [PATCH 16/35] fix: stop MQTT internal loop immediately when main loop is closed (#1484) --- custom_components/xiaomi_home/miot/miot_mips.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/custom_components/xiaomi_home/miot/miot_mips.py b/custom_components/xiaomi_home/miot/miot_mips.py index 6e7186d..afd0c46 100644 --- a/custom_components/xiaomi_home/miot/miot_mips.py +++ b/custom_components/xiaomi_home/miot/miot_mips.py @@ -589,6 +589,13 @@ class _MipsClient(ABC): def __mqtt_loop_handler(self) -> None: try: + # If the main loop is closed, stop the internal loop immediately + if self.main_loop.is_closed(): + self.log_debug( + 'The main loop is closed, stop the internal loop.') + if not self._internal_loop.is_closed(): + self._internal_loop.stop() + return if self._mqtt: self._mqtt.loop_read() if self._mqtt: From a1a216aea67b609ab692a28d2a5cda06e48f5e91 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 7 Nov 2025 10:19:01 +0800 Subject: [PATCH 17/35] fix: float value precision (#1485) --- custom_components/xiaomi_home/miot/miot_spec.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/custom_components/xiaomi_home/miot/miot_spec.py b/custom_components/xiaomi_home/miot/miot_spec.py index 7f6a5b2..cd61887 100644 --- a/custom_components/xiaomi_home/miot/miot_spec.py +++ b/custom_components/xiaomi_home/miot/miot_spec.py @@ -66,7 +66,7 @@ class MIoTSpecValueRange: """MIoT SPEC value range class.""" min_: int max_: int - step: int + step: int | float def __init__(self, value_range: Union[dict, list]) -> None: if isinstance(value_range, dict): @@ -567,9 +567,8 @@ class MIoTSpecProperty(_MIoTSpecBase): return self._value_range = MIoTSpecValueRange(value_range=value) if isinstance(value, list): - self.precision = len(str( - value[2]).split('.')[1].rstrip('0')) if '.' in str( - value[2]) else 0 + step_: str = format(value[2], '.10f').rstrip('0').rstrip('.') + self.precision = len(step_.split('.')[1]) if '.' in step_ else 0 @property def value_list(self) -> Optional[MIoTSpecValueList]: From f49e76937ca2afda913e611d624413aea5bad1b7 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 7 Nov 2025 10:19:31 +0800 Subject: [PATCH 18/35] fix: climate entity swing mode setting (#1486) --- custom_components/xiaomi_home/climate.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index 9d872e3..50d49a0 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -320,9 +320,15 @@ class FeatureSwingMode(MIoTServiceEntity, ClimateEntity): await self.set_property_async(prop=self._prop_vertical_swing, value=True) elif swing_mode == SWING_HORIZONTAL: + if self._prop_vertical_swing: + await self.set_property_async(prop=self._prop_vertical_swing, + value=False) await self.set_property_async(prop=self._prop_horizontal_swing, value=True) elif swing_mode == SWING_VERTICAL: + if self._prop_horizontal_swing: + await self.set_property_async(prop=self._prop_horizontal_swing, + value=False) await self.set_property_async(prop=self._prop_vertical_swing, value=True) elif swing_mode == SWING_OFF: From 551b5cc9386f8b8847580253bf5a3870c3b39546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=BCmin=20K=C3=B6yk=C4=B1ran?= Date: Mon, 10 Nov 2025 04:50:02 +0300 Subject: [PATCH 19/35] feat: add Turkish language support (#1468) * feat: add Turkish language support Added comprehensive Turkish (tr) language translations for Xiaomi Home Integration. Changes: - Added custom_components/xiaomi_home/translations/tr.json with complete UI translations - Added custom_components/xiaomi_home/miot/i18n/tr.json with MIoT-specific translations - All placeholders, markdown formatting, and special characters preserved - Validated against English source files for structural integrity Technical details: - Followed Home Assistant language code standards (ISO 639-1: tr) - Maintained formal Turkish (siz form) for professional user experience - Preserved all technical terms (OAuth2, MIoT-Spec-V2, MQTT, etc.) - JSON structure validated and all keys match English version * feat: add Turkish translations to bool_trans.yaml Added Turkish boolean value translations for: - default: true/false values - contact_state: contact/no contact states - motion_state: motion detected states - open_close: open/close states - yes_no: yes/no values * docs: update README to reflect Turkish language support Updated multiple language support section: - Changed from 8 to 13 supported languages - Added Turkish (tr) to the language code list - Updated language list to include all currently supported languages: Italian, Dutch, Portuguese, Brazilian Portuguese, and Turkish * fix: correct language dictionary sorting and update documentation - Sort INTEGRATION_LANGUAGES dictionary alphabetically (tr after ru) - Update CLAUDE.md to reflect 13 supported languages instead of 8 - Fixes test_miot_data_sort() test requirement - No functional changes, documentation and code style improvements only * docs: update Chinese README to reflect 13 supported languages - Changed from 8 to 13 languages in Chinese documentation - Added Turkish, Italian, Dutch, Portuguese, Brazilian Portuguese - Updated language code list to include: it, nl, pt, pt-BR, tr - Changed translation source note from "machine translation" to "machine translation or community contributions" - Maintains consistency with English README.md --- CLAUDE.md | 192 +++++++++++++++ README.md | 4 +- custom_components/xiaomi_home/miot/const.py | 1 + .../xiaomi_home/miot/i18n/tr.json | 160 +++++++++++++ .../xiaomi_home/miot/specs/bool_trans.yaml | 15 ++ .../xiaomi_home/translations/tr.json | 225 ++++++++++++++++++ doc/README_zh.md | 4 +- 7 files changed, 597 insertions(+), 4 deletions(-) create mode 100644 CLAUDE.md create mode 100644 custom_components/xiaomi_home/miot/i18n/tr.json create mode 100644 custom_components/xiaomi_home/translations/tr.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8030244 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,192 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Xiaomi Home Integration is an official Home Assistant integration for controlling Xiaomi IoT smart devices. It connects to devices via Xiaomi Cloud (MQTT) or locally through Xiaomi Central Hub Gateway. The integration converts MIoT-Spec-V2 device specifications into Home Assistant entities. + +## Development Commands + +### Installation & Setup +```bash +# Install to Home Assistant config directory +./install.sh /path/to/config + +# Install test dependencies +pip install pytest pytest-asyncio pytest-dependency zeroconf paho.mqtt psutil cryptography slugify +``` + +### Testing +```bash +# Run all tests +pytest -v -s -m github ./test/ + +# Run specific test files +pytest -v -s ./test/test_spec.py +pytest -v -s ./test/test_cloud.py +pytest -v -s ./test/test_lan.py + +# Check rule format +pytest -v -s -m github ./test/check_rule_format.py +``` + +### Code Quality +```bash +# Run pylint (follows Google Python Style Guide) +pylint $(git ls-files '*.py') + +# Lint specific files +pylint custom_components/xiaomi_home/*.py +``` + +### Validation +```bash +# HACS validation (run by GitHub Actions) +# Uses: hacs/action@main + +# Hassfest validation (run by GitHub Actions) +# Uses: home-assistant/actions/hassfest@master +``` + +## Architecture Overview + +### Core Components (miot/) + +The integration is built around the `miot/` core package: + +**miot_client.py**: Top-level client instance representing a logged-in Xiaomi user. Each user login creates one MIoTClient. Manages authentication, device list, and message routing. + +**miot_cloud.py**: OAuth 2.0 authentication and HTTP API calls to Xiaomi Cloud. Handles token refresh, user info, device control commands, and spec downloads. + +**miot_mips.py**: Message bus (MQTT) for subscribing to device property changes and events. Implements both cloud (MipsCloudClient) and local (MipsLocalClient) message handling. + +**miot_device.py**: Device entity class. Each MIoT device creates multiple MIoTDevice instances (one per Home Assistant entity). Handles property updates, action execution, and event processing. + +**miot_spec.py**: MIoT-Spec-V2 parser. Parses device specifications (URN-based type system) from cloud or local cache. Each spec defines services, properties, events, and actions. + +**miot_lan.py**: Local LAN control for IP devices in same network. Discovery and control without cloud (optional). + +**miot_mdns.py**: mDNS discovery for Xiaomi Central Hub Gateway services. + +**miot_storage.py**: File storage for certificates, device specs, translations, and cached data. + +**miot_network.py**: Network status monitoring and IP address detection. + +**miot_i18n.py**: Multi-language support (13 languages). Manages translations for entity names. + +### Entity Conversion (specs/specv2entity.py) + +MIoT-Spec-V2 instances are converted to Home Assistant entities using three mapping dictionaries: + +- **SPEC_DEVICE_TRANS_MAP**: Whole-device patterns (e.g., vacuum, humidifier, climate) +- **SPEC_SERVICE_TRANS_MAP**: Service-level patterns (e.g., battery, air-purifier) +- **SPEC_PROP_TRANS_MAP**: Property-level patterns (e.g., temperature, humidity) + +Conversion priority: Device > Service > Property > General rules + +### Spec Customization Files (miot/specs/) + +**spec_filter.yaml**: Filters out MIoT-Spec-V2 instances that should NOT be converted to entities. Uses device URN keys and supports wildcard matching for service/property/event/action IIDs. + +**spec_modify.yaml**: Modifies spec instances before conversion (e.g., changing value ranges, access modes). + +**multi_lang.json**: Local translation overrides with higher priority than cloud translations. Keyed by device URN (without version). + +**spec_add.json**: Additional spec definitions for devices not in cloud database. + +**bool_trans.yaml**: Boolean value translation mappings. + +After editing spec files, you MUST update conversion rules via Integration CONFIGURE page in Home Assistant. + +### Platform Files (custom_components/xiaomi_home/) + +Standard Home Assistant platform files (sensor.py, switch.py, climate.py, etc.) implement entity registration and state management. Each platform imports from miot_device.py and creates entity subclasses. + +**config_flow.py**: Configuration flow for OAuth login and device selection. + +**__init__.py**: Integration setup, entry management, and data structure initialization. + +## MIoT-Spec-V2 Concepts + +**URN Format**: `urn::::[::]` +- namespace: miot-spec-v2 (Xiaomi), bluetooth-spec (SIG), or vendor-specific +- type: device, service, property, event, action +- name: human-readable identifier (used for mapping) + +**IIDs (Instance IDs)**: Decimal identifiers +- siid: Service Instance ID +- piid: Property Instance ID +- eiid: Event Instance ID +- aiid: Action Instance ID + +**Instance Code Format**: +``` +service: # service +service::property: # property +service::property::valuelist: # value list item +service::event: # event +service::action: # action +``` + +## Naming Conventions + +From CONTRIBUTING.md: + +- **Xiaomi**: Always "Xiaomi" in text. Variables: "xiaomi" or "mi" +- **Xiaomi Home**: Always "Xiaomi Home" in text. Variables: "mihome" or "MiHome" +- **Xiaomi IoT**: Always "MIoT" in text. Variables: "miot" or "MIoT" +- **Home Assistant**: Always "Home Assistant" in text. Variables: "hass" or "hass_xxx" + +Mixed Chinese/English: Add space between Chinese and English or use Chinese quotation marks. + +## Commit Message Format + +``` +: + + + +