From 4d6951584983f31a1464aa8e47040d5cbc28f569 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 19 Jan 2024 03:41:32 -1000 Subject: [PATCH] Use shorthand attributes for mobile_app sensor platforms (#108353) --- .../components/mobile_app/binary_sensor.py | 17 ++--- homeassistant/components/mobile_app/entity.py | 65 +++++++------------ homeassistant/components/mobile_app/sensor.py | 48 +++++++------- 3 files changed, 55 insertions(+), 75 deletions(-) diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py index 69ecb913c98..2be71965371 100644 --- a/homeassistant/components/mobile_app/binary_sensor.py +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -4,7 +4,7 @@ from typing import Any from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_WEBHOOK_ID, STATE_ON -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -70,13 +70,14 @@ async def async_setup_entry( class MobileAppBinarySensor(MobileAppEntity, BinarySensorEntity): """Representation of an mobile app binary sensor.""" - @property - def is_on(self): - """Return the state of the binary sensor.""" - return self._config[ATTR_SENSOR_STATE] - - async def async_restore_last_state(self, last_state): + async def async_restore_last_state(self, last_state: State) -> None: """Restore previous state.""" - await super().async_restore_last_state(last_state) self._config[ATTR_SENSOR_STATE] = last_state.state == STATE_ON + self._async_update_attr_from_config() + + @callback + def _async_update_attr_from_config(self) -> None: + """Update the entity from the config.""" + super()._async_update_attr_from_config() + self._attr_is_on = self._config[ATTR_SENSOR_STATE] diff --git a/homeassistant/components/mobile_app/entity.py b/homeassistant/components/mobile_app/entity.py index 120014d1d52..76cf22cef54 100644 --- a/homeassistant/components/mobile_app/entity.py +++ b/homeassistant/components/mobile_app/entity.py @@ -5,7 +5,7 @@ from typing import Any from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ICON, CONF_NAME, CONF_UNIQUE_ID, STATE_UNAVAILABLE -from homeassistant.core import callback +from homeassistant.core import State, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.restore_state import RestoreEntity @@ -32,9 +32,23 @@ class MobileAppEntity(RestoreEntity): self._entry = entry self._registration = entry.data self._attr_unique_id = config[CONF_UNIQUE_ID] - self._name = self._config[CONF_NAME] + self._attr_entity_registry_enabled_default = not config.get( + ATTR_SENSOR_DISABLED + ) + self._attr_name = config[CONF_NAME] + self._async_update_attr_from_config() - async def async_added_to_hass(self): + @callback + def _async_update_attr_from_config(self) -> None: + """Update the entity from the config.""" + config = self._config + self._attr_device_class = config.get(ATTR_SENSOR_DEVICE_CLASS) + self._attr_extra_state_attributes = config[ATTR_SENSOR_ATTRIBUTES] + self._attr_icon = config[ATTR_SENSOR_ICON] + self._attr_entity_category = config.get(ATTR_SENSOR_ENTITY_CATEGORY) + self._attr_available = config.get(ATTR_SENSOR_STATE) != STATE_UNAVAILABLE + + async def async_added_to_hass(self) -> None: """Register callbacks.""" self.async_on_remove( async_dispatcher_connect( @@ -49,58 +63,25 @@ class MobileAppEntity(RestoreEntity): await self.async_restore_last_state(state) - async def async_restore_last_state(self, last_state): + async def async_restore_last_state(self, last_state: State) -> None: """Restore previous state.""" - self._config[ATTR_SENSOR_STATE] = last_state.state - self._config[ATTR_SENSOR_ATTRIBUTES] = { + config = self._config + config[ATTR_SENSOR_STATE] = last_state.state + config[ATTR_SENSOR_ATTRIBUTES] = { **last_state.attributes, **self._config[ATTR_SENSOR_ATTRIBUTES], } if ATTR_ICON in last_state.attributes: - self._config[ATTR_SENSOR_ICON] = last_state.attributes[ATTR_ICON] - - @property - def name(self): - """Return the name of the mobile app sensor.""" - return self._name - - @property - def entity_registry_enabled_default(self) -> bool: - """Return if entity should be enabled by default.""" - return not self._config.get(ATTR_SENSOR_DISABLED) - - @property - def device_class(self): - """Return the device class.""" - return self._config.get(ATTR_SENSOR_DEVICE_CLASS) - - @property - def extra_state_attributes(self): - """Return the device state attributes.""" - return self._config[ATTR_SENSOR_ATTRIBUTES] - - @property - def icon(self): - """Return the icon to use in the frontend, if any.""" - return self._config[ATTR_SENSOR_ICON] - - @property - def entity_category(self): - """Return the entity category, if any.""" - return self._config.get(ATTR_SENSOR_ENTITY_CATEGORY) + config[ATTR_SENSOR_ICON] = last_state.attributes[ATTR_ICON] @property def device_info(self): """Return device registry information for this entity.""" return device_info(self._registration) - @property - def available(self) -> bool: - """Return True if entity is available.""" - return self._config.get(ATTR_SENSOR_STATE) != STATE_UNAVAILABLE - @callback def _handle_update(self, data: dict[str, Any]) -> None: """Handle async event updates.""" self._config.update(data) + self._async_update_attr_from_config() self.async_write_ha_state() diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index fc325b1b6e9..fd712faf121 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -2,12 +2,12 @@ from __future__ import annotations from datetime import date, datetime -from typing import Any +from typing import TYPE_CHECKING, Any from homeassistant.components.sensor import RestoreSensor, SensorDeviceClass from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_WEBHOOK_ID, STATE_UNKNOWN, UnitOfTemperature -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -79,26 +79,28 @@ async def async_setup_entry( class MobileAppSensor(MobileAppEntity, RestoreSensor): """Representation of an mobile app sensor.""" - async def async_restore_last_state(self, last_state): + async def async_restore_last_state(self, last_state: State) -> None: """Restore previous state.""" - await super().async_restore_last_state(last_state) - + config = self._config if not (last_sensor_data := await self.async_get_last_sensor_data()): # Workaround to handle migration to RestoreSensor, can be removed # in HA Core 2023.4 - self._config[ATTR_SENSOR_STATE] = None + config[ATTR_SENSOR_STATE] = None webhook_id = self._entry.data[CONF_WEBHOOK_ID] + if TYPE_CHECKING: + assert self.unique_id is not None sensor_unique_id = _extract_sensor_unique_id(webhook_id, self.unique_id) if ( self.device_class == SensorDeviceClass.TEMPERATURE and sensor_unique_id == "battery_temperature" ): - self._config[ATTR_SENSOR_UOM] = UnitOfTemperature.CELSIUS - return + config[ATTR_SENSOR_UOM] = UnitOfTemperature.CELSIUS + else: + config[ATTR_SENSOR_STATE] = last_sensor_data.native_value + config[ATTR_SENSOR_UOM] = last_sensor_data.native_unit_of_measurement - self._config[ATTR_SENSOR_STATE] = last_sensor_data.native_value - self._config[ATTR_SENSOR_UOM] = last_sensor_data.native_unit_of_measurement + self._async_update_attr_from_config() @property def native_value(self) -> StateType | date | datetime: @@ -106,29 +108,25 @@ class MobileAppSensor(MobileAppEntity, RestoreSensor): if (state := self._config[ATTR_SENSOR_STATE]) in (None, STATE_UNKNOWN): return None + device_class = self.device_class + if ( - self.device_class - in ( - SensorDeviceClass.DATE, - SensorDeviceClass.TIMESTAMP, - ) + device_class in (SensorDeviceClass.DATE, SensorDeviceClass.TIMESTAMP) # Only parse strings: if the sensor's state is restored, the state is a # native date or datetime, not str and isinstance(state, str) and (timestamp := dt_util.parse_datetime(state)) is not None ): - if self.device_class == SensorDeviceClass.DATE: + if device_class == SensorDeviceClass.DATE: return timestamp.date() return timestamp return state - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit of measurement this sensor expresses itself in.""" - return self._config.get(ATTR_SENSOR_UOM) - - @property - def state_class(self) -> str | None: - """Return state class.""" - return self._config.get(ATTR_SENSOR_STATE_CLASS) + @callback + def _async_update_attr_from_config(self) -> None: + """Update the entity from the config.""" + super()._async_update_attr_from_config() + config = self._config + self._attr_native_unit_of_measurement = config.get(ATTR_SENSOR_UOM) + self._attr_state_class = config.get(ATTR_SENSOR_STATE_CLASS)