Compare commits

...

7 Commits

Author SHA1 Message Date
Necroneco
108a5c5f47
Merge 506bd9f52e into 4c2e10038c 2025-07-09 10:22:01 +08:00
Li Shuzhen
4c2e10038c
docs: update changelog and version to v0.3.4 (#1238)
Some checks are pending
Tests / check-rule-format (push) Waiting to run
Validate / validate-hassfest (push) Waiting to run
Validate / validate-hacs (push) Waiting to run
Validate / validate-lint (push) Waiting to run
Validate / validate-setup (push) Waiting to run
2025-07-09 09:39:43 +08:00
Li Shuzhen
9afc62f39a
docs: modify README (#1237)
* docs: modify spec_filter.yaml in README

* docs: modify event descriptions in README
2025-07-09 09:17:57 +08:00
Li Shuzhen
b46805b92c
fix: airer status for cover entity (#1235)
* fix: xiaomi.airer.pro3 airer status rising (#1222)

* fix: airer status

* fix: filter out non alphabetic characters from status descriptions
2025-07-09 09:16:55 +08:00
Li Shuzhen
a43447ef61
Fix specs (#1236)
* fix: ignore bjkcz.curtain.kczble curtain status (#1184)

* fix: yutai.plug.fsov8m power consumption (#1229)
2025-07-09 09:14:10 +08:00
caibinqing
506bd9f52e
fix pylint 2025-04-07 11:19:19 +08:00
caibinqing
7c0caa9df7
fix: add migration step for config entry 2025-04-07 10:54:09 +08:00
10 changed files with 191 additions and 61 deletions

View File

@ -1,9 +1,23 @@
# CHANGELOG
## v0.3.4
### Added
- Exclude the unsupported device models. [#1205](https://github.com/XiaoMi/ha_xiaomi_home/pull/1205)
### Changed
- Subscribe the BLE device upstream messages even though the device is offline. [#1207](https://github.com/XiaoMi/ha_xiaomi_home/pull/1207)
- Record "opening", "closing" and "closed" status of the airer service that occur frequently and do not record "stop" status for the cover entity. [#1235](https://github.com/XiaoMi/ha_xiaomi_home/pull/1235)
- Modify README about spec_filter.yaml and the event attributes. [#1237](https://github.com/XiaoMi/ha_xiaomi_home/pull/1237)
### Fixed
- Fix the reconnect delay time to be reset when the client is connected to the broker. [#1200](https://github.com/XiaoMi/ha_xiaomi_home/pull/1200)
- Fix the HA warning in the logs related to vacuum state setting. [#694](https://github.com/XiaoMi/ha_xiaomi_home/pull/694)
- Fix the operation mode when the device does not have a mode property. [#1199](https://github.com/XiaoMi/ha_xiaomi_home/pull/1199)
- Fix 090615.aircondition.ktf environment temperature. [#1210](https://github.com/XiaoMi/ha_xiaomi_home/pull/1210)
- Fix a missing variable in translation it.json. [#1215](https://github.com/XiaoMi/ha_xiaomi_home/pull/1215)
- Fix yutai.plug.fsov8m power consumption and ignore bjkcz.curtain.kczble curtain status. [#1236](https://github.com/XiaoMi/ha_xiaomi_home/pull/1236)
## v0.3.3
### Changed
- Change the log level of error "mips unsub internal error, 4, None". [#1135](https://github.com/XiaoMi/ha_xiaomi_home/pull/1135)
- Add necessary logs for distinguishing the set_properties command source. [#1160](https://github.com/XiaoMi/ha_xiaomi_home/pull/1160)
### Fixed
- Fix tofan.airrtc.wk01 thermostat and air conditioner service. [#1160](https://github.com/XiaoMi/ha_xiaomi_home/pull/1160)
- Fix mrbond.airer.m1t closing status. [#1134](https://github.com/XiaoMi/ha_xiaomi_home/pull/1134)

View File

@ -155,6 +155,8 @@ In MIoT-Spec-V2 protocol, a product is defined as a device. A device contains se
MIoT-Spec-V2 event is transformed to Event entity in Home Assistant. The event's parameters are also passed to entity's `_trigger_event`.
MIoT-Spec-V2 event's arguments field is the list of parameters of the event. The list elements represent the piid of the property in the same service. For example, the [MIoT-Spec-V2](http://poc.miot-spec.srv/miot-spec-v2/instance?type=urn:miot-spec-v2:device:remote-control:0000A021:xiaomi-mcn002:1:0000D057) of the Xiaomi Wireless Double-key Switch contains the siid=2 Switch Sensor service. The eiid=1014 Long Press event of the service is triggered when a button is long pressed. The debug level log will print `Press and hold, attributes: {'Button Type': 1}`. This is an example log that the button type is 1, which means the right button is long pressed.
- Action
| in | Entity in Home Assistant |
@ -289,39 +291,37 @@ The value of the event instance name indicates `_attr_device_class` of the Home
### MIoT-Spec-V2 Filter
`spec_filter.json` is used to filter out the MIoT-Spec-V2 instance that will not be converted to Home Assistant entity.
`spec_filter.yaml` is used to filter out the MIoT-Spec-V2 instance that will not be converted to Home Assistant entity.
The format of `spec_filter.json` is as follows.
```
{
"<MIoT-Spec-V2 device instance>":{
"services": list<service_iid: str>,
"properties": list<service_iid.property_iid: str>,
"events": list<service_iid.event_iid: str>,
"actions": list<service_iid.action_iid: str>,
}
}
```yaml
<MIoT-Spec-V2 device instance urn without the version field>:
services: list<service_iid: str>
properties: list<service_iid.property_iid: str>
events: list<service_iid.event_iid: str>
actions: list<service_iid.action_iid: str>
```
The key of `spec_filter.json` dictionary is the urn excluding the "version" field of the MIoT-Spec-V2 device instance. The firmware of different versions of the same product may be associated with the MIoT-Spec-V2 device instances of different versions. It is required that the MIoT-Spec-V2 instance of a higher version must contain all MIoT-Spec-V2 instances of the lower versions when a vendor defines the MIoT-Spec-V2 of its product on MIoT platform. Thus, the key of `spec_filter.json` does not need to specify the version number of MIoT-Spec-V2 device instance.
The key of `spec_filter.yaml` dictionary is the urn excluding the "version" field of the MIoT-Spec-V2 device instance. The firmware of different versions of the same product may be associated with the MIoT-Spec-V2 device instances of different versions. It is required that the MIoT-Spec-V2 instance of a higher version must contain all MIoT-Spec-V2 instances of the lower versions when a vendor defines the MIoT-Spec-V2 of its product on MIoT platform. Thus, the key of `spec_filter.yaml` does not need to specify the version number of MIoT-Spec-V2 device instance.
The value of "services", "properties", "events" or "actions" fields under "device instance" is the instance id (iid) of the service, property, event or action that will be ignored in the conversion process. Wildcard matching is supported.
Example:
```
{
"urn:miot-spec-v2:device:television:0000A010:xiaomi-rmi1":{
"services": ["*"] # Filter out all services. It is equivalent to completely ignoring the device with such MIoT-Spec-V2 device instance.
},
"urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1": {
"services": ["3"], # Filter out the service whose iid=3.
"properties": ["4.*"] # Filter out all properties in the service whose iid=4.
"events": ["4.1"], # Filter out the iid=1 event in the iid=4 service.
"actions": ["4.1"] # Filter out the iid=1 action in the iid=4 service.
}
}
```yaml
urn:miot-spec-v2:device:television:0000A010:xiaomi-rmi1:
services:
- '*' # Filter out all services. It is equivalent to completely ignoring the device with such MIoT-Spec-V2.
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:
services:
- '3' # Filter out the siid=3 service.
properties:
- '4.*' # Filter out all properties in the siid=4 service.
events:
- '4.1' # Filter out the eiid=1 event in the siid=4 service.
actions:
- '4.1' # Filter out the aiid=1 action in the siid=4 service.
```
Device information service (urn:miot-spec-v2:service:device-information:00007801) of all devices will never be converted to Home Assistant entity.
@ -376,7 +376,7 @@ Example:
}
```
> If you edit any files in the `custom_components/xiaomi_home/miot/specs` directory (`spec_filter.py`, `spec_modify.json`, `multi_lang.json`, etc.) in your Home Assistant, you need to update the entity conversion rule in the integration's CONFIGURE page to take effect. Method: [Settings > Devices & services > Configured > Xiaomi Home](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home) > CONFIGURE > Update entity conversion rules
> If you edit any files in the `custom_components/xiaomi_home/miot/specs` directory (`spec_filter.yaml`, `spec_modify.yaml`, `multi_lang.json`, etc.) in your Home Assistant, you need to update the entity conversion rule in the integration's CONFIGURE page to take effect. Method: [Settings > Devices & services > Configured > Xiaomi Home](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home) > CONFIGURE > Update entity conversion rules
## Documents

View File

@ -349,3 +349,101 @@ async def async_remove_config_entry_device(
_LOGGER.info(
'remove device, %s, %s', identifiers[1], device_entry.id)
return True
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"""Migrate old entry."""
_LOGGER.debug(
'Migrating configuration from version %s.%s',
config_entry.version,
config_entry.minor_version,
)
if config_entry.version > 1:
# This means the user has downgraded from a future version
return False
if config_entry.version == 1:
await _migrate_v1_to_v2(hass, config_entry)
_LOGGER.debug(
'Migration to configuration version %s.%s successful',
config_entry.version,
config_entry.minor_version,
)
return True
async def _migrate_v1_to_v2(hass: HomeAssistant, config_entry: ConfigEntry):
def ha_persistent_notify(
notify_id: str, title: Optional[str] = None,
message: Optional[str] = None
) -> None:
"""Send messages in Notifications dialog box."""
if title:
persistent_notification.async_create(
hass=hass, message=message or '',
title=title, notification_id=notify_id)
else:
persistent_notification.async_dismiss(
hass=hass, notification_id=notify_id)
entry_id = config_entry.entry_id
entry_data = dict(config_entry.data)
ha_persistent_notify(
notify_id=f'{entry_id}.oauth_error', title=None, message=None)
miot_client: MIoTClient = await get_miot_instance_async(
hass=hass, entry_id=entry_id,
entry_data=entry_data,
persistent_notify=ha_persistent_notify)
# Spec parser
spec_parser = MIoTSpecParser(
lang=entry_data.get(
'integration_language', DEFAULT_INTEGRATION_LANGUAGE),
storage=miot_client.miot_storage,
loop=miot_client.main_loop
)
await spec_parser.init_async()
# Manufacturer
manufacturer: DeviceManufacturer = DeviceManufacturer(
storage=miot_client.miot_storage,
loop=miot_client.main_loop)
await manufacturer.init_async()
er = entity_registry.async_get(hass)
for _, info in miot_client.device_list.items():
spec_instance = await spec_parser.parse(urn=info['urn'])
if not isinstance(spec_instance, MIoTSpecInstance):
continue
device: MIoTDevice = MIoTDevice(
miot_client=miot_client,
device_info={
**info, 'manufacturer': manufacturer.get_name(
info.get('manufacturer', ''))},
spec_instance=spec_instance)
device.spec_transform()
# Update unique_id
for platform, entities in device.entity_list.items():
for entity in entities:
if not isinstance(entity.spec, MIoTSpecService):
continue
old_unique_id = device.gen_service_entity_id_v1(
ha_domain=DOMAIN,
siid=entity.spec.iid,
)
entity_id = er.async_get_entity_id(
platform, DOMAIN, old_unique_id
)
if entity_id is None:
continue
new_unique_id = device.gen_service_entity_id(
ha_domain=DOMAIN,
siid=entity.spec.iid,
description=entity.spec.description,
)
er.async_update_entity(entity_id, new_unique_id=new_unique_id)
hass.config_entries.async_update_entry(config_entry, version=2)

View File

@ -105,7 +105,7 @@ _LOGGER = logging.getLogger(__name__)
class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Xiaomi Home config flow."""
# pylint: disable=unused-argument, inconsistent-quotes
VERSION = 1
VERSION = 2
MINOR_VERSION = 1
DEFAULT_AREA_NAME_RULE = 'room'
_main_loop: asyncio.AbstractEventLoop

View File

@ -46,8 +46,9 @@ off Xiaomi or its affiliates' products.
Cover entities for Xiaomi Home.
"""
from __future__ import annotations
import logging
from typing import Any, Optional
import re
import logging
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -97,7 +98,6 @@ class Cover(MIoTServiceEntity, CoverEntity):
_prop_status: Optional[MIoTSpecProperty]
_prop_status_opening: Optional[list[int]]
_prop_status_closing: Optional[list[int]]
_prop_status_stop: Optional[list[int]]
_prop_status_closed: Optional[list[int]]
_prop_current_position: Optional[MIoTSpecProperty]
_prop_target_position: Optional[MIoTSpecProperty]
@ -122,7 +122,6 @@ class Cover(MIoTServiceEntity, CoverEntity):
self._prop_status = None
self._prop_status_opening = []
self._prop_status_closing = []
self._prop_status_stop = []
self._prop_status_closed = []
self._prop_current_position = None
self._prop_target_position = None
@ -159,13 +158,21 @@ class Cover(MIoTServiceEntity, CoverEntity):
self.entity_id)
continue
for item in prop.value_list.items:
if item.name in {'opening', 'open', 'up'}:
item_str: str = item.name
item_name: str = re.sub(r'[^a-z]', '', item_str)
if item_name in {
'opening', 'open', 'up', 'uping', 'rise', 'rising'
}:
self._prop_status_opening.append(item.value)
elif item.name in {'closing', 'close', 'down', 'dowm'}:
elif item_name in {
'closing', 'close', 'down', 'dowm', 'falling',
'dropping', 'downing', 'lower'
}:
self._prop_status_closing.append(item.value)
elif item.name in {'stop', 'stopped', 'pause'}:
self._prop_status_stop.append(item.value)
elif item.name in {'closed'}:
elif item_name in {
'stopatlowest', 'stoplowerlimit', 'lowerlimitstop',
'floor', 'lowerlimit'
}:
self._prop_status_closed.append(item.value)
self._prop_status = prop
elif prop.name == 'current-position':

View File

@ -25,7 +25,7 @@
"cryptography",
"psutil"
],
"version": "v0.3.3",
"version": "v0.3.4",
"zeroconf": [
"_miot-central._tcp.local."
]

View File

@ -345,6 +345,11 @@ class MIoTDevice:
f'{ha_domain}.{self._model_strs[0][:9]}_{self.did_tag}_'
f'{self._model_strs[-1][:20]}')
def gen_service_entity_id_v1(self, ha_domain: str, siid: int) -> str:
return (
f'{ha_domain}.{self._model_strs[0][:9]}_{self.did_tag}_'
f'{self._model_strs[-1][:20]}_s_{siid}')
def gen_service_entity_id(self, ha_domain: str, siid: int,
description: str) -> str:
return (

View File

@ -1178,7 +1178,7 @@ class MipsLocalClient(_MipsClient):
or 'piid' not in msg
or 'value' not in msg
):
# self.log_error(f'on_prop_msg, recv unknown msg, {payload}')
self.log_info('unknown prop msg, %s', payload)
return
if handler:
self.log_debug('local, on properties_changed, %s', payload)

View File

@ -75,6 +75,9 @@ urn:miot-spec-v2:device:bath-heater:0000A028:opple-acmoto:1:
urn:miot-spec-v2:device:bath-heater:0000A028:xiaomi-s1:1:
prop.4.4:
name: fan-level-ventilation
urn:miot-spec-v2:device:curtain:0000A00C:bjkcz-kczble:1:0000D031:
prop.2.2:
name: status-a
urn:miot-spec-v2:device:fan:0000A005:dmaker-p33:1:
prop.2.2:
name: fan-level-a
@ -209,6 +212,9 @@ urn:miot-spec-v2:device:outlet:0000A002:qmi-psv3:1:0000C816:
unit: mV
prop.3.4:
unit: mA
urn:miot-spec-v2:device:outlet:0000A002:yutai-fsov8m:1:0000C816:
prop.4.1:
expr: round(src_value/10000, 2)
urn:miot-spec-v2:device:outlet:0000A002:zimi-zncz01:1:0000C816: urn:miot-spec-v2:device:outlet:0000A002:zimi-zncz01:2:0000C816
urn:miot-spec-v2:device:outlet:0000A002:zimi-zncz01:2:0000C816:
prop.3.1:

View File

@ -157,6 +157,8 @@ git checkout v1.0.0
转换后的实体为 Event事件参数同时传递给实体的 `_trigger_event`
MIoT-Spec-V2 事件的 arguments 字段是事件的参数列表,列表元素代表同服务下属性的 piid 。例如,小米智能无线开关(双开)的 [MIoT-Spec-V2](http://poc.miot-spec.srv/miot-spec-v2/instance?type=urn:miot-spec-v2:device:remote-control:0000A021:xiaomi-mcn002:1:0000D057)的 siid=2 无线开关服务下包含 eiid=1014 长按事件,该事件触发时会携带一个 piid=2 的按键类型属性作为事件参数, debug 等级日志会打印 `长按, attributes: {'按键类型': 1}` (日志示例,按键类型为 1 表示右键触发了长按事件)。
- 方法Action
| in输入参数列表 | 转换后的实体 |
@ -291,39 +293,37 @@ event instance name 下的值表示转换后实体所用的 `_attr_device_class`
### MIoT-Spec-V2 过滤规则
`spec_filter.json` 用于过滤掉不需要的 MIoT-Spec-V2 实例,过滤掉的实例不会转换成 Home Assistant 实体。
`spec_filter.yaml` 用于过滤掉不需要的 MIoT-Spec-V2 实例,过滤掉的实例不会转换成 Home Assistant 实体。
`spec_filter.json`的格式如下:
`spec_filter.yaml`的格式如下:
```
{
"<MIoT-Spec-V2 device instance>":{
"services": list<service_iid: str>,
"properties": list<service_iid.property_iid: str>,
"events": list<service_iid.event_iid: str>,
"actions": list<service_iid.action_iid: str>,
}
}
```yaml
<MIoT-Spec-V2 device instance urn without the version field>:
services: list<service_iid: str>
properties: list<service_iid.property_iid: str>
events: list<service_iid.event_iid: str>
actions: list<service_iid.action_iid: str>
```
`spec_filter.json` 的键值为 MIoT-Spec-V2 设备实例的 urn 不含版本号“version”字段。一个产品的不同版本的固件可能会关联不同版本的 MIoT-Spec-V2 设备实例。 MIoT 平台要求厂商定义产品的 MIoT-Spec-V2 时,高版本的 MIoT-Spec-V2 实例必须包含全部低版本的 MIoT-Spec-V2 实例。因此, `spec_filter.json` 的键值不需要指定设备实例的版本号。
`spec_filter.yaml` 的键值为 MIoT-Spec-V2 设备实例的 urn 不含版本号“version”字段。一个产品的不同版本的固件可能会关联不同版本的 MIoT-Spec-V2 设备实例。 MIoT 平台要求厂商定义产品的 MIoT-Spec-V2 时,高版本的 MIoT-Spec-V2 实例必须包含全部低版本的 MIoT-Spec-V2 实例。因此, `spec_filter.yaml` 的键值不需要指定设备实例的版本号。
设备实例下的 services 、 properties 、 events 、 actions 域的值表示需要过滤掉的服务、属性、事件、方法的实例号( iid ,即 instance id )。支持通配符匹配。
示例:
```
{
"urn:miot-spec-v2:device:television:0000A010:xiaomi-rmi1":{
"services": ["*"] # Filter out all services. It is equivalent to completely ignoring the device with such MIoT-Spec-V2 device instance.
},
"urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1": {
"services": ["3"], # Filter out the service whose iid=3.
"properties": ["4.*"] # Filter out all properties in the service whose iid=4.
"events": ["4.1"], # Filter out the iid=1 event in the iid=4 service.
"actions": ["4.1"] # Filter out the iid=1 action in the iid=4 service.
}
}
```yaml
urn:miot-spec-v2:device:television:0000A010:xiaomi-rmi1:
services:
- '*' # 排除所有服务,相当于排除拥有该 MIoT-Spec-V2 的设备。
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:
services:
- '3' # 排除 siid=3 的服务。
properties:
- '4.*' # 排除 siid=4 服务的所有属性。
events:
- '4.1' # 排除 siid=4 服务的 eiid=1 的事件。
actions:
- '4.1' # 排除 siid=4 服务的 aiid=1 的方法。
```
所有设备的设备信息服务( urn:miot-spec-v2:service:device-information:00007801 )均不会生成 Home Assistant 实体。
@ -378,7 +378,7 @@ siid、piid、eiid、aiid、value 均为十进制三位整数。
}
```
> 在 Home Assistant 中修改了 `custom_components/xiaomi_home/miot/specs` 路径下的任何文件(`spec_filter.py`、`spec_modify.json`、`multi_lang.json`等),需要在集成配置中更新实体转换规则才能生效。方法:[设置 > 设备与服务 > 已配置 > Xiaomi Home](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home) > 配置 > 更新实体转换规则
> 在 Home Assistant 中修改了 `custom_components/xiaomi_home/miot/specs` 路径下的任何文件(`spec_filter.yaml`、`spec_modify.yaml`、`multi_lang.json`等),需要在集成配置中更新实体转换规则才能生效。方法:[设置 > 设备与服务 > 已配置 > Xiaomi Home](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home) > 配置 > 更新实体转换规则
## 文档