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

View file

@ -1,6 +1,4 @@
"""Support for Meteo-France raining forecast sensor.""" """Support for Meteo-France raining forecast sensor."""
import logging
from meteofrance_api.helpers import ( from meteofrance_api.helpers import (
get_warning_text_status_from_indice_color, get_warning_text_status_from_indice_color,
readeable_phenomenoms_dict, readeable_phenomenoms_dict,
@ -24,19 +22,15 @@ from .const import (
COORDINATOR_FORECAST, COORDINATOR_FORECAST,
COORDINATOR_RAIN, COORDINATOR_RAIN,
DOMAIN, DOMAIN,
ENTITY_API_DATA_PATH,
ENTITY_DEVICE_CLASS,
ENTITY_ENABLE,
ENTITY_ICON,
ENTITY_NAME,
ENTITY_UNIT,
MANUFACTURER, MANUFACTURER,
MODEL, MODEL,
SENSOR_TYPES, SENSOR_TYPES,
SENSOR_TYPES_ALERT,
SENSOR_TYPES_PROBABILITY,
SENSOR_TYPES_RAIN,
MeteoFranceSensorEntityDescription,
) )
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities 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_rain = hass.data[DOMAIN][entry.entry_id][COORDINATOR_RAIN]
coordinator_alert = hass.data[DOMAIN][entry.entry_id][COORDINATOR_ALERT] coordinator_alert = hass.data[DOMAIN][entry.entry_id][COORDINATOR_ALERT]
entities = [] entities = [
for sensor_type in SENSOR_TYPES: MeteoFranceSensor(coordinator_forecast, description)
if sensor_type == "next_rain": for description in SENSOR_TYPES
if coordinator_rain: ]
entities.append(MeteoFranceRainSensor(sensor_type, coordinator_rain)) entities.extend(
[
elif sensor_type == "weather_alert": MeteoFranceRainSensor(coordinator_rain, description)
if coordinator_alert: for description in SENSOR_TYPES_RAIN
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.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): class MeteoFranceSensor(CoordinatorEntity, SensorEntity):
"""Representation of a Meteo-France sensor.""" """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.""" """Initialize the Meteo-France sensor."""
super().__init__(coordinator) super().__init__(coordinator)
self._type = sensor_type self.entity_description = description
if hasattr(self.coordinator.data, "position"): if hasattr(coordinator.data, "position"):
city_name = self.coordinator.data.position["name"] city_name = coordinator.data.position["name"]
self._name = f"{city_name} {SENSOR_TYPES[self._type][ENTITY_NAME]}" self._attr_name = f"{city_name} {description.name}"
self._unique_id = f"{self.coordinator.data.position['lat']},{self.coordinator.data.position['lon']}_{self._type}" 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 unique_id(self):
"""Return the unique id."""
return self._unique_id
@property
def name(self):
"""Return the name."""
return self._name
@property @property
def device_info(self): def device_info(self):
@ -111,7 +100,7 @@ class MeteoFranceSensor(CoordinatorEntity, SensorEntity):
@property @property
def native_value(self): def native_value(self):
"""Return the state.""" """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]) data = getattr(self.coordinator.data, path[0])
# Specific case for probability forecast # Specific case for probability forecast
@ -129,36 +118,11 @@ class MeteoFranceSensor(CoordinatorEntity, SensorEntity):
else: else:
value = data[path[1]] 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 # convert API wind speed from m/s to km/h
value = round(value * 3.6) value = round(value * 3.6)
return value 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): class MeteoFranceRainSensor(MeteoFranceSensor):
"""Representation of a Meteo-France rain sensor.""" """Representation of a Meteo-France rain sensor."""
@ -194,12 +158,16 @@ class MeteoFranceRainSensor(MeteoFranceSensor):
class MeteoFranceAlertSensor(MeteoFranceSensor): class MeteoFranceAlertSensor(MeteoFranceSensor):
"""Representation of a Meteo-France alert sensor.""" """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.""" """Initialize the Meteo-France sensor."""
super().__init__(sensor_type, coordinator) super().__init__(coordinator, description)
dept_code = self.coordinator.data.domain_id dept_code = self.coordinator.data.domain_id
self._name = f"{dept_code} {SENSOR_TYPES[self._type][ENTITY_NAME]}" self._attr_name = f"{dept_code} {description.name}"
self._unique_id = self._name self._attr_unique_id = self._attr_name
@property @property
def native_value(self): def native_value(self):