mirror of
https://github.com/home-assistant/core.git
synced 2025-11-11 20:10:12 +00:00
Rework with mixin - Light only
This commit is contained in:
@@ -15,6 +15,7 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
SERVICE_TOGGLE,
|
SERVICE_TOGGLE,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
@@ -1336,6 +1337,9 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||||||
if color_mode:
|
if color_mode:
|
||||||
data.update(self._light_internal_convert_color(color_mode))
|
data.update(self._light_internal_convert_color(color_mode))
|
||||||
|
|
||||||
|
if included_entities := getattr(self, "included_entities", None):
|
||||||
|
data[ATTR_ENTITY_ID] = included_entities
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import voluptuous as vol
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_CONFIGURATION_URL,
|
ATTR_CONFIGURATION_URL,
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
ATTR_HW_VERSION,
|
ATTR_HW_VERSION,
|
||||||
ATTR_MANUFACTURER,
|
ATTR_MANUFACTURER,
|
||||||
ATTR_MODEL,
|
ATTR_MODEL,
|
||||||
@@ -33,13 +32,7 @@ from homeassistant.const import (
|
|||||||
CONF_URL,
|
CONF_URL,
|
||||||
CONF_VALUE_TEMPLATE,
|
CONF_VALUE_TEMPLATE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import (
|
from homeassistant.core import Event, HassJobType, HomeAssistant, callback
|
||||||
CALLBACK_TYPE,
|
|
||||||
Event,
|
|
||||||
HassJobType,
|
|
||||||
HomeAssistant,
|
|
||||||
callback,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.device_registry import (
|
from homeassistant.helpers.device_registry import (
|
||||||
DeviceEntry,
|
DeviceEntry,
|
||||||
@@ -50,7 +43,11 @@ from homeassistant.helpers.dispatcher import (
|
|||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
from homeassistant.helpers.entity import (
|
||||||
|
Entity,
|
||||||
|
IncludedEntitiesMixin,
|
||||||
|
async_generate_entity_id,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
from homeassistant.helpers.event import (
|
from homeassistant.helpers.event import (
|
||||||
async_track_device_registry_updated_event,
|
async_track_device_registry_updated_event,
|
||||||
@@ -471,78 +468,28 @@ def async_setup_entity_entry_helper(
|
|||||||
_async_setup_entities()
|
_async_setup_entities()
|
||||||
|
|
||||||
|
|
||||||
class MqttAttributesMixin(Entity):
|
class MqttAttributesMixin(IncludedEntitiesMixin):
|
||||||
"""Mixin used for platforms that support JSON attributes."""
|
"""Mixin used for platforms that support JSON attributes."""
|
||||||
|
|
||||||
_attributes_extra_blocked: frozenset[str] = frozenset()
|
_attributes_extra_blocked: frozenset[str] = frozenset()
|
||||||
_attr_tpl: Callable[[ReceivePayloadType], ReceivePayloadType] | None = None
|
_attr_tpl: Callable[[ReceivePayloadType], ReceivePayloadType] | None = None
|
||||||
_default_group_icon: str | None = None
|
|
||||||
_group_entity_ids: list[str] | None = None
|
|
||||||
_message_callback: Callable[
|
_message_callback: Callable[
|
||||||
[MessageCallbackType, set[str] | None, ReceiveMessage], None
|
[MessageCallbackType, set[str] | None, ReceiveMessage], None
|
||||||
]
|
]
|
||||||
_process_update_extra_state_attributes: Callable[[dict[str, Any]], None]
|
_process_update_extra_state_attributes: Callable[[dict[str, Any]], None]
|
||||||
_monitor_member_updates_callback: CALLBACK_TYPE
|
|
||||||
|
|
||||||
def __init__(self, config: ConfigType) -> None:
|
def __init__(self, config: ConfigType) -> None:
|
||||||
"""Initialize the JSON attributes mixin."""
|
"""Initialize the JSON attributes mixin."""
|
||||||
self._attributes_sub_state: dict[str, EntitySubscription] = {}
|
self._attributes_sub_state: dict[str, EntitySubscription] = {}
|
||||||
self._attributes_config = config
|
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.
|
|
||||||
|
|
||||||
Setting entity_id in the extra state attributes will show the discovered entity
|
|
||||||
as a group and will show the member entities in the UI.
|
|
||||||
"""
|
|
||||||
if CONF_GROUP not in self._attributes_config:
|
|
||||||
self._default_entity_icon = None
|
|
||||||
return
|
|
||||||
self._attr_icon = self._attr_icon or self._default_group_icon
|
|
||||||
entity_registry = er.async_get(self.hass)
|
|
||||||
|
|
||||||
self._group_entity_ids = []
|
|
||||||
for resource_id in self._attributes_config[CONF_GROUP]:
|
|
||||||
if entity_id := entity_registry.async_get_entity_id(
|
|
||||||
self.entity_id.split(".")[0], DOMAIN, resource_id
|
|
||||||
):
|
|
||||||
self._group_entity_ids.append(entity_id)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Subscribe MQTT events."""
|
"""Subscribe MQTT events."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
self._update_group_entity_ids()
|
if CONF_GROUP in self._attributes_config:
|
||||||
if self._group_entity_ids is not None:
|
self.async_set_included_entities(
|
||||||
self._monitor_member_updates()
|
DOMAIN, self._attributes_config[CONF_GROUP]
|
||||||
self._attr_extra_state_attributes = {ATTR_ENTITY_ID: self._group_entity_ids}
|
)
|
||||||
|
|
||||||
self._attributes_prepare_subscribe_topics()
|
self._attributes_prepare_subscribe_topics()
|
||||||
self._attributes_subscribe_topics()
|
self._attributes_subscribe_topics()
|
||||||
@@ -616,8 +563,6 @@ class MqttAttributesMixin(Entity):
|
|||||||
if k not in MQTT_ATTRIBUTES_BLOCKED
|
if k not in MQTT_ATTRIBUTES_BLOCKED
|
||||||
and k not in self._attributes_extra_blocked
|
and k not in self._attributes_extra_blocked
|
||||||
}
|
}
|
||||||
if self._group_entity_ids is not None:
|
|
||||||
filtered_dict[ATTR_ENTITY_ID] = self._group_entity_ids
|
|
||||||
if hasattr(self, "_process_update_extra_state_attributes"):
|
if hasattr(self, "_process_update_extra_state_attributes"):
|
||||||
self._process_update_extra_state_attributes(filtered_dict)
|
self._process_update_extra_state_attributes(filtered_dict)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -239,7 +239,6 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
"""Representation of a MQTT light."""
|
"""Representation of a MQTT light."""
|
||||||
|
|
||||||
_default_name = DEFAULT_NAME
|
_default_name = DEFAULT_NAME
|
||||||
_default_group_icon = "mdi:lightbulb-group"
|
|
||||||
_entity_id_format = ENTITY_ID_FORMAT
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
||||||
_topic: dict[str, str | None]
|
_topic: dict[str, str | None]
|
||||||
|
|||||||
@@ -164,7 +164,6 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
"""Representation of a MQTT JSON light."""
|
"""Representation of a MQTT JSON light."""
|
||||||
|
|
||||||
_default_name = DEFAULT_NAME
|
_default_name = DEFAULT_NAME
|
||||||
_default_group_icon = "mdi:lightbulb-group"
|
|
||||||
_entity_id_format = ENTITY_ID_FORMAT
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
|
|||||||
"""Representation of a MQTT Template light."""
|
"""Representation of a MQTT Template light."""
|
||||||
|
|
||||||
_default_name = DEFAULT_NAME
|
_default_name = DEFAULT_NAME
|
||||||
_default_group_icon = "mdi:lightbulb-group"
|
|
||||||
_entity_id_format = ENTITY_ID_FORMAT
|
_entity_id_format = ENTITY_ID_FORMAT
|
||||||
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
_attributes_extra_blocked = MQTT_LIGHT_ATTRIBUTES_BLOCKED
|
||||||
_optimistic: bool
|
_optimistic: bool
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from propcache.api import cached_property
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ( # noqa: F401
|
from homeassistant.const import ( # noqa: F401
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
CONF_UNIT_OF_MEASUREMENT,
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
EntityCategory,
|
EntityCategory,
|
||||||
|
|||||||
@@ -1917,7 +1917,6 @@ async def test_light_group_discovery_members_before_group(
|
|||||||
group_state = hass.states.get("light.group")
|
group_state = hass.states.get("light.group")
|
||||||
assert group_state is not None
|
assert group_state is not None
|
||||||
assert group_state.attributes.get("entity_id") == ["light.member1", "light.member2"]
|
assert group_state.attributes.get("entity_id") == ["light.member1", "light.member2"]
|
||||||
assert group_state.attributes.get("icon") == "mdi:lightbulb-group"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_light_group_discovery_group_before_members(
|
async def test_light_group_discovery_group_before_members(
|
||||||
@@ -1950,7 +1949,6 @@ async def test_light_group_discovery_group_before_members(
|
|||||||
group_state = hass.states.get("light.group")
|
group_state = hass.states.get("light.group")
|
||||||
assert group_state is not None
|
assert group_state is not None
|
||||||
assert group_state.attributes.get("entity_id") == ["light.member1", "light.member2"]
|
assert group_state.attributes.get("entity_id") == ["light.member1", "light.member2"]
|
||||||
assert group_state.attributes.get("icon") == "mdi:lightbulb-group"
|
|
||||||
|
|
||||||
# Remove member 1
|
# Remove member 1
|
||||||
async_fire_mqtt_message(hass, GROUP_MEMBER_1_TOPIC, "")
|
async_fire_mqtt_message(hass, GROUP_MEMBER_1_TOPIC, "")
|
||||||
@@ -2209,7 +2207,6 @@ async def test_setting_attribute_via_mqtt_json_message_light_group(
|
|||||||
|
|
||||||
assert state and state.attributes.get("val") == "100"
|
assert state and state.attributes.get("val") == "100"
|
||||||
assert state.attributes.get("entity_id") == ["light.member_1", "light.member_2"]
|
assert state.attributes.get("entity_id") == ["light.member_1", "light.member_2"]
|
||||||
assert state.attributes.get("icon") == "mdi:lightbulb-group"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_setting_blocked_attribute_via_mqtt_json_message(
|
async def test_setting_blocked_attribute_via_mqtt_json_message(
|
||||||
|
|||||||
Reference in New Issue
Block a user