Use EntityDescription - meteo_france (#55677)

This commit is contained in:
Marc Mueller 2021-09-27 19:40:55 +02:00 committed by GitHub
parent 71ce858378
commit 0dcd8b32ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 179 additions and 203 deletions

View file

@ -1,5 +1,9 @@
"""Meteo-France component constants."""
from __future__ import annotations
from dataclasses import dataclass
from homeassistant.components.sensor import SensorEntityDescription
from homeassistant.components.weather import (
ATTR_CONDITION_CLEAR_NIGHT,
ATTR_CONDITION_CLOUDY,
@ -47,127 +51,131 @@ FORECAST_MODE = [FORECAST_MODE_HOURLY, FORECAST_MODE_DAILY]
ATTR_NEXT_RAIN_1_HOUR_FORECAST = "1_hour_forecast"
ATTR_NEXT_RAIN_DT_REF = "forecast_time_ref"
ENTITY_NAME = "name"
ENTITY_UNIT = "unit"
ENTITY_ICON = "icon"
ENTITY_DEVICE_CLASS = "device_class"
ENTITY_ENABLE = "enable"
ENTITY_API_DATA_PATH = "data_path"
SENSOR_TYPES = {
"pressure": {
ENTITY_NAME: "Pressure",
ENTITY_UNIT: PRESSURE_HPA,
ENTITY_ICON: None,
ENTITY_DEVICE_CLASS: DEVICE_CLASS_PRESSURE,
ENTITY_ENABLE: False,
ENTITY_API_DATA_PATH: "current_forecast:sea_level",
},
"rain_chance": {
ENTITY_NAME: "Rain chance",
ENTITY_UNIT: PERCENTAGE,
ENTITY_ICON: "mdi:weather-rainy",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: "probability_forecast:rain:3h",
},
"snow_chance": {
ENTITY_NAME: "Snow chance",
ENTITY_UNIT: PERCENTAGE,
ENTITY_ICON: "mdi:weather-snowy",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: "probability_forecast:snow:3h",
},
"freeze_chance": {
ENTITY_NAME: "Freeze chance",
ENTITY_UNIT: PERCENTAGE,
ENTITY_ICON: "mdi:snowflake",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: "probability_forecast:freezing",
},
"wind_gust": {
ENTITY_NAME: "Wind gust",
ENTITY_UNIT: SPEED_KILOMETERS_PER_HOUR,
ENTITY_ICON: "mdi:weather-windy-variant",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: False,
ENTITY_API_DATA_PATH: "current_forecast:wind:gust",
},
"wind_speed": {
ENTITY_NAME: "Wind speed",
ENTITY_UNIT: SPEED_KILOMETERS_PER_HOUR,
ENTITY_ICON: "mdi:weather-windy",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: False,
ENTITY_API_DATA_PATH: "current_forecast:wind:speed",
},
"next_rain": {
ENTITY_NAME: "Next rain",
ENTITY_UNIT: None,
ENTITY_ICON: None,
ENTITY_DEVICE_CLASS: DEVICE_CLASS_TIMESTAMP,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: None,
},
"temperature": {
ENTITY_NAME: "Temperature",
ENTITY_UNIT: TEMP_CELSIUS,
ENTITY_ICON: None,
ENTITY_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ENTITY_ENABLE: False,
ENTITY_API_DATA_PATH: "current_forecast:T:value",
},
"uv": {
ENTITY_NAME: "UV",
ENTITY_UNIT: UV_INDEX,
ENTITY_ICON: "mdi:sunglasses",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: "today_forecast:uv",
},
"weather_alert": {
ENTITY_NAME: "Weather alert",
ENTITY_UNIT: None,
ENTITY_ICON: "mdi:weather-cloudy-alert",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: None,
},
"precipitation": {
ENTITY_NAME: "Daily precipitation",
ENTITY_UNIT: LENGTH_MILLIMETERS,
ENTITY_ICON: "mdi:cup-water",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: "today_forecast:precipitation:24h",
},
"cloud": {
ENTITY_NAME: "Cloud cover",
ENTITY_UNIT: PERCENTAGE,
ENTITY_ICON: "mdi:weather-partly-cloudy",
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: True,
ENTITY_API_DATA_PATH: "current_forecast:clouds",
},
"original_condition": {
ENTITY_NAME: "Original condition",
ENTITY_UNIT: None,
ENTITY_ICON: None,
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: False,
ENTITY_API_DATA_PATH: "current_forecast:weather:desc",
},
"daily_original_condition": {
ENTITY_NAME: "Daily original condition",
ENTITY_UNIT: None,
ENTITY_ICON: None,
ENTITY_DEVICE_CLASS: None,
ENTITY_ENABLE: False,
ENTITY_API_DATA_PATH: "today_forecast:weather12H:desc",
},
}
@dataclass
class MeteoFranceRequiredKeysMixin:
"""Mixin for required keys."""
data_path: str
@dataclass
class MeteoFranceSensorEntityDescription(
SensorEntityDescription, MeteoFranceRequiredKeysMixin
):
"""Describes Meteo-France sensor entity."""
SENSOR_TYPES: tuple[MeteoFranceSensorEntityDescription, ...] = (
MeteoFranceSensorEntityDescription(
key="pressure",
name="Pressure",
native_unit_of_measurement=PRESSURE_HPA,
device_class=DEVICE_CLASS_PRESSURE,
entity_registry_enabled_default=False,
data_path="current_forecast:sea_level",
),
MeteoFranceSensorEntityDescription(
key="wind_gust",
name="Wind gust",
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
icon="mdi:weather-windy-variant",
entity_registry_enabled_default=False,
data_path="current_forecast:wind:gust",
),
MeteoFranceSensorEntityDescription(
key="wind_speed",
name="Wind speed",
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
icon="mdi:weather-windy",
entity_registry_enabled_default=False,
data_path="current_forecast:wind:speed",
),
MeteoFranceSensorEntityDescription(
key="temperature",
name="Temperature",
native_unit_of_measurement=TEMP_CELSIUS,
device_class=DEVICE_CLASS_TEMPERATURE,
entity_registry_enabled_default=False,
data_path="current_forecast:T:value",
),
MeteoFranceSensorEntityDescription(
key="uv",
name="UV",
native_unit_of_measurement=UV_INDEX,
icon="mdi:sunglasses",
data_path="today_forecast:uv",
),
MeteoFranceSensorEntityDescription(
key="precipitation",
name="Daily precipitation",
native_unit_of_measurement=LENGTH_MILLIMETERS,
icon="mdi:cup-water",
data_path="today_forecast:precipitation:24h",
),
MeteoFranceSensorEntityDescription(
key="cloud",
name="Cloud cover",
native_unit_of_measurement=PERCENTAGE,
icon="mdi:weather-partly-cloudy",
data_path="current_forecast:clouds",
),
MeteoFranceSensorEntityDescription(
key="original_condition",
name="Original condition",
entity_registry_enabled_default=False,
data_path="current_forecast:weather:desc",
),
MeteoFranceSensorEntityDescription(
key="daily_original_condition",
name="Daily original condition",
entity_registry_enabled_default=False,
data_path="today_forecast:weather12H:desc",
),
)
SENSOR_TYPES_RAIN: tuple[MeteoFranceSensorEntityDescription, ...] = (
MeteoFranceSensorEntityDescription(
key="next_rain",
name="Next rain",
device_class=DEVICE_CLASS_TIMESTAMP,
data_path="",
),
)
SENSOR_TYPES_ALERT: tuple[MeteoFranceSensorEntityDescription, ...] = (
MeteoFranceSensorEntityDescription(
key="weather_alert",
name="Weather alert",
icon="mdi:weather-cloudy-alert",
data_path="",
),
)
SENSOR_TYPES_PROBABILITY: tuple[MeteoFranceSensorEntityDescription, ...] = (
MeteoFranceSensorEntityDescription(
key="rain_chance",
name="Rain chance",
native_unit_of_measurement=PERCENTAGE,
icon="mdi:weather-rainy",
data_path="probability_forecast:rain:3h",
),
MeteoFranceSensorEntityDescription(
key="snow_chance",
name="Snow chance",
native_unit_of_measurement=PERCENTAGE,
icon="mdi:weather-snowy",
data_path="probability_forecast:snow:3h",
),
MeteoFranceSensorEntityDescription(
key="freeze_chance",
name="Freeze chance",
native_unit_of_measurement=PERCENTAGE,
icon="mdi:snowflake",
data_path="probability_forecast:freezing",
),
)
CONDITION_CLASSES = {
ATTR_CONDITION_CLEAR_NIGHT: ["Nuit Claire", "Nuit claire"],

View file

@ -1,6 +1,4 @@
"""Support for Meteo-France raining forecast sensor."""
import logging
from meteofrance_api.helpers import (
get_warning_text_status_from_indice_color,
readeable_phenomenoms_dict,
@ -24,19 +22,15 @@ from .const import (
COORDINATOR_FORECAST,
COORDINATOR_RAIN,
DOMAIN,
ENTITY_API_DATA_PATH,
ENTITY_DEVICE_CLASS,
ENTITY_ENABLE,
ENTITY_ICON,
ENTITY_NAME,
ENTITY_UNIT,
MANUFACTURER,
MODEL,
SENSOR_TYPES,
SENSOR_TYPES_ALERT,
SENSOR_TYPES_PROBABILITY,
SENSOR_TYPES_RAIN,
MeteoFranceSensorEntityDescription,
)
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
@ -46,56 +40,51 @@ async def async_setup_entry(
coordinator_rain = hass.data[DOMAIN][entry.entry_id][COORDINATOR_RAIN]
coordinator_alert = hass.data[DOMAIN][entry.entry_id][COORDINATOR_ALERT]
entities = []
for sensor_type in SENSOR_TYPES:
if sensor_type == "next_rain":
if coordinator_rain:
entities.append(MeteoFranceRainSensor(sensor_type, coordinator_rain))
elif sensor_type == "weather_alert":
if coordinator_alert:
entities.append(MeteoFranceAlertSensor(sensor_type, coordinator_alert))
elif sensor_type in ("rain_chance", "freeze_chance", "snow_chance"):
if coordinator_forecast.data.probability_forecast:
entities.append(MeteoFranceSensor(sensor_type, coordinator_forecast))
else:
_LOGGER.warning(
"Sensor %s skipped for %s as data is missing in the API",
sensor_type,
coordinator_forecast.data.position["name"],
)
else:
entities.append(MeteoFranceSensor(sensor_type, coordinator_forecast))
async_add_entities(
entities,
False,
entities = [
MeteoFranceSensor(coordinator_forecast, description)
for description in SENSOR_TYPES
]
entities.extend(
[
MeteoFranceRainSensor(coordinator_rain, description)
for description in SENSOR_TYPES_RAIN
]
)
entities.extend(
[
MeteoFranceAlertSensor(coordinator_alert, description)
for description in SENSOR_TYPES_ALERT
]
)
if coordinator_forecast.data.probability_forecast:
entities.extend(
[
MeteoFranceSensor(coordinator_forecast, description)
for description in SENSOR_TYPES_PROBABILITY
]
)
async_add_entities(entities, False)
class MeteoFranceSensor(CoordinatorEntity, SensorEntity):
"""Representation of a Meteo-France sensor."""
def __init__(self, sensor_type: str, coordinator: DataUpdateCoordinator) -> None:
entity_description: MeteoFranceSensorEntityDescription
def __init__(
self,
coordinator: DataUpdateCoordinator,
description: MeteoFranceSensorEntityDescription,
) -> None:
"""Initialize the Meteo-France sensor."""
super().__init__(coordinator)
self._type = sensor_type
if hasattr(self.coordinator.data, "position"):
city_name = self.coordinator.data.position["name"]
self._name = f"{city_name} {SENSOR_TYPES[self._type][ENTITY_NAME]}"
self._unique_id = f"{self.coordinator.data.position['lat']},{self.coordinator.data.position['lon']}_{self._type}"
@property
def unique_id(self):
"""Return the unique id."""
return self._unique_id
@property
def name(self):
"""Return the name."""
return self._name
self.entity_description = description
if hasattr(coordinator.data, "position"):
city_name = coordinator.data.position["name"]
self._attr_name = f"{city_name} {description.name}"
self._attr_unique_id = f"{coordinator.data.position['lat']},{coordinator.data.position['lon']}_{description.key}"
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
@property
def device_info(self):
@ -111,7 +100,7 @@ class MeteoFranceSensor(CoordinatorEntity, SensorEntity):
@property
def native_value(self):
"""Return the state."""
path = SENSOR_TYPES[self._type][ENTITY_API_DATA_PATH].split(":")
path = self.entity_description.data_path.split(":")
data = getattr(self.coordinator.data, path[0])
# Specific case for probability forecast
@ -129,36 +118,11 @@ class MeteoFranceSensor(CoordinatorEntity, SensorEntity):
else:
value = data[path[1]]
if self._type in ("wind_speed", "wind_gust"):
if self.entity_description.key in ("wind_speed", "wind_gust"):
# convert API wind speed from m/s to km/h
value = round(value * 3.6)
return value
@property
def native_unit_of_measurement(self):
"""Return the unit of measurement."""
return SENSOR_TYPES[self._type][ENTITY_UNIT]
@property
def icon(self):
"""Return the icon."""
return SENSOR_TYPES[self._type][ENTITY_ICON]
@property
def device_class(self):
"""Return the device class."""
return SENSOR_TYPES[self._type][ENTITY_DEVICE_CLASS]
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
return SENSOR_TYPES[self._type][ENTITY_ENABLE]
@property
def extra_state_attributes(self):
"""Return the state attributes."""
return {ATTR_ATTRIBUTION: ATTRIBUTION}
class MeteoFranceRainSensor(MeteoFranceSensor):
"""Representation of a Meteo-France rain sensor."""
@ -194,12 +158,16 @@ class MeteoFranceRainSensor(MeteoFranceSensor):
class MeteoFranceAlertSensor(MeteoFranceSensor):
"""Representation of a Meteo-France alert sensor."""
def __init__(self, sensor_type: str, coordinator: DataUpdateCoordinator) -> None:
def __init__(
self,
coordinator: DataUpdateCoordinator,
description: MeteoFranceSensorEntityDescription,
) -> None:
"""Initialize the Meteo-France sensor."""
super().__init__(sensor_type, coordinator)
super().__init__(coordinator, description)
dept_code = self.coordinator.data.domain_id
self._name = f"{dept_code} {SENSOR_TYPES[self._type][ENTITY_NAME]}"
self._unique_id = self._name
self._attr_name = f"{dept_code} {description.name}"
self._attr_unique_id = self._attr_name
@property
def native_value(self):