From 2b1660c0f72f90b87d5f9d3815305f0a25969f3e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 20 Jun 2023 02:45:30 -0500 Subject: [PATCH] Dispatch when esphome static info changes (#94876) --- homeassistant/components/esphome/__init__.py | 53 +++++++++++++------ .../components/esphome/entry_data.py | 4 ++ homeassistant/components/esphome/number.py | 8 +-- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 70c026614c6..d774a1fc663 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -45,7 +45,10 @@ from homeassistant.helpers import template import homeassistant.helpers.config_validation as cv import homeassistant.helpers.device_registry as dr from homeassistant.helpers.device_registry import format_mac -from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_state_change_event @@ -709,7 +712,7 @@ async def platform_async_setup_entry( old_infos.pop(info.key) else: # Create new entity - entity = entity_type(entry_data, component_key, info.key, state_type) + entity = entity_type(entry_data, component_key, info, state_type) add_entities.append(entity) new_infos[info.key] = info @@ -722,8 +725,15 @@ async def platform_async_setup_entry( # Then update the actual info entry_data.info[component_key] = new_infos - # Add entities to Home Assistant - async_add_entities(add_entities) + async_dispatcher_send( + hass, + entry_data.signal_component_static_info_updated(component_key), + new_infos, + ) + + if add_entities: + # Add entities to Home Assistant + async_add_entities(add_entities) entry_data.cleanup_callbacks.append( async_dispatcher_connect( @@ -774,18 +784,20 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]): """Define a base esphome entity.""" _attr_should_poll = False + _static_info: _InfoT def __init__( self, entry_data: RuntimeEntryData, component_key: str, - key: int, + entity_info: EntityInfo, state_type: type[_StateT], ) -> None: """Initialize.""" self._entry_data = entry_data self._component_key = component_key - self._key = key + self._key = entity_info.key + self._static_info = cast(_InfoT, entity_info) self._state_type = state_type if entry_data.device_info is not None and entry_data.device_info.friendly_name: self._attr_has_entity_name = True @@ -814,6 +826,25 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]): ) ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self._entry_data.signal_component_static_info_updated( + self._component_key + ), + self._on_static_info_update, + ) + ) + + @callback + def _on_static_info_update(self, static_infos: dict[int, EntityInfo]) -> None: + """Save the static info for this entity when it changes. + + This method can be overridden in child classes to know + when the static info changes. + """ + self._static_info = cast(_InfoT, static_infos[self._key]) + @callback def _on_state_update(self) -> None: # Behavior can be changed in child classes @@ -837,16 +868,6 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]): def _api_version(self) -> APIVersion: return self._entry_data.api_version - @property - def _static_info(self) -> _InfoT: - # Check if value is in info database. Use a single lookup. - info = self._entry_data.info[self._component_key].get(self._key) - if info is not None: - return cast(_InfoT, info) - # This entity is in the removal project and has been removed from .info - # already, look in old_info - return cast(_InfoT, self._entry_data.old_info[self._component_key][self._key]) - @property def _device_info(self) -> EsphomeDeviceInfo: assert self._entry_data.device_info is not None diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index 9c78e69709e..e4daa524088 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -129,6 +129,10 @@ class RuntimeEntryData: """Return the signal to listen to for updates on static info.""" return f"esphome_{self.entry_id}_on_list" + def signal_component_static_info_updated(self, component_key: str) -> str: + """Return the signal to listen to for updates on static info for a specific component_key.""" + return f"esphome_{self.entry_id}_static_info_updated_{component_key}" + @callback def async_update_ble_connection_limits(self, free: int, limit: int) -> None: """Update the BLE connection limits.""" diff --git a/homeassistant/components/esphome/number.py b/homeassistant/components/esphome/number.py index 3ca8e0b9728..4e27d45bf61 100644 --- a/homeassistant/components/esphome/number.py +++ b/homeassistant/components/esphome/number.py @@ -52,22 +52,22 @@ class EsphomeNumber(EsphomeEntity[NumberInfo, NumberState], NumberEntity): @property def native_min_value(self) -> float: """Return the minimum value.""" - return super()._static_info.min_value + return self._static_info.min_value @property def native_max_value(self) -> float: """Return the maximum value.""" - return super()._static_info.max_value + return self._static_info.max_value @property def native_step(self) -> float: """Return the increment/decrement step.""" - return super()._static_info.step + return self._static_info.step @property def native_unit_of_measurement(self) -> str | None: """Return the unit of measurement.""" - return super()._static_info.unit_of_measurement + return self._static_info.unit_of_measurement @property def mode(self) -> NumberMode: