From 858739949b36631d7c45cd8091fb02ea707fbde5 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:18:18 +0200 Subject: [PATCH] Use EntityDescription - openweathermap (#56888) --- .coveragerc | 1 - .../openweathermap/abstract_owm_sensor.py | 96 ------- .../components/openweathermap/const.py | 248 ++++++++++-------- .../components/openweathermap/sensor.py | 114 +++++--- 4 files changed, 213 insertions(+), 246 deletions(-) delete mode 100644 homeassistant/components/openweathermap/abstract_owm_sensor.py diff --git a/.coveragerc b/.coveragerc index 26e0a214e40..5f9ccd9e014 100644 --- a/.coveragerc +++ b/.coveragerc @@ -776,7 +776,6 @@ omit = homeassistant/components/openweathermap/sensor.py homeassistant/components/openweathermap/weather.py homeassistant/components/openweathermap/weather_update_coordinator.py - homeassistant/components/openweathermap/abstract_owm_sensor.py homeassistant/components/opnsense/* homeassistant/components/opple/light.py homeassistant/components/orangepi_gpio/* diff --git a/homeassistant/components/openweathermap/abstract_owm_sensor.py b/homeassistant/components/openweathermap/abstract_owm_sensor.py deleted file mode 100644 index 3c66ca50f3c..00000000000 --- a/homeassistant/components/openweathermap/abstract_owm_sensor.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Abstraction form OWM sensors.""" -from homeassistant.components.sensor import SensorEntity -from homeassistant.const import ATTR_ATTRIBUTION -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator - -from .const import ( - ATTRIBUTION, - DEFAULT_NAME, - DOMAIN, - MANUFACTURER, - SENSOR_DEVICE_CLASS, - SENSOR_NAME, - SENSOR_UNIT, -) - - -class AbstractOpenWeatherMapSensor(SensorEntity): - """Abstract class for an OpenWeatherMap sensor.""" - - def __init__( - self, - name, - unique_id, - sensor_type, - sensor_configuration, - coordinator: DataUpdateCoordinator, - ): - """Initialize the sensor.""" - self._name = name - self._unique_id = unique_id - self._sensor_type = sensor_type - self._sensor_name = sensor_configuration[SENSOR_NAME] - self._unit_of_measurement = sensor_configuration.get(SENSOR_UNIT) - self._device_class = sensor_configuration.get(SENSOR_DEVICE_CLASS) - self._coordinator = coordinator - - @property - def name(self): - """Return the name of the sensor.""" - return f"{self._name} {self._sensor_name}" - - @property - def unique_id(self): - """Return a unique_id for this entity.""" - return self._unique_id - - @property - def device_info(self): - """Return the device info.""" - split_unique_id = self._unique_id.split("-") - return { - "identifiers": {(DOMAIN, f"{split_unique_id[0]}-{split_unique_id[1]}")}, - "name": DEFAULT_NAME, - "manufacturer": MANUFACTURER, - "entry_type": "service", - } - - @property - def should_poll(self): - """Return the polling requirement of the entity.""" - return False - - @property - def attribution(self): - """Return the attribution.""" - return ATTRIBUTION - - @property - def device_class(self): - """Return the device_class.""" - return self._device_class - - @property - def native_unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return self._unit_of_measurement - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - return {ATTR_ATTRIBUTION: ATTRIBUTION} - - @property - def available(self): - """Return True if entity is available.""" - return self._coordinator.last_update_success - - async def async_added_to_hass(self): - """Connect to dispatcher listening for entity data notifications.""" - self.async_on_remove( - self._coordinator.async_add_listener(self.async_write_ha_state) - ) - - async def async_update(self): - """Get the latest data from OWM and updates the states.""" - await self._coordinator.async_request_refresh() diff --git a/homeassistant/components/openweathermap/const.py b/homeassistant/components/openweathermap/const.py index c1ca96188d8..7647d64d7a2 100644 --- a/homeassistant/components/openweathermap/const.py +++ b/homeassistant/components/openweathermap/const.py @@ -1,4 +1,7 @@ """Consts for the OpenWeatherMap.""" +from __future__ import annotations + +from homeassistant.components.sensor import SensorEntityDescription from homeassistant.components.weather import ( ATTR_CONDITION_CLOUDY, ATTR_CONDITION_EXCEPTIONAL, @@ -21,8 +24,6 @@ from homeassistant.components.weather import ( ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, - ATTR_FORECAST_WIND_BEARING, - ATTR_FORECAST_WIND_SPEED, ) from homeassistant.const import ( DEGREE, @@ -65,9 +66,6 @@ ATTR_API_SNOW = "snow" ATTR_API_UV_INDEX = "uv_index" ATTR_API_WEATHER_CODE = "weather_code" ATTR_API_FORECAST = "forecast" -SENSOR_NAME = "sensor_name" -SENSOR_UNIT = "sensor_unit" -SENSOR_DEVICE_CLASS = "sensor_device_class" UPDATE_LISTENER = "update_listener" PLATFORMS = ["sensor", "weather"] @@ -84,35 +82,6 @@ FORECAST_MODES = [ ] DEFAULT_FORECAST_MODE = FORECAST_MODE_ONECALL_DAILY -MONITORED_CONDITIONS = [ - ATTR_API_WEATHER, - ATTR_API_DEW_POINT, - ATTR_API_TEMPERATURE, - ATTR_API_FEELS_LIKE_TEMPERATURE, - ATTR_API_WIND_SPEED, - ATTR_API_WIND_BEARING, - ATTR_API_HUMIDITY, - ATTR_API_PRESSURE, - ATTR_API_CLOUDS, - ATTR_API_RAIN, - ATTR_API_SNOW, - ATTR_API_PRECIPITATION_KIND, - ATTR_API_UV_INDEX, - ATTR_API_CONDITION, - ATTR_API_WEATHER_CODE, -] -FORECAST_MONITORED_CONDITIONS = [ - ATTR_FORECAST_CONDITION, - ATTR_FORECAST_PRECIPITATION, - ATTR_FORECAST_PRECIPITATION_PROBABILITY, - ATTR_FORECAST_PRESSURE, - ATTR_FORECAST_TEMP, - ATTR_FORECAST_TEMP_LOW, - ATTR_FORECAST_TIME, - ATTR_FORECAST_WIND_BEARING, - ATTR_FORECAST_WIND_SPEED, - ATTR_API_CLOUDS, -] LANGUAGES = [ "af", "al", @@ -194,82 +163,135 @@ CONDITION_CLASSES = { 904, ], } -WEATHER_SENSOR_TYPES = { - ATTR_API_WEATHER: {SENSOR_NAME: "Weather"}, - ATTR_API_DEW_POINT: { - SENSOR_NAME: "Dew Point", - SENSOR_UNIT: TEMP_CELSIUS, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - }, - ATTR_API_TEMPERATURE: { - SENSOR_NAME: "Temperature", - SENSOR_UNIT: TEMP_CELSIUS, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - }, - ATTR_API_FEELS_LIKE_TEMPERATURE: { - SENSOR_NAME: "Feels like temperature", - SENSOR_UNIT: TEMP_CELSIUS, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - }, - ATTR_API_WIND_SPEED: { - SENSOR_NAME: "Wind speed", - SENSOR_UNIT: SPEED_METERS_PER_SECOND, - }, - ATTR_API_WIND_BEARING: {SENSOR_NAME: "Wind bearing", SENSOR_UNIT: DEGREE}, - ATTR_API_HUMIDITY: { - SENSOR_NAME: "Humidity", - SENSOR_UNIT: PERCENTAGE, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY, - }, - ATTR_API_PRESSURE: { - SENSOR_NAME: "Pressure", - SENSOR_UNIT: PRESSURE_HPA, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE, - }, - ATTR_API_CLOUDS: {SENSOR_NAME: "Cloud coverage", SENSOR_UNIT: PERCENTAGE}, - ATTR_API_RAIN: {SENSOR_NAME: "Rain", SENSOR_UNIT: LENGTH_MILLIMETERS}, - ATTR_API_SNOW: {SENSOR_NAME: "Snow", SENSOR_UNIT: LENGTH_MILLIMETERS}, - ATTR_API_PRECIPITATION_KIND: {SENSOR_NAME: "Precipitation kind"}, - ATTR_API_UV_INDEX: { - SENSOR_NAME: "UV Index", - SENSOR_UNIT: UV_INDEX, - }, - ATTR_API_CONDITION: {SENSOR_NAME: "Condition"}, - ATTR_API_WEATHER_CODE: {SENSOR_NAME: "Weather Code"}, -} -FORECAST_SENSOR_TYPES = { - ATTR_FORECAST_CONDITION: {SENSOR_NAME: "Condition"}, - ATTR_FORECAST_PRECIPITATION: { - SENSOR_NAME: "Precipitation", - SENSOR_UNIT: LENGTH_MILLIMETERS, - }, - ATTR_FORECAST_PRECIPITATION_PROBABILITY: { - SENSOR_NAME: "Precipitation probability", - SENSOR_UNIT: PERCENTAGE, - }, - ATTR_FORECAST_PRESSURE: { - SENSOR_NAME: "Pressure", - SENSOR_UNIT: PRESSURE_HPA, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE, - }, - ATTR_FORECAST_TEMP: { - SENSOR_NAME: "Temperature", - SENSOR_UNIT: TEMP_CELSIUS, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - }, - ATTR_FORECAST_TEMP_LOW: { - SENSOR_NAME: "Temperature Low", - SENSOR_UNIT: TEMP_CELSIUS, - SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - }, - ATTR_FORECAST_TIME: { - SENSOR_NAME: "Time", - SENSOR_DEVICE_CLASS: DEVICE_CLASS_TIMESTAMP, - }, - ATTR_API_WIND_BEARING: {SENSOR_NAME: "Wind bearing", SENSOR_UNIT: DEGREE}, - ATTR_API_WIND_SPEED: { - SENSOR_NAME: "Wind speed", - SENSOR_UNIT: SPEED_METERS_PER_SECOND, - }, - ATTR_API_CLOUDS: {SENSOR_NAME: "Cloud coverage", SENSOR_UNIT: PERCENTAGE}, -} +WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key=ATTR_API_WEATHER, + name="Weather", + ), + SensorEntityDescription( + key=ATTR_API_DEW_POINT, + name="Dew Point", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_API_TEMPERATURE, + name="Temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_API_FEELS_LIKE_TEMPERATURE, + name="Feels like temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_API_WIND_SPEED, + name="Wind speed", + native_unit_of_measurement=SPEED_METERS_PER_SECOND, + ), + SensorEntityDescription( + key=ATTR_API_WIND_BEARING, + name="Wind bearing", + native_unit_of_measurement=DEGREE, + ), + SensorEntityDescription( + key=ATTR_API_HUMIDITY, + name="Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=DEVICE_CLASS_HUMIDITY, + ), + SensorEntityDescription( + key=ATTR_API_PRESSURE, + name="Pressure", + native_unit_of_measurement=PRESSURE_HPA, + device_class=DEVICE_CLASS_PRESSURE, + ), + SensorEntityDescription( + key=ATTR_API_CLOUDS, + name="Cloud coverage", + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + key=ATTR_API_RAIN, + name="Rain", + native_unit_of_measurement=LENGTH_MILLIMETERS, + ), + SensorEntityDescription( + key=ATTR_API_SNOW, + name="Snow", + native_unit_of_measurement=LENGTH_MILLIMETERS, + ), + SensorEntityDescription( + key=ATTR_API_PRECIPITATION_KIND, + name="Precipitation kind", + ), + SensorEntityDescription( + key=ATTR_API_UV_INDEX, + name="UV Index", + native_unit_of_measurement=UV_INDEX, + ), + SensorEntityDescription( + key=ATTR_API_CONDITION, + name="Condition", + ), + SensorEntityDescription( + key=ATTR_API_WEATHER_CODE, + name="Weather Code", + ), +) +FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key=ATTR_FORECAST_CONDITION, + name="Condition", + ), + SensorEntityDescription( + key=ATTR_FORECAST_PRECIPITATION, + name="Precipitation", + native_unit_of_measurement=LENGTH_MILLIMETERS, + ), + SensorEntityDescription( + key=ATTR_FORECAST_PRECIPITATION_PROBABILITY, + name="Precipitation probability", + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + key=ATTR_FORECAST_PRESSURE, + name="Pressure", + native_unit_of_measurement=PRESSURE_HPA, + device_class=DEVICE_CLASS_PRESSURE, + ), + SensorEntityDescription( + key=ATTR_FORECAST_TEMP, + name="Temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_FORECAST_TEMP_LOW, + name="Temperature Low", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_FORECAST_TIME, + name="Time", + device_class=DEVICE_CLASS_TIMESTAMP, + ), + SensorEntityDescription( + key=ATTR_API_WIND_BEARING, + name="Wind bearing", + native_unit_of_measurement=DEGREE, + ), + SensorEntityDescription( + key=ATTR_API_WIND_SPEED, + name="Wind speed", + native_unit_of_measurement=SPEED_METERS_PER_SECOND, + ), + SensorEntityDescription( + key=ATTR_API_CLOUDS, + name="Cloud coverage", + native_unit_of_measurement=PERCENTAGE, + ), +) diff --git a/homeassistant/components/openweathermap/sensor.py b/homeassistant/components/openweathermap/sensor.py index 3586f958a6a..6da352abf0a 100644 --- a/homeassistant/components/openweathermap/sensor.py +++ b/homeassistant/components/openweathermap/sensor.py @@ -1,13 +1,19 @@ """Support for the OpenWeatherMap (OWM) service.""" -from .abstract_owm_sensor import AbstractOpenWeatherMapSensor +from __future__ import annotations + +from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.const import ATTR_ATTRIBUTION +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + from .const import ( ATTR_API_FORECAST, + ATTRIBUTION, + DEFAULT_NAME, DOMAIN, ENTRY_NAME, ENTRY_WEATHER_COORDINATOR, - FORECAST_MONITORED_CONDITIONS, FORECAST_SENSOR_TYPES, - MONITORED_CONDITIONS, + MANUFACTURER, WEATHER_SENSOR_TYPES, ) from .weather_update_coordinator import WeatherUpdateCoordinator @@ -19,37 +25,79 @@ async def async_setup_entry(hass, config_entry, async_add_entities): name = domain_data[ENTRY_NAME] weather_coordinator = domain_data[ENTRY_WEATHER_COORDINATOR] - weather_sensor_types = WEATHER_SENSOR_TYPES - forecast_sensor_types = FORECAST_SENSOR_TYPES - - entities = [] - for sensor_type in MONITORED_CONDITIONS: - unique_id = f"{config_entry.unique_id}-{sensor_type}" - entities.append( - OpenWeatherMapSensor( - name, - unique_id, - sensor_type, - weather_sensor_types[sensor_type], - weather_coordinator, - ) + entities: list[AbstractOpenWeatherMapSensor] = [ + OpenWeatherMapSensor( + name, + f"{config_entry.unique_id}-{description.key}", + description, + weather_coordinator, ) + for description in WEATHER_SENSOR_TYPES + ] - for sensor_type in FORECAST_MONITORED_CONDITIONS: - unique_id = f"{config_entry.unique_id}-forecast-{sensor_type}" - entities.append( + entities.extend( + [ OpenWeatherMapForecastSensor( f"{name} Forecast", - unique_id, - sensor_type, - forecast_sensor_types[sensor_type], + f"{config_entry.unique_id}-forecast-{description.key}", + description, weather_coordinator, ) - ) + for description in FORECAST_SENSOR_TYPES + ] + ) async_add_entities(entities) +class AbstractOpenWeatherMapSensor(SensorEntity): + """Abstract class for an OpenWeatherMap sensor.""" + + _attr_should_poll = False + _attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION} + + def __init__( + self, + name, + unique_id, + description: SensorEntityDescription, + coordinator: DataUpdateCoordinator, + ): + """Initialize the sensor.""" + self.entity_description = description + self._coordinator = coordinator + + self._attr_name = f"{name} {description.name}" + self._attr_unique_id = unique_id + split_unique_id = unique_id.split("-") + self._attr_device_info = { + "identifiers": {(DOMAIN, f"{split_unique_id[0]}-{split_unique_id[1]}")}, + "name": DEFAULT_NAME, + "manufacturer": MANUFACTURER, + "entry_type": "service", + } + + @property + def attribution(self): + """Return the attribution.""" + return ATTRIBUTION + + @property + def available(self): + """Return True if entity is available.""" + return self._coordinator.last_update_success + + async def async_added_to_hass(self): + """Connect to dispatcher listening for entity data notifications.""" + self.async_on_remove( + self._coordinator.async_add_listener(self.async_write_ha_state) + ) + + async def async_update(self): + """Get the latest data from OWM and updates the states.""" + await self._coordinator.async_request_refresh() + + class OpenWeatherMapSensor(AbstractOpenWeatherMapSensor): """Implementation of an OpenWeatherMap sensor.""" @@ -57,20 +105,17 @@ class OpenWeatherMapSensor(AbstractOpenWeatherMapSensor): self, name, unique_id, - sensor_type, - sensor_configuration, + description: SensorEntityDescription, weather_coordinator: WeatherUpdateCoordinator, ): """Initialize the sensor.""" - super().__init__( - name, unique_id, sensor_type, sensor_configuration, weather_coordinator - ) + super().__init__(name, unique_id, description, weather_coordinator) self._weather_coordinator = weather_coordinator @property def native_value(self): """Return the state of the device.""" - return self._weather_coordinator.data.get(self._sensor_type, None) + return self._weather_coordinator.data.get(self.entity_description.key, None) class OpenWeatherMapForecastSensor(AbstractOpenWeatherMapSensor): @@ -80,14 +125,11 @@ class OpenWeatherMapForecastSensor(AbstractOpenWeatherMapSensor): self, name, unique_id, - sensor_type, - sensor_configuration, + description: SensorEntityDescription, weather_coordinator: WeatherUpdateCoordinator, ): """Initialize the sensor.""" - super().__init__( - name, unique_id, sensor_type, sensor_configuration, weather_coordinator - ) + super().__init__(name, unique_id, description, weather_coordinator) self._weather_coordinator = weather_coordinator @property @@ -95,5 +137,5 @@ class OpenWeatherMapForecastSensor(AbstractOpenWeatherMapSensor): """Return the state of the device.""" forecasts = self._weather_coordinator.data.get(ATTR_API_FORECAST) if forecasts is not None and len(forecasts) > 0: - return forecasts[0].get(self._sensor_type, None) + return forecasts[0].get(self.entity_description.key, None) return None