mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2026-10-27 16:10:49 +08:00
fix: use a valid entity_id with the platform domain (#1777)
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
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
Home Assistant reports the entity_id set by the integration as invalid when its domain is not the platform domain, or when the object_id contains characters that are not allowed, e.g. a MIoT-Spec-V2 description in Chinese or a description that is empty once slugified. Both are deprecated and stop working in 2027.5.0 and 2027.2.0. Derive the platform domain from the Home Assistant entity base class that each entity mixes in, and slugify the object_id. The unique_id keeps using the integration domain and the raw description, so it stays byte for byte identical for the entities that are already registered. The entity_id of an already registered entity is owned by the entity registry and is not changed either, and a newly registered entity gets the same entity_id as before, since the registry was already slugifying the object_id it was given. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: LiShuzhen <SusanPhevos@gmail.com>
This commit is contained in:
@@ -108,6 +108,32 @@ from .miot_spec import (
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def ha_entity_domain(entity: Entity) -> str:
|
||||||
|
"""Return the Home Assistant platform domain of an entity.
|
||||||
|
|
||||||
|
Every entity mixes in a Home Assistant entity base class, e.g.
|
||||||
|
Light(MIoTServiceEntity, LightEntity), which is declared in
|
||||||
|
homeassistant.components.<domain>. Fall back to the integration domain
|
||||||
|
when no such base class is found.
|
||||||
|
"""
|
||||||
|
for cls in type(entity).__mro__:
|
||||||
|
module = cls.__module__ or ''
|
||||||
|
if module.startswith('homeassistant.components.'):
|
||||||
|
return module.split('.')[2]
|
||||||
|
return DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
def to_valid_entity_id(entity_id: str) -> str:
|
||||||
|
"""Return a valid entity_id.
|
||||||
|
|
||||||
|
The object_id is built from the MIoT-Spec-V2 instance, which may contain
|
||||||
|
characters that are not allowed in an entity_id, e.g. a description in
|
||||||
|
Chinese or a trailing separator.
|
||||||
|
"""
|
||||||
|
domain, _, object_id = entity_id.partition('.')
|
||||||
|
return f'{domain}.{slugify_name(object_id)}'
|
||||||
|
|
||||||
|
|
||||||
class MIoTEntityData:
|
class MIoTEntityData:
|
||||||
"""MIoT Entity Data."""
|
"""MIoT Entity Data."""
|
||||||
platform: str
|
platform: str
|
||||||
@@ -933,19 +959,28 @@ class MIoTServiceEntity(Entity):
|
|||||||
self._state_sub_id = 0
|
self._state_sub_id = 0
|
||||||
self._value_sub_ids = {}
|
self._value_sub_ids = {}
|
||||||
# Gen entity id
|
# Gen entity id
|
||||||
|
# The unique_id keeps the integration domain so that it stays the same
|
||||||
|
# for the entities that are already registered, the entity_id uses the
|
||||||
|
# Home Assistant platform domain and must be a valid entity_id.
|
||||||
|
ha_domain = ha_entity_domain(self)
|
||||||
if isinstance(self.entity_data.spec, MIoTSpecInstance):
|
if isinstance(self.entity_data.spec, MIoTSpecInstance):
|
||||||
self.entity_id = miot_device.gen_device_entity_id(DOMAIN)
|
self._attr_unique_id = miot_device.gen_device_entity_id(DOMAIN)
|
||||||
|
self.entity_id = to_valid_entity_id(
|
||||||
|
miot_device.gen_device_entity_id(ha_domain))
|
||||||
self._attr_name = f' {self.entity_data.spec.description_trans}'
|
self._attr_name = f' {self.entity_data.spec.description_trans}'
|
||||||
elif isinstance(self.entity_data.spec, MIoTSpecService):
|
elif isinstance(self.entity_data.spec, MIoTSpecService):
|
||||||
self.entity_id = miot_device.gen_service_entity_id(
|
self._attr_unique_id = miot_device.gen_service_entity_id(
|
||||||
DOMAIN, siid=self.entity_data.spec.iid,
|
DOMAIN, siid=self.entity_data.spec.iid,
|
||||||
description=self.entity_data.spec.description)
|
description=self.entity_data.spec.description)
|
||||||
|
self.entity_id = to_valid_entity_id(
|
||||||
|
miot_device.gen_service_entity_id(
|
||||||
|
ha_domain, siid=self.entity_data.spec.iid,
|
||||||
|
description=self.entity_data.spec.description))
|
||||||
self._attr_name = (
|
self._attr_name = (
|
||||||
f'{"* "if self.entity_data.spec.proprietary else " "}'
|
f'{"* "if self.entity_data.spec.proprietary else " "}'
|
||||||
f'{self.entity_data.spec.description_trans}')
|
f'{self.entity_data.spec.description_trans}')
|
||||||
self._attr_entity_category = entity_data.spec.entity_category
|
self._attr_entity_category = entity_data.spec.entity_category
|
||||||
# Set entity attr
|
# Set entity attr
|
||||||
self._attr_unique_id = self.entity_id
|
|
||||||
self._attr_should_poll = False
|
self._attr_should_poll = False
|
||||||
self._attr_has_entity_name = True
|
self._attr_has_entity_name = True
|
||||||
self._attr_available = miot_device.online
|
self._attr_available = miot_device.online
|
||||||
@@ -1240,11 +1275,15 @@ class MIoTPropertyEntity(Entity):
|
|||||||
self._value_sub_id = 0
|
self._value_sub_id = 0
|
||||||
self._pending_write_ha_state_timer = None
|
self._pending_write_ha_state_timer = None
|
||||||
# Gen entity_id
|
# Gen entity_id
|
||||||
self.entity_id = self.miot_device.gen_prop_entity_id(
|
# The unique_id keeps the integration domain, see MIoTServiceEntity
|
||||||
|
self._attr_unique_id = self.miot_device.gen_prop_entity_id(
|
||||||
ha_domain=DOMAIN, spec_name=spec.name,
|
ha_domain=DOMAIN, spec_name=spec.name,
|
||||||
siid=spec.service.iid, piid=spec.iid)
|
siid=spec.service.iid, piid=spec.iid)
|
||||||
|
self.entity_id = to_valid_entity_id(
|
||||||
|
self.miot_device.gen_prop_entity_id(
|
||||||
|
ha_domain=ha_entity_domain(self), spec_name=spec.name,
|
||||||
|
siid=spec.service.iid, piid=spec.iid))
|
||||||
# Set entity attr
|
# Set entity attr
|
||||||
self._attr_unique_id = self.entity_id
|
|
||||||
self._attr_should_poll = False
|
self._attr_should_poll = False
|
||||||
self._attr_has_entity_name = True
|
self._attr_has_entity_name = True
|
||||||
self._attr_name = (
|
self._attr_name = (
|
||||||
@@ -1382,11 +1421,15 @@ class MIoTEventEntity(Entity):
|
|||||||
self.service = spec.service
|
self.service = spec.service
|
||||||
self._main_loop = miot_device.miot_client.main_loop
|
self._main_loop = miot_device.miot_client.main_loop
|
||||||
# Gen entity_id
|
# Gen entity_id
|
||||||
self.entity_id = self.miot_device.gen_event_entity_id(
|
# The unique_id keeps the integration domain, see MIoTServiceEntity
|
||||||
|
self._attr_unique_id = self.miot_device.gen_event_entity_id(
|
||||||
ha_domain=DOMAIN, spec_name=spec.name,
|
ha_domain=DOMAIN, spec_name=spec.name,
|
||||||
siid=spec.service.iid, eiid=spec.iid)
|
siid=spec.service.iid, eiid=spec.iid)
|
||||||
|
self.entity_id = to_valid_entity_id(
|
||||||
|
self.miot_device.gen_event_entity_id(
|
||||||
|
ha_domain=ha_entity_domain(self), spec_name=spec.name,
|
||||||
|
siid=spec.service.iid, eiid=spec.iid))
|
||||||
# Set entity attr
|
# Set entity attr
|
||||||
self._attr_unique_id = self.entity_id
|
|
||||||
self._attr_should_poll = False
|
self._attr_should_poll = False
|
||||||
self._attr_has_entity_name = True
|
self._attr_has_entity_name = True
|
||||||
self._attr_name = (
|
self._attr_name = (
|
||||||
@@ -1493,11 +1536,15 @@ class MIoTActionEntity(Entity):
|
|||||||
self._main_loop = miot_device.miot_client.main_loop
|
self._main_loop = miot_device.miot_client.main_loop
|
||||||
self._state_sub_id = 0
|
self._state_sub_id = 0
|
||||||
# Gen entity_id
|
# Gen entity_id
|
||||||
self.entity_id = self.miot_device.gen_action_entity_id(
|
# The unique_id keeps the integration domain, see MIoTServiceEntity
|
||||||
|
self._attr_unique_id = self.miot_device.gen_action_entity_id(
|
||||||
ha_domain=DOMAIN, spec_name=spec.name,
|
ha_domain=DOMAIN, spec_name=spec.name,
|
||||||
siid=spec.service.iid, aiid=spec.iid)
|
siid=spec.service.iid, aiid=spec.iid)
|
||||||
|
self.entity_id = to_valid_entity_id(
|
||||||
|
self.miot_device.gen_action_entity_id(
|
||||||
|
ha_domain=ha_entity_domain(self), spec_name=spec.name,
|
||||||
|
siid=spec.service.iid, aiid=spec.iid))
|
||||||
# Set entity attr
|
# Set entity attr
|
||||||
self._attr_unique_id = self.entity_id
|
|
||||||
self._attr_should_poll = False
|
self._attr_should_poll = False
|
||||||
self._attr_has_entity_name = True
|
self._attr_has_entity_name = True
|
||||||
self._attr_name = (
|
self._attr_name = (
|
||||||
|
|||||||
Reference in New Issue
Block a user