From 7c0caa9df75cddadeae9ab8051cb39b71d41a5be Mon Sep 17 00:00:00 2001 From: caibinqing Date: Sat, 5 Apr 2025 19:08:43 +0800 Subject: [PATCH 1/2] fix: add migration step for config entry --- custom_components/xiaomi_home/__init__.py | 96 +++++++++++++++++++ custom_components/xiaomi_home/config_flow.py | 2 +- .../xiaomi_home/miot/miot_device.py | 5 + 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_home/__init__.py b/custom_components/xiaomi_home/__init__.py index b26cf0a..3b156d9 100644 --- a/custom_components/xiaomi_home/__init__.py +++ b/custom_components/xiaomi_home/__init__.py @@ -349,3 +349,99 @@ 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 did, 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 in device.entity_list: + for entity in device.entity_list[platform]: + 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) diff --git a/custom_components/xiaomi_home/config_flow.py b/custom_components/xiaomi_home/config_flow.py index 7c0d20a..da15c85 100644 --- a/custom_components/xiaomi_home/config_flow.py +++ b/custom_components/xiaomi_home/config_flow.py @@ -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 diff --git a/custom_components/xiaomi_home/miot/miot_device.py b/custom_components/xiaomi_home/miot/miot_device.py index fafff87..fb8d318 100644 --- a/custom_components/xiaomi_home/miot/miot_device.py +++ b/custom_components/xiaomi_home/miot/miot_device.py @@ -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 ( From 506bd9f52ecc9c831f1ae8f2e96b2a74bc1d1c67 Mon Sep 17 00:00:00 2001 From: caibinqing Date: Mon, 7 Apr 2025 11:19:19 +0800 Subject: [PATCH 2/2] fix pylint --- custom_components/xiaomi_home/__init__.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/custom_components/xiaomi_home/__init__.py b/custom_components/xiaomi_home/__init__.py index 3b156d9..3f1ddfb 100644 --- a/custom_components/xiaomi_home/__init__.py +++ b/custom_components/xiaomi_home/__init__.py @@ -354,7 +354,7 @@ async def async_remove_config_entry_device( async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry): """Migrate old entry.""" _LOGGER.debug( - "Migrating configuration from version %s.%s", + 'Migrating configuration from version %s.%s', config_entry.version, config_entry.minor_version, ) @@ -367,7 +367,7 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry): await _migrate_v1_to_v2(hass, config_entry) _LOGGER.debug( - "Migration to configuration version %s.%s successful", + 'Migration to configuration version %s.%s successful', config_entry.version, config_entry.minor_version, ) @@ -413,8 +413,8 @@ async def _migrate_v1_to_v2(hass: HomeAssistant, config_entry: ConfigEntry): loop=miot_client.main_loop) await manufacturer.init_async() er = entity_registry.async_get(hass) - for did, info in miot_client.device_list.items(): - spec_instance = await spec_parser.parse(urn=info["urn"]) + 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( @@ -426,15 +426,17 @@ async def _migrate_v1_to_v2(hass: HomeAssistant, config_entry: ConfigEntry): device.spec_transform() # Update unique_id - for platform in device.entity_list: - for entity in device.entity_list[platform]: + 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) + 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(