mirror of
https://github.com/home-assistant/core.git
synced 2025-11-09 19:09:32 +00:00
Automatically update the entity propery when a member created, updated or deleted
This commit is contained in:
@@ -33,7 +33,13 @@ from homeassistant.const import (
|
||||
CONF_URL,
|
||||
CONF_VALUE_TEMPLATE,
|
||||
)
|
||||
from homeassistant.core import Event, HassJobType, HomeAssistant, callback
|
||||
from homeassistant.core import (
|
||||
CALLBACK_TYPE,
|
||||
Event,
|
||||
HassJobType,
|
||||
HomeAssistant,
|
||||
callback,
|
||||
)
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.device_registry import (
|
||||
DeviceEntry,
|
||||
@@ -476,12 +482,41 @@ class MqttAttributesMixin(Entity):
|
||||
[MessageCallbackType, set[str] | None, ReceiveMessage], None
|
||||
]
|
||||
_process_update_extra_state_attributes: Callable[[dict[str, Any]], None]
|
||||
_monitor_member_updates_callback: CALLBACK_TYPE
|
||||
|
||||
def __init__(self, config: ConfigType) -> None:
|
||||
"""Initialize the JSON attributes mixin."""
|
||||
self._attributes_sub_state: dict[str, EntitySubscription] = {}
|
||||
self._attributes_config = config
|
||||
|
||||
def _monitor_member_updates(self) -> None:
|
||||
"""Update the group members if the entity registry is updated."""
|
||||
entity_registry = er.async_get(self.hass)
|
||||
|
||||
async def _handle_entity_registry_updated(event: Event[Any]) -> None:
|
||||
"""Handle registry update event."""
|
||||
if (
|
||||
event.data["action"] in {"create", "update"}
|
||||
and (entry := entity_registry.async_get(event.data["entity_id"]))
|
||||
and entry.unique_id in self._attributes_config[CONF_GROUP]
|
||||
) or (
|
||||
event.data["action"] == "remove"
|
||||
and self._group_entity_ids is not None
|
||||
and event.data["entity_id"] in self._group_entity_ids
|
||||
):
|
||||
self._update_group_entity_ids()
|
||||
self._attr_extra_state_attributes[ATTR_ENTITY_ID] = (
|
||||
self._group_entity_ids
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
self.async_on_remove(
|
||||
self.hass.bus.async_listen(
|
||||
er.EVENT_ENTITY_REGISTRY_UPDATED,
|
||||
_handle_entity_registry_updated,
|
||||
)
|
||||
)
|
||||
|
||||
def _update_group_entity_ids(self) -> None:
|
||||
"""Set the entity_id property if the entity represents a group of entities.
|
||||
|
||||
@@ -506,6 +541,7 @@ class MqttAttributesMixin(Entity):
|
||||
await super().async_added_to_hass()
|
||||
self._update_group_entity_ids()
|
||||
if self._group_entity_ids is not None:
|
||||
self._monitor_member_updates()
|
||||
self._attr_extra_state_attributes = {ATTR_ENTITY_ID: self._group_entity_ids}
|
||||
|
||||
self._attributes_prepare_subscribe_topics()
|
||||
|
||||
@@ -101,6 +101,7 @@ from homeassistant.const import (
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util.json import json_loads
|
||||
|
||||
from .common import (
|
||||
@@ -1893,7 +1894,7 @@ async def test_white_scale(
|
||||
assert state.attributes.get("brightness") == 129
|
||||
|
||||
|
||||
async def test_light_group_discovery_members(
|
||||
async def test_light_group_discovery_members_before_group(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
"""Test the discovery of a light group and linked entity IDs.
|
||||
@@ -1919,6 +1920,62 @@ async def test_light_group_discovery_members(
|
||||
assert group_state.attributes.get("icon") == "mdi:lightbulb-group"
|
||||
|
||||
|
||||
async def test_light_group_discovery_group_before_members(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test the discovery of a light group and linked entity IDs.
|
||||
|
||||
The group is discovered first, so the group members are
|
||||
not (all) known yet in the entity registry.
|
||||
The entity property should be updates as soon as member entities
|
||||
are discovered, updated or removed.
|
||||
"""
|
||||
await mqtt_mock_entry()
|
||||
|
||||
# Discover group
|
||||
async_fire_mqtt_message(hass, GROUP_TOPIC, GROUP_DISCOVERY_LIGHT_GROUP_CONFIG)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Discover light group members
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_1_TOPIC, GROUP_DISCOVERY_MEMBER_1_CONFIG)
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_2_TOPIC, GROUP_DISCOVERY_MEMBER_2_CONFIG)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("light.member1") is not None
|
||||
assert hass.states.get("light.member2") is not None
|
||||
|
||||
group_state = hass.states.get("light.group")
|
||||
assert group_state is not None
|
||||
assert group_state.attributes.get("entity_id") == ["light.member1", "light.member2"]
|
||||
assert group_state.attributes.get("icon") == "mdi:lightbulb-group"
|
||||
|
||||
# Remove member 1
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_1_TOPIC, "")
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("light.member1") is None
|
||||
assert hass.states.get("light.member2") is not None
|
||||
|
||||
group_state = hass.states.get("light.group")
|
||||
assert group_state is not None
|
||||
assert group_state.attributes.get("entity_id") == ["light.member2"]
|
||||
|
||||
# Rename member 2
|
||||
entity_registry.async_update_entity(
|
||||
"light.member2", new_entity_id="light.member2_updated"
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
group_state = hass.states.get("light.group")
|
||||
assert group_state is not None
|
||||
assert group_state.attributes.get("entity_id") == ["light.member2_updated"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user