mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Use new AEMET library data for sensor platform (#102972)
Co-authored-by: Franck Nijhof <frenck@frenck.nl> Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
parent
de9bb20135
commit
6a6c447c28
@ -59,8 +59,6 @@ ENTRY_WEATHER_COORDINATOR = "weather_coordinator"
|
||||
|
||||
ATTR_API_CONDITION = "condition"
|
||||
ATTR_API_FORECAST_CONDITION = "condition"
|
||||
ATTR_API_FORECAST_DAILY = "forecast-daily"
|
||||
ATTR_API_FORECAST_HOURLY = "forecast-hourly"
|
||||
ATTR_API_FORECAST_PRECIPITATION = "precipitation"
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY = "precipitation_probability"
|
||||
ATTR_API_FORECAST_TEMP = "temperature"
|
||||
@ -101,49 +99,6 @@ CONDITIONS_MAP = {
|
||||
AOD_COND_SUNNY: ATTR_CONDITION_SUNNY,
|
||||
}
|
||||
|
||||
FORECAST_MONITORED_CONDITIONS = [
|
||||
ATTR_API_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP,
|
||||
ATTR_API_FORECAST_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED,
|
||||
]
|
||||
MONITORED_CONDITIONS = [
|
||||
ATTR_API_CONDITION,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_RAIN,
|
||||
ATTR_API_RAIN_PROB,
|
||||
ATTR_API_SNOW,
|
||||
ATTR_API_SNOW_PROB,
|
||||
ATTR_API_STATION_ID,
|
||||
ATTR_API_STATION_NAME,
|
||||
ATTR_API_STATION_TIMESTAMP,
|
||||
ATTR_API_STORM_PROB,
|
||||
ATTR_API_TEMPERATURE,
|
||||
ATTR_API_TEMPERATURE_FEELING,
|
||||
ATTR_API_TOWN_ID,
|
||||
ATTR_API_TOWN_NAME,
|
||||
ATTR_API_TOWN_TIMESTAMP,
|
||||
ATTR_API_WIND_BEARING,
|
||||
ATTR_API_WIND_MAX_SPEED,
|
||||
ATTR_API_WIND_SPEED,
|
||||
]
|
||||
|
||||
FORECAST_MODE_DAILY = "daily"
|
||||
FORECAST_MODE_HOURLY = "hourly"
|
||||
FORECAST_MODES = [
|
||||
FORECAST_MODE_DAILY,
|
||||
FORECAST_MODE_HOURLY,
|
||||
]
|
||||
FORECAST_MODE_ATTR_API = {
|
||||
FORECAST_MODE_DAILY: ATTR_API_FORECAST_DAILY,
|
||||
FORECAST_MODE_HOURLY: ATTR_API_FORECAST_HOURLY,
|
||||
}
|
||||
|
||||
FORECAST_MAP = {
|
||||
AOD_FORECAST_DAILY: {
|
||||
AOD_CONDITION: ATTR_FORECAST_CONDITION,
|
||||
|
@ -1,6 +1,41 @@
|
||||
"""Support for the AEMET OpenData service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Final
|
||||
|
||||
from aemet_opendata.const import (
|
||||
AOD_CONDITION,
|
||||
AOD_FEEL_TEMP,
|
||||
AOD_FORECAST_CURRENT,
|
||||
AOD_FORECAST_DAILY,
|
||||
AOD_FORECAST_HOURLY,
|
||||
AOD_HUMIDITY,
|
||||
AOD_ID,
|
||||
AOD_NAME,
|
||||
AOD_PRECIPITATION,
|
||||
AOD_PRECIPITATION_PROBABILITY,
|
||||
AOD_PRESSURE,
|
||||
AOD_RAIN,
|
||||
AOD_RAIN_PROBABILITY,
|
||||
AOD_SNOW,
|
||||
AOD_SNOW_PROBABILITY,
|
||||
AOD_STATION,
|
||||
AOD_STORM_PROBABILITY,
|
||||
AOD_TEMP,
|
||||
AOD_TEMP_MAX,
|
||||
AOD_TEMP_MIN,
|
||||
AOD_TIMESTAMP,
|
||||
AOD_TOWN,
|
||||
AOD_WEATHER,
|
||||
AOD_WIND_DIRECTION,
|
||||
AOD_WIND_SPEED,
|
||||
AOD_WIND_SPEED_MAX,
|
||||
)
|
||||
from aemet_opendata.helpers import dict_nested_value
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
@ -18,7 +53,6 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
@ -51,172 +85,270 @@ from .const import (
|
||||
ATTR_API_WIND_MAX_SPEED,
|
||||
ATTR_API_WIND_SPEED,
|
||||
ATTRIBUTION,
|
||||
CONDITIONS_MAP,
|
||||
DOMAIN,
|
||||
ENTRY_NAME,
|
||||
ENTRY_WEATHER_COORDINATOR,
|
||||
FORECAST_MODE_ATTR_API,
|
||||
FORECAST_MODE_DAILY,
|
||||
FORECAST_MODES,
|
||||
FORECAST_MONITORED_CONDITIONS,
|
||||
MONITORED_CONDITIONS,
|
||||
)
|
||||
from .entity import AemetEntity
|
||||
from .weather_update_coordinator import WeatherUpdateCoordinator
|
||||
|
||||
FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_CONDITION,
|
||||
name="Condition",
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class AemetSensorEntityDescription(SensorEntityDescription):
|
||||
"""A class that describes AEMET OpenData sensor entities."""
|
||||
|
||||
keys: list[str] | None = None
|
||||
value_fn: Callable[[str], datetime | float | int | str | None] = lambda value: value
|
||||
|
||||
|
||||
FORECAST_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
|
||||
AemetSensorEntityDescription(
|
||||
key=f"forecast-daily-{ATTR_API_FORECAST_CONDITION}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_CONDITION],
|
||||
name="Daily forecast condition",
|
||||
value_fn=CONDITIONS_MAP.get,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_PRECIPITATION,
|
||||
name="Precipitation",
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_CONDITION}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_CONDITION],
|
||||
name="Hourly forecast condition",
|
||||
value_fn=CONDITIONS_MAP.get,
|
||||
),
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_PRECIPITATION}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_PRECIPITATION],
|
||||
name="Hourly forecast precipitation",
|
||||
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
name="Precipitation probability",
|
||||
AemetSensorEntityDescription(
|
||||
key=f"forecast-daily-{ATTR_API_FORECAST_PRECIPITATION_PROBABILITY}",
|
||||
keys=[
|
||||
AOD_TOWN,
|
||||
AOD_FORECAST_DAILY,
|
||||
AOD_FORECAST_CURRENT,
|
||||
AOD_PRECIPITATION_PROBABILITY,
|
||||
],
|
||||
name="Daily forecast precipitation probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_TEMP,
|
||||
name="Temperature",
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_PRECIPITATION_PROBABILITY}",
|
||||
keys=[
|
||||
AOD_TOWN,
|
||||
AOD_FORECAST_HOURLY,
|
||||
AOD_FORECAST_CURRENT,
|
||||
AOD_PRECIPITATION_PROBABILITY,
|
||||
],
|
||||
name="Hourly forecast precipitation probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
AemetSensorEntityDescription(
|
||||
key=f"forecast-daily-{ATTR_API_FORECAST_TEMP}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_TEMP_MAX],
|
||||
name="Daily forecast temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_TEMP_LOW,
|
||||
name="Temperature Low",
|
||||
AemetSensorEntityDescription(
|
||||
key=f"forecast-daily-{ATTR_API_FORECAST_TEMP_LOW}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_TEMP_MIN],
|
||||
name="Daily forecast temperature low",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_TIME,
|
||||
name="Time",
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_TEMP}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_TEMP],
|
||||
name="Hourly forecast temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
AemetSensorEntityDescription(
|
||||
key=f"forecast-daily-{ATTR_API_FORECAST_TIME}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_TIMESTAMP],
|
||||
name="Daily forecast time",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=dt_util.parse_datetime,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_WIND_BEARING,
|
||||
name="Wind bearing",
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_TIME}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_TIMESTAMP],
|
||||
name="Hourly forecast time",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=dt_util.parse_datetime,
|
||||
),
|
||||
AemetSensorEntityDescription(
|
||||
key=f"forecast-daily-{ATTR_API_FORECAST_WIND_BEARING}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_WIND_DIRECTION],
|
||||
name="Daily forecast wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_WIND_MAX_SPEED,
|
||||
name="Wind max speed",
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_WIND_BEARING}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_WIND_DIRECTION],
|
||||
name="Hourly forecast wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_WIND_MAX_SPEED}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_WIND_SPEED_MAX],
|
||||
name="Hourly forecast wind max speed",
|
||||
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FORECAST_WIND_SPEED,
|
||||
name="Wind speed",
|
||||
AemetSensorEntityDescription(
|
||||
key=f"forecast-daily-{ATTR_API_FORECAST_WIND_SPEED}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_DAILY, AOD_FORECAST_CURRENT, AOD_WIND_SPEED],
|
||||
name="Daily forecast wind speed",
|
||||
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
),
|
||||
AemetSensorEntityDescription(
|
||||
entity_registry_enabled_default=False,
|
||||
key=f"forecast-hourly-{ATTR_API_FORECAST_WIND_SPEED}",
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_FORECAST_CURRENT, AOD_WIND_SPEED],
|
||||
name="Hourly forecast wind speed",
|
||||
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
),
|
||||
)
|
||||
WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
|
||||
|
||||
WEATHER_SENSORS: Final[tuple[AemetSensorEntityDescription, ...]] = (
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_CONDITION,
|
||||
keys=[AOD_WEATHER, AOD_CONDITION],
|
||||
name="Condition",
|
||||
value_fn=CONDITIONS_MAP.get,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_HUMIDITY,
|
||||
keys=[AOD_WEATHER, AOD_HUMIDITY],
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_PRESSURE,
|
||||
keys=[AOD_WEATHER, AOD_PRESSURE],
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.HPA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_RAIN,
|
||||
keys=[AOD_WEATHER, AOD_RAIN],
|
||||
name="Rain",
|
||||
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_RAIN_PROB,
|
||||
keys=[AOD_WEATHER, AOD_RAIN_PROBABILITY],
|
||||
name="Rain probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_SNOW,
|
||||
keys=[AOD_WEATHER, AOD_SNOW],
|
||||
name="Snow",
|
||||
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_SNOW_PROB,
|
||||
keys=[AOD_WEATHER, AOD_SNOW_PROBABILITY],
|
||||
name="Snow probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_STATION_ID,
|
||||
keys=[AOD_STATION, AOD_ID],
|
||||
name="Station ID",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_STATION_NAME,
|
||||
keys=[AOD_STATION, AOD_NAME],
|
||||
name="Station name",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_STATION_TIMESTAMP,
|
||||
keys=[AOD_STATION, AOD_TIMESTAMP],
|
||||
name="Station timestamp",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=dt_util.parse_datetime,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_STORM_PROB,
|
||||
keys=[AOD_WEATHER, AOD_STORM_PROBABILITY],
|
||||
name="Storm probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
keys=[AOD_WEATHER, AOD_TEMP],
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE_FEELING,
|
||||
keys=[AOD_WEATHER, AOD_FEEL_TEMP],
|
||||
name="Temperature feeling",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_TOWN_ID,
|
||||
keys=[AOD_TOWN, AOD_ID],
|
||||
name="Town ID",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_TOWN_NAME,
|
||||
keys=[AOD_TOWN, AOD_NAME],
|
||||
name="Town name",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_TOWN_TIMESTAMP,
|
||||
keys=[AOD_TOWN, AOD_FORECAST_HOURLY, AOD_TIMESTAMP],
|
||||
name="Town timestamp",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=dt_util.parse_datetime,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_WIND_BEARING,
|
||||
keys=[AOD_WEATHER, AOD_WIND_DIRECTION],
|
||||
name="Wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_WIND_MAX_SPEED,
|
||||
keys=[AOD_WEATHER, AOD_WIND_SPEED_MAX],
|
||||
name="Wind max speed",
|
||||
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
AemetSensorEntityDescription(
|
||||
key=ATTR_API_WIND_SPEED,
|
||||
keys=[AOD_WEATHER, AOD_WIND_SPEED],
|
||||
name="Wind speed",
|
||||
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
@ -232,108 +364,46 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up AEMET OpenData sensor entities based on a config entry."""
|
||||
domain_data = hass.data[DOMAIN][config_entry.entry_id]
|
||||
name = domain_data[ENTRY_NAME]
|
||||
weather_coordinator = domain_data[ENTRY_WEATHER_COORDINATOR]
|
||||
name: str = domain_data[ENTRY_NAME]
|
||||
coordinator: WeatherUpdateCoordinator = domain_data[ENTRY_WEATHER_COORDINATOR]
|
||||
|
||||
unique_id = config_entry.unique_id
|
||||
entities: list[AbstractAemetSensor] = [
|
||||
AemetSensor(name, unique_id, weather_coordinator, description)
|
||||
for description in WEATHER_SENSOR_TYPES
|
||||
if description.key in MONITORED_CONDITIONS
|
||||
]
|
||||
entities.extend(
|
||||
[
|
||||
AemetForecastSensor(
|
||||
f"{domain_data[ENTRY_NAME]} {mode} Forecast",
|
||||
f"{unique_id}-forecast-{mode}",
|
||||
weather_coordinator,
|
||||
mode,
|
||||
description,
|
||||
entities: list[AemetSensor] = []
|
||||
|
||||
for description in FORECAST_SENSORS + WEATHER_SENSORS:
|
||||
if dict_nested_value(coordinator.data["lib"], description.keys) is not None:
|
||||
entities.append(
|
||||
AemetSensor(
|
||||
name,
|
||||
coordinator,
|
||||
description,
|
||||
config_entry,
|
||||
)
|
||||
)
|
||||
for mode in FORECAST_MODES
|
||||
for description in FORECAST_SENSOR_TYPES
|
||||
if description.key in FORECAST_MONITORED_CONDITIONS
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class AbstractAemetSensor(CoordinatorEntity[WeatherUpdateCoordinator], SensorEntity):
|
||||
"""Abstract class for an AEMET OpenData sensor."""
|
||||
class AemetSensor(AemetEntity, SensorEntity):
|
||||
"""Implementation of an AEMET OpenData sensor."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
entity_description: AemetSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
unique_id,
|
||||
name: str,
|
||||
coordinator: WeatherUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
description: AemetSensorEntityDescription,
|
||||
config_entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
self._attr_unique_id = unique_id
|
||||
|
||||
|
||||
class AemetSensor(AbstractAemetSensor):
|
||||
"""Implementation of an AEMET OpenData sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
unique_id_prefix,
|
||||
weather_coordinator: WeatherUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
name=name,
|
||||
unique_id=f"{unique_id_prefix}-{description.key}",
|
||||
coordinator=weather_coordinator,
|
||||
description=description,
|
||||
)
|
||||
self._attr_unique_id = f"{config_entry.unique_id}-{description.key}"
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
return self.coordinator.data.get(self.entity_description.key)
|
||||
|
||||
|
||||
class AemetForecastSensor(AbstractAemetSensor):
|
||||
"""Implementation of an AEMET OpenData forecast sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
unique_id_prefix,
|
||||
weather_coordinator: WeatherUpdateCoordinator,
|
||||
forecast_mode,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
name=name,
|
||||
unique_id=f"{unique_id_prefix}-{description.key}",
|
||||
coordinator=weather_coordinator,
|
||||
description=description,
|
||||
)
|
||||
self._forecast_mode = forecast_mode
|
||||
self._attr_entity_registry_enabled_default = (
|
||||
self._forecast_mode == FORECAST_MODE_DAILY
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
forecast = None
|
||||
forecasts = self.coordinator.data.get(
|
||||
FORECAST_MODE_ATTR_API[self._forecast_mode]
|
||||
)
|
||||
if forecasts:
|
||||
forecast = forecasts[0].get(self.entity_description.key)
|
||||
if self.entity_description.key == ATTR_API_FORECAST_TIME:
|
||||
forecast = dt_util.parse_datetime(forecast)
|
||||
return forecast
|
||||
value = self.get_aemet_value(self.entity_description.keys)
|
||||
return self.entity_description.value_fn(value)
|
||||
|
@ -7,117 +7,28 @@ import logging
|
||||
from typing import Any, Final, cast
|
||||
|
||||
from aemet_opendata.const import (
|
||||
AEMET_ATTR_DATE,
|
||||
AEMET_ATTR_DAY,
|
||||
AEMET_ATTR_DIRECTION,
|
||||
AEMET_ATTR_ELABORATED,
|
||||
AEMET_ATTR_FEEL_TEMPERATURE,
|
||||
AEMET_ATTR_FORECAST,
|
||||
AEMET_ATTR_HUMIDITY,
|
||||
AEMET_ATTR_MAX,
|
||||
AEMET_ATTR_MIN,
|
||||
AEMET_ATTR_PRECIPITATION,
|
||||
AEMET_ATTR_PRECIPITATION_PROBABILITY,
|
||||
AEMET_ATTR_SKY_STATE,
|
||||
AEMET_ATTR_SNOW,
|
||||
AEMET_ATTR_SNOW_PROBABILITY,
|
||||
AEMET_ATTR_SPEED,
|
||||
AEMET_ATTR_STATION_DATE,
|
||||
AEMET_ATTR_STATION_HUMIDITY,
|
||||
AEMET_ATTR_STATION_PRESSURE,
|
||||
AEMET_ATTR_STATION_PRESSURE_SEA,
|
||||
AEMET_ATTR_STATION_TEMPERATURE,
|
||||
AEMET_ATTR_STORM_PROBABILITY,
|
||||
AEMET_ATTR_TEMPERATURE,
|
||||
AEMET_ATTR_WIND,
|
||||
AEMET_ATTR_WIND_GUST,
|
||||
AOD_CONDITION,
|
||||
AOD_FORECAST,
|
||||
AOD_FORECAST_DAILY,
|
||||
AOD_FORECAST_HOURLY,
|
||||
AOD_TOWN,
|
||||
ATTR_DATA,
|
||||
)
|
||||
from aemet_opendata.exceptions import AemetError
|
||||
from aemet_opendata.forecast import ForecastValue
|
||||
from aemet_opendata.helpers import (
|
||||
dict_nested_value,
|
||||
get_forecast_day_value,
|
||||
get_forecast_hour_value,
|
||||
get_forecast_interval_value,
|
||||
)
|
||||
from aemet_opendata.helpers import dict_nested_value
|
||||
from aemet_opendata.interface import AEMET
|
||||
|
||||
from homeassistant.components.weather import Forecast
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
ATTR_API_CONDITION,
|
||||
ATTR_API_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_DAILY,
|
||||
ATTR_API_FORECAST_HOURLY,
|
||||
ATTR_API_FORECAST_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP,
|
||||
ATTR_API_FORECAST_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_MAX_SPEED,
|
||||
ATTR_API_FORECAST_WIND_SPEED,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_RAIN,
|
||||
ATTR_API_RAIN_PROB,
|
||||
ATTR_API_SNOW,
|
||||
ATTR_API_SNOW_PROB,
|
||||
ATTR_API_STATION_ID,
|
||||
ATTR_API_STATION_NAME,
|
||||
ATTR_API_STATION_TIMESTAMP,
|
||||
ATTR_API_STORM_PROB,
|
||||
ATTR_API_TEMPERATURE,
|
||||
ATTR_API_TEMPERATURE_FEELING,
|
||||
ATTR_API_TOWN_ID,
|
||||
ATTR_API_TOWN_NAME,
|
||||
ATTR_API_TOWN_TIMESTAMP,
|
||||
ATTR_API_WIND_BEARING,
|
||||
ATTR_API_WIND_MAX_SPEED,
|
||||
ATTR_API_WIND_SPEED,
|
||||
CONDITIONS_MAP,
|
||||
DOMAIN,
|
||||
FORECAST_MAP,
|
||||
)
|
||||
from .const import CONDITIONS_MAP, DOMAIN, FORECAST_MAP
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
API_TIMEOUT: Final[int] = 120
|
||||
STATION_MAX_DELTA = timedelta(hours=2)
|
||||
WEATHER_UPDATE_INTERVAL = timedelta(minutes=10)
|
||||
|
||||
|
||||
def format_condition(condition: str) -> str:
|
||||
"""Return condition from dict CONDITIONS_MAP."""
|
||||
val = ForecastValue.parse_condition(condition)
|
||||
return CONDITIONS_MAP.get(val, val)
|
||||
|
||||
|
||||
def format_float(value) -> float | None:
|
||||
"""Try converting string to float."""
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def format_int(value) -> int | None:
|
||||
"""Try converting string to int."""
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Weather data update coordinator."""
|
||||
|
||||
@ -143,139 +54,14 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
await self.aemet.update()
|
||||
except AemetError as error:
|
||||
raise UpdateFailed(error) from error
|
||||
weather_response = self.aemet.legacy_weather()
|
||||
return self._convert_weather_response(weather_response)
|
||||
|
||||
def _convert_weather_response(self, weather_response):
|
||||
"""Format the weather response correctly."""
|
||||
if not weather_response or not weather_response.hourly:
|
||||
return None
|
||||
|
||||
elaborated = dt_util.parse_datetime(
|
||||
weather_response.hourly[ATTR_DATA][0][AEMET_ATTR_ELABORATED] + "Z"
|
||||
)
|
||||
now = dt_util.now()
|
||||
now_utc = dt_util.utcnow()
|
||||
hour = now.hour
|
||||
|
||||
# Get current day
|
||||
day = None
|
||||
for cur_day in weather_response.hourly[ATTR_DATA][0][AEMET_ATTR_FORECAST][
|
||||
AEMET_ATTR_DAY
|
||||
]:
|
||||
cur_day_date = dt_util.parse_datetime(cur_day[AEMET_ATTR_DATE])
|
||||
if now.date() == cur_day_date.date():
|
||||
day = cur_day
|
||||
break
|
||||
|
||||
# Get latest station data
|
||||
station_data = None
|
||||
station_dt = None
|
||||
if weather_response.station:
|
||||
for _station_data in weather_response.station[ATTR_DATA]:
|
||||
if AEMET_ATTR_STATION_DATE in _station_data:
|
||||
_station_dt = dt_util.parse_datetime(
|
||||
_station_data[AEMET_ATTR_STATION_DATE] + "Z"
|
||||
)
|
||||
if not station_dt or _station_dt > station_dt:
|
||||
station_data = _station_data
|
||||
station_dt = _station_dt
|
||||
|
||||
condition = None
|
||||
humidity = None
|
||||
pressure = None
|
||||
rain = None
|
||||
rain_prob = None
|
||||
snow = None
|
||||
snow_prob = None
|
||||
station_id = None
|
||||
station_name = None
|
||||
station_timestamp = None
|
||||
storm_prob = None
|
||||
temperature = None
|
||||
temperature_feeling = None
|
||||
town_id = None
|
||||
town_name = None
|
||||
town_timestamp = dt_util.as_utc(elaborated)
|
||||
wind_bearing = None
|
||||
wind_max_speed = None
|
||||
wind_speed = None
|
||||
|
||||
# Get weather values
|
||||
if day:
|
||||
condition = self._get_condition(day, hour)
|
||||
humidity = self._get_humidity(day, hour)
|
||||
rain = self._get_rain(day, hour)
|
||||
rain_prob = self._get_rain_prob(day, hour)
|
||||
snow = self._get_snow(day, hour)
|
||||
snow_prob = self._get_snow_prob(day, hour)
|
||||
station_id = self._get_station_id()
|
||||
station_name = self._get_station_name()
|
||||
storm_prob = self._get_storm_prob(day, hour)
|
||||
temperature = self._get_temperature(day, hour)
|
||||
temperature_feeling = self._get_temperature_feeling(day, hour)
|
||||
town_id = self._get_town_id()
|
||||
town_name = self._get_town_name()
|
||||
wind_bearing = self._get_wind_bearing(day, hour)
|
||||
wind_max_speed = self._get_wind_max_speed(day, hour)
|
||||
wind_speed = self._get_wind_speed(day, hour)
|
||||
|
||||
# Overwrite weather values with closest station data (if present)
|
||||
if station_data:
|
||||
station_timestamp = dt_util.as_utc(station_dt)
|
||||
if (now_utc - station_dt) <= STATION_MAX_DELTA:
|
||||
if AEMET_ATTR_STATION_HUMIDITY in station_data:
|
||||
humidity = format_float(station_data[AEMET_ATTR_STATION_HUMIDITY])
|
||||
if AEMET_ATTR_STATION_PRESSURE_SEA in station_data:
|
||||
pressure = format_float(
|
||||
station_data[AEMET_ATTR_STATION_PRESSURE_SEA]
|
||||
)
|
||||
elif AEMET_ATTR_STATION_PRESSURE in station_data:
|
||||
pressure = format_float(station_data[AEMET_ATTR_STATION_PRESSURE])
|
||||
if AEMET_ATTR_STATION_TEMPERATURE in station_data:
|
||||
temperature = format_float(
|
||||
station_data[AEMET_ATTR_STATION_TEMPERATURE]
|
||||
)
|
||||
else:
|
||||
_LOGGER.warning("Station data is outdated")
|
||||
|
||||
# Get forecast from weather data
|
||||
forecast_daily = self._get_daily_forecast_from_weather_response(
|
||||
weather_response, now
|
||||
)
|
||||
forecast_hourly = self._get_hourly_forecast_from_weather_response(
|
||||
weather_response, now
|
||||
)
|
||||
|
||||
data = self.aemet.data()
|
||||
forecasts: list[dict[str, Forecast]] = {
|
||||
AOD_FORECAST_DAILY: self.aemet_forecast(data, AOD_FORECAST_DAILY),
|
||||
AOD_FORECAST_HOURLY: self.aemet_forecast(data, AOD_FORECAST_HOURLY),
|
||||
}
|
||||
|
||||
return {
|
||||
ATTR_API_CONDITION: condition,
|
||||
ATTR_API_FORECAST_DAILY: forecast_daily,
|
||||
ATTR_API_FORECAST_HOURLY: forecast_hourly,
|
||||
ATTR_API_HUMIDITY: humidity,
|
||||
ATTR_API_TEMPERATURE: temperature,
|
||||
ATTR_API_TEMPERATURE_FEELING: temperature_feeling,
|
||||
ATTR_API_PRESSURE: pressure,
|
||||
ATTR_API_RAIN: rain,
|
||||
ATTR_API_RAIN_PROB: rain_prob,
|
||||
ATTR_API_SNOW: snow,
|
||||
ATTR_API_SNOW_PROB: snow_prob,
|
||||
ATTR_API_STATION_ID: station_id,
|
||||
ATTR_API_STATION_NAME: station_name,
|
||||
ATTR_API_STATION_TIMESTAMP: station_timestamp,
|
||||
ATTR_API_STORM_PROB: storm_prob,
|
||||
ATTR_API_TOWN_ID: town_id,
|
||||
ATTR_API_TOWN_NAME: town_name,
|
||||
ATTR_API_TOWN_TIMESTAMP: town_timestamp,
|
||||
ATTR_API_WIND_BEARING: wind_bearing,
|
||||
ATTR_API_WIND_MAX_SPEED: wind_max_speed,
|
||||
ATTR_API_WIND_SPEED: wind_speed,
|
||||
"forecast": forecasts,
|
||||
"forecast": {
|
||||
AOD_FORECAST_DAILY: self.aemet_forecast(data, AOD_FORECAST_DAILY),
|
||||
AOD_FORECAST_HOURLY: self.aemet_forecast(data, AOD_FORECAST_HOURLY),
|
||||
},
|
||||
"lib": data,
|
||||
}
|
||||
|
||||
@ -297,262 +83,3 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
cur_forecast[ha_key] = value
|
||||
forecast_list += [cur_forecast]
|
||||
return cast(list[Forecast], forecast_list)
|
||||
|
||||
def _get_daily_forecast_from_weather_response(self, weather_response, now):
|
||||
if weather_response.daily:
|
||||
parse = False
|
||||
forecast = []
|
||||
for day in weather_response.daily[ATTR_DATA][0][AEMET_ATTR_FORECAST][
|
||||
AEMET_ATTR_DAY
|
||||
]:
|
||||
day_date = dt_util.parse_datetime(day[AEMET_ATTR_DATE])
|
||||
if now.date() == day_date.date():
|
||||
parse = True
|
||||
if parse:
|
||||
cur_forecast = self._convert_forecast_day(day_date, day)
|
||||
if cur_forecast:
|
||||
forecast.append(cur_forecast)
|
||||
return forecast
|
||||
return None
|
||||
|
||||
def _get_hourly_forecast_from_weather_response(self, weather_response, now):
|
||||
if weather_response.hourly:
|
||||
parse = False
|
||||
hour = now.hour
|
||||
forecast = []
|
||||
for day in weather_response.hourly[ATTR_DATA][0][AEMET_ATTR_FORECAST][
|
||||
AEMET_ATTR_DAY
|
||||
]:
|
||||
day_date = dt_util.parse_datetime(day[AEMET_ATTR_DATE])
|
||||
hour_start = 0
|
||||
if now.date() == day_date.date():
|
||||
parse = True
|
||||
hour_start = now.hour
|
||||
if parse:
|
||||
for hour in range(hour_start, 24):
|
||||
cur_forecast = self._convert_forecast_hour(day_date, day, hour)
|
||||
if cur_forecast:
|
||||
forecast.append(cur_forecast)
|
||||
return forecast
|
||||
return None
|
||||
|
||||
def _convert_forecast_day(self, date, day):
|
||||
if not (condition := self._get_condition_day(day)):
|
||||
return None
|
||||
|
||||
return {
|
||||
ATTR_API_FORECAST_CONDITION: condition,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: self._get_precipitation_prob_day(
|
||||
day
|
||||
),
|
||||
ATTR_API_FORECAST_TEMP: self._get_temperature_day(day),
|
||||
ATTR_API_FORECAST_TEMP_LOW: self._get_temperature_low_day(day),
|
||||
ATTR_API_FORECAST_TIME: dt_util.as_utc(date).isoformat(),
|
||||
ATTR_API_FORECAST_WIND_SPEED: self._get_wind_speed_day(day),
|
||||
ATTR_API_FORECAST_WIND_BEARING: self._get_wind_bearing_day(day),
|
||||
}
|
||||
|
||||
def _convert_forecast_hour(self, date, day, hour):
|
||||
if not (condition := self._get_condition(day, hour)):
|
||||
return None
|
||||
|
||||
forecast_dt = date.replace(hour=hour, minute=0, second=0)
|
||||
|
||||
return {
|
||||
ATTR_API_FORECAST_CONDITION: condition,
|
||||
ATTR_API_FORECAST_PRECIPITATION: self._calc_precipitation(day, hour),
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: self._calc_precipitation_prob(
|
||||
day, hour
|
||||
),
|
||||
ATTR_API_FORECAST_TEMP: self._get_temperature(day, hour),
|
||||
ATTR_API_FORECAST_TIME: dt_util.as_utc(forecast_dt).isoformat(),
|
||||
ATTR_API_FORECAST_WIND_MAX_SPEED: self._get_wind_max_speed(day, hour),
|
||||
ATTR_API_FORECAST_WIND_SPEED: self._get_wind_speed(day, hour),
|
||||
ATTR_API_FORECAST_WIND_BEARING: self._get_wind_bearing(day, hour),
|
||||
}
|
||||
|
||||
def _calc_precipitation(self, day, hour):
|
||||
"""Calculate the precipitation."""
|
||||
rain_value = self._get_rain(day, hour) or 0
|
||||
snow_value = self._get_snow(day, hour) or 0
|
||||
|
||||
if round(rain_value + snow_value, 1) == 0:
|
||||
return None
|
||||
return round(rain_value + snow_value, 1)
|
||||
|
||||
def _calc_precipitation_prob(self, day, hour):
|
||||
"""Calculate the precipitation probability (hour)."""
|
||||
rain_value = self._get_rain_prob(day, hour) or 0
|
||||
snow_value = self._get_snow_prob(day, hour) or 0
|
||||
|
||||
if rain_value == 0 and snow_value == 0:
|
||||
return None
|
||||
return max(rain_value, snow_value)
|
||||
|
||||
@staticmethod
|
||||
def _get_condition(day_data, hour):
|
||||
"""Get weather condition (hour) from weather data."""
|
||||
val = get_forecast_hour_value(day_data[AEMET_ATTR_SKY_STATE], hour)
|
||||
if val:
|
||||
return format_condition(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_condition_day(day_data):
|
||||
"""Get weather condition (day) from weather data."""
|
||||
val = get_forecast_day_value(day_data[AEMET_ATTR_SKY_STATE])
|
||||
if val:
|
||||
return format_condition(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_humidity(day_data, hour):
|
||||
"""Get humidity from weather data."""
|
||||
val = get_forecast_hour_value(day_data[AEMET_ATTR_HUMIDITY], hour)
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_precipitation_prob_day(day_data):
|
||||
"""Get humidity from weather data."""
|
||||
val = get_forecast_day_value(day_data[AEMET_ATTR_PRECIPITATION_PROBABILITY])
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_rain(day_data, hour):
|
||||
"""Get rain from weather data."""
|
||||
val = get_forecast_hour_value(day_data[AEMET_ATTR_PRECIPITATION], hour)
|
||||
if val:
|
||||
return format_float(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_rain_prob(day_data, hour):
|
||||
"""Get rain probability from weather data."""
|
||||
val = get_forecast_interval_value(
|
||||
day_data[AEMET_ATTR_PRECIPITATION_PROBABILITY], hour
|
||||
)
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_snow(day_data, hour):
|
||||
"""Get snow from weather data."""
|
||||
val = get_forecast_hour_value(day_data[AEMET_ATTR_SNOW], hour)
|
||||
if val:
|
||||
return format_float(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_snow_prob(day_data, hour):
|
||||
"""Get snow probability from weather data."""
|
||||
val = get_forecast_interval_value(day_data[AEMET_ATTR_SNOW_PROBABILITY], hour)
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
||||
def _get_station_id(self):
|
||||
"""Get station ID from weather data."""
|
||||
if self.aemet.station:
|
||||
return self.aemet.station.get_id()
|
||||
return None
|
||||
|
||||
def _get_station_name(self):
|
||||
"""Get station name from weather data."""
|
||||
if self.aemet.station:
|
||||
return self.aemet.station.get_name()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_storm_prob(day_data, hour):
|
||||
"""Get storm probability from weather data."""
|
||||
val = get_forecast_interval_value(day_data[AEMET_ATTR_STORM_PROBABILITY], hour)
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_temperature(day_data, hour):
|
||||
"""Get temperature (hour) from weather data."""
|
||||
val = get_forecast_hour_value(day_data[AEMET_ATTR_TEMPERATURE], hour)
|
||||
return format_int(val)
|
||||
|
||||
@staticmethod
|
||||
def _get_temperature_day(day_data):
|
||||
"""Get temperature (day) from weather data."""
|
||||
val = get_forecast_day_value(
|
||||
day_data[AEMET_ATTR_TEMPERATURE], key=AEMET_ATTR_MAX
|
||||
)
|
||||
return format_int(val)
|
||||
|
||||
@staticmethod
|
||||
def _get_temperature_low_day(day_data):
|
||||
"""Get temperature (day) from weather data."""
|
||||
val = get_forecast_day_value(
|
||||
day_data[AEMET_ATTR_TEMPERATURE], key=AEMET_ATTR_MIN
|
||||
)
|
||||
return format_int(val)
|
||||
|
||||
@staticmethod
|
||||
def _get_temperature_feeling(day_data, hour):
|
||||
"""Get temperature from weather data."""
|
||||
val = get_forecast_hour_value(day_data[AEMET_ATTR_FEEL_TEMPERATURE], hour)
|
||||
return format_int(val)
|
||||
|
||||
def _get_town_id(self):
|
||||
"""Get town ID from weather data."""
|
||||
if self.aemet.town:
|
||||
return self.aemet.town.get_id()
|
||||
return None
|
||||
|
||||
def _get_town_name(self):
|
||||
"""Get town name from weather data."""
|
||||
if self.aemet.town:
|
||||
return self.aemet.town.get_name()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_wind_bearing(day_data, hour):
|
||||
"""Get wind bearing (hour) from weather data."""
|
||||
val = get_forecast_hour_value(
|
||||
day_data[AEMET_ATTR_WIND_GUST], hour, key=AEMET_ATTR_DIRECTION
|
||||
)[0]
|
||||
return ForecastValue.parse_wind_direction(val)
|
||||
|
||||
@staticmethod
|
||||
def _get_wind_bearing_day(day_data):
|
||||
"""Get wind bearing (day) from weather data."""
|
||||
val = get_forecast_day_value(
|
||||
day_data[AEMET_ATTR_WIND], key=AEMET_ATTR_DIRECTION
|
||||
)
|
||||
return ForecastValue.parse_wind_direction(val)
|
||||
|
||||
@staticmethod
|
||||
def _get_wind_max_speed(day_data, hour):
|
||||
"""Get wind max speed from weather data."""
|
||||
val = get_forecast_hour_value(day_data[AEMET_ATTR_WIND_GUST], hour)
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_wind_speed(day_data, hour):
|
||||
"""Get wind speed (hour) from weather data."""
|
||||
val = get_forecast_hour_value(
|
||||
day_data[AEMET_ATTR_WIND_GUST], hour, key=AEMET_ATTR_SPEED
|
||||
)[0]
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_wind_speed_day(day_data):
|
||||
"""Get wind speed (day) from weather data."""
|
||||
val = get_forecast_day_value(day_data[AEMET_ATTR_WIND], key=AEMET_ATTR_SPEED)
|
||||
if val:
|
||||
return format_int(val)
|
||||
return None
|
||||
|
@ -93,7 +93,7 @@ async def test_aemet_weather_create_sensors(
|
||||
assert state.state == "1004.4"
|
||||
|
||||
state = hass.states.get("sensor.aemet_rain")
|
||||
assert state.state == "1.8"
|
||||
assert state.state == "7.0"
|
||||
|
||||
state = hass.states.get("sensor.aemet_rain_probability")
|
||||
assert state.state == "100"
|
||||
@ -132,10 +132,10 @@ async def test_aemet_weather_create_sensors(
|
||||
assert state.state == "2021-01-09T11:47:45+00:00"
|
||||
|
||||
state = hass.states.get("sensor.aemet_wind_bearing")
|
||||
assert state.state == "90.0"
|
||||
assert state.state == "122.0"
|
||||
|
||||
state = hass.states.get("sensor.aemet_wind_max_speed")
|
||||
assert state.state == "24"
|
||||
assert state.state == "12.2"
|
||||
|
||||
state = hass.states.get("sensor.aemet_wind_speed")
|
||||
assert state.state == "15"
|
||||
assert state.state == "3.2"
|
||||
|
Loading…
x
Reference in New Issue
Block a user