"""Support for the AEMET OpenData service.""" from __future__ import annotations from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( DEGREE, PERCENTAGE, UnitOfPressure, UnitOfSpeed, UnitOfTemperature, UnitOfVolumetricFlux, ) 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 ( ATTR_API_CONDITION, 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, 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, ATTRIBUTION, DOMAIN, ENTRY_NAME, ENTRY_WEATHER_COORDINATOR, FORECAST_MODE_ATTR_API, FORECAST_MODE_DAILY, FORECAST_MODES, FORECAST_MONITORED_CONDITIONS, MONITORED_CONDITIONS, ) from .weather_update_coordinator import WeatherUpdateCoordinator FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_API_FORECAST_CONDITION, name="Condition", ), SensorEntityDescription( key=ATTR_API_FORECAST_PRECIPITATION, name="Precipitation", native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, ), SensorEntityDescription( key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, name="Precipitation probability", native_unit_of_measurement=PERCENTAGE, ), SensorEntityDescription( key=ATTR_API_FORECAST_TEMP, name="Temperature", native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key=ATTR_API_FORECAST_TEMP_LOW, name="Temperature Low", native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key=ATTR_API_FORECAST_TIME, name="Time", device_class=SensorDeviceClass.TIMESTAMP, ), SensorEntityDescription( key=ATTR_API_FORECAST_WIND_BEARING, name="Wind bearing", native_unit_of_measurement=DEGREE, ), SensorEntityDescription( key=ATTR_API_FORECAST_WIND_SPEED, name="Wind speed", native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, device_class=SensorDeviceClass.WIND_SPEED, ), ) WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_API_CONDITION, name="Condition", ), SensorEntityDescription( key=ATTR_API_HUMIDITY, name="Humidity", native_unit_of_measurement=PERCENTAGE, device_class=SensorDeviceClass.HUMIDITY, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_PRESSURE, name="Pressure", native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_RAIN, name="Rain", native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, ), SensorEntityDescription( key=ATTR_API_RAIN_PROB, name="Rain probability", native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_SNOW, name="Snow", native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, ), SensorEntityDescription( key=ATTR_API_SNOW_PROB, name="Snow probability", native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_STATION_ID, name="Station ID", ), SensorEntityDescription( key=ATTR_API_STATION_NAME, name="Station name", ), SensorEntityDescription( key=ATTR_API_STATION_TIMESTAMP, name="Station timestamp", device_class=SensorDeviceClass.TIMESTAMP, ), SensorEntityDescription( key=ATTR_API_STORM_PROB, name="Storm probability", native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_TEMPERATURE, name="Temperature", native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_TEMPERATURE_FEELING, name="Temperature feeling", native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_TOWN_ID, name="Town ID", ), SensorEntityDescription( key=ATTR_API_TOWN_NAME, name="Town name", ), SensorEntityDescription( key=ATTR_API_TOWN_TIMESTAMP, name="Town timestamp", device_class=SensorDeviceClass.TIMESTAMP, ), SensorEntityDescription( key=ATTR_API_WIND_BEARING, name="Wind bearing", native_unit_of_measurement=DEGREE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_API_WIND_MAX_SPEED, name="Wind max speed", native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key=ATTR_API_WIND_SPEED, name="Wind speed", native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.WIND_SPEED, ), ) async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> 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] 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, ) 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.""" _attr_attribution = ATTRIBUTION def __init__( self, name, unique_id, coordinator: WeatherUpdateCoordinator, description: SensorEntityDescription, ): """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, ): """Initialize the sensor.""" super().__init__( name=name, unique_id=f"{unique_id_prefix}-{description.key}", coordinator=weather_coordinator, description=description, ) @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, ): """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