Compare commits

...

8 Commits

Author SHA1 Message Date
Necroneco
6041e173bf
Merge 506bd9f52e into 0f65635342 2025-08-20 11:05:08 +08:00
Li Shuzhen
0f65635342
docs: update changelog and version to v0.4.2 (#1368)
Some checks failed
Tests / check-rule-format (push) Has been cancelled
Validate / validate-hassfest (push) Has been cancelled
Validate / validate-hacs (push) Has been cancelled
Validate / validate-lint (push) Has been cancelled
Validate / validate-setup (push) Has been cancelled
2025-08-20 09:15:31 +08:00
Li Shuzhen
947169f18d
Fix specs (#1367)
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
* 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
2025-08-19 17:49:27 +08:00
Li Shuzhen
65a7a6d22a
fix: correct the property value format after expression calculation (#1366) 2025-08-19 14:36:30 +08:00
Li Shuzhen
c29f7eecbd
fix: delete all unsupported MIoT-Spec-V2 instances of Narwa vacuum (#1355) 2025-08-19 14:12:35 +08:00
Brandon Chen
58c671483e
fix: add RETURN_HOME functionality for new vacuum cleaner model (#1344)
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-08-19 10:15:01 +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
12 changed files with 1583 additions and 4 deletions

View File

@ -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)

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

@ -108,7 +108,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

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

View File

@ -89,6 +89,7 @@ SUPPORTED_PLATFORMS: list = [
UNSUPPORTED_MODELS: list = [
'chuangmi.ir.v2',
'hmpace.motion.v6nfc',
'xiaomi.router.rd03'
]

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 (
@ -1108,6 +1113,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 +1286,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()

View File

@ -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()

File diff suppressed because it is too large Load Diff

View File

@ -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:
- '*'

View File

@ -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

View File

@ -170,6 +170,11 @@ SPEC_DEVICE_TRANS_MAP: dict = {
'properties': {
'battery-level': {'read'}
}
},
'optional': {
'actions': {
'start-charge'
}
}
}
},

View File

@ -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: