Use EntityDescription - meteo_france (#55677)
This commit is contained in:
parent
71ce858378
commit
0dcd8b32ab
2 changed files with 179 additions and 203 deletions
|
@ -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"],
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Add table
Reference in a new issue