2019-02-14 15:01:46 +00:00
|
|
|
"""Support for IPMA weather service."""
|
2022-01-03 15:27:33 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2019-12-05 15:56:01 +00:00
|
|
|
import logging
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
import async_timeout
|
2020-01-21 16:04:22 +00:00
|
|
|
from pyipma.api import IPMA_API
|
2022-08-31 11:00:42 +00:00
|
|
|
from pyipma.forecast import Forecast
|
2020-01-21 16:04:22 +00:00
|
|
|
from pyipma.location import Location
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
from homeassistant.components.weather import (
|
2019-07-31 19:25:30 +00:00
|
|
|
ATTR_FORECAST_CONDITION,
|
2022-07-04 13:18:57 +00:00
|
|
|
ATTR_FORECAST_NATIVE_TEMP,
|
|
|
|
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
|
|
|
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
2020-08-11 02:55:44 +00:00
|
|
|
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
2019-07-31 19:25:30 +00:00
|
|
|
ATTR_FORECAST_TIME,
|
2020-02-14 17:04:41 +00:00
|
|
|
ATTR_FORECAST_WIND_BEARING,
|
2019-12-05 15:56:01 +00:00
|
|
|
WeatherEntity,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2022-01-03 15:27:33 +00:00
|
|
|
from homeassistant.config_entries import ConfigEntry
|
2020-02-14 17:04:41 +00:00
|
|
|
from homeassistant.const import (
|
|
|
|
CONF_MODE,
|
|
|
|
CONF_NAME,
|
2022-11-29 17:13:05 +00:00
|
|
|
UnitOfPressure,
|
|
|
|
UnitOfSpeed,
|
|
|
|
UnitOfTemperature,
|
2020-02-14 17:04:41 +00:00
|
|
|
)
|
2022-01-03 15:27:33 +00:00
|
|
|
from homeassistant.core import HomeAssistant, callback
|
2023-03-01 07:02:51 +00:00
|
|
|
from homeassistant.helpers import entity_registry as er
|
2022-01-03 15:27:33 +00:00
|
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
2022-08-31 11:00:42 +00:00
|
|
|
from homeassistant.helpers.sun import is_up
|
2018-06-03 21:01:48 +00:00
|
|
|
from homeassistant.util import Throttle
|
|
|
|
|
2023-06-27 14:00:10 +00:00
|
|
|
from .const import (
|
|
|
|
ATTRIBUTION,
|
|
|
|
CONDITION_CLASSES,
|
|
|
|
DATA_API,
|
|
|
|
DATA_LOCATION,
|
|
|
|
DOMAIN,
|
|
|
|
MIN_TIME_BETWEEN_UPDATES,
|
|
|
|
)
|
|
|
|
from .entity import IPMADevice
|
2022-09-14 01:00:59 +00:00
|
|
|
|
2018-06-03 21:01:48 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2022-01-03 15:27:33 +00:00
|
|
|
async def async_setup_entry(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
config_entry: ConfigEntry,
|
|
|
|
async_add_entities: AddEntitiesCallback,
|
|
|
|
) -> None:
|
2019-02-08 09:55:58 +00:00
|
|
|
"""Add a weather entity from a config_entry."""
|
2022-09-14 01:00:59 +00:00
|
|
|
api = hass.data[DOMAIN][config_entry.entry_id][DATA_API]
|
|
|
|
location = hass.data[DOMAIN][config_entry.entry_id][DATA_LOCATION]
|
2020-10-21 21:41:06 +00:00
|
|
|
mode = config_entry.data[CONF_MODE]
|
2019-02-08 09:55:58 +00:00
|
|
|
|
2020-10-21 21:41:06 +00:00
|
|
|
# Migrate old unique_id
|
|
|
|
@callback
|
2023-03-01 07:02:51 +00:00
|
|
|
def _async_migrator(entity_entry: er.RegistryEntry):
|
2020-10-21 21:41:06 +00:00
|
|
|
# Reject if new unique_id
|
|
|
|
if entity_entry.unique_id.count(",") == 2:
|
|
|
|
return None
|
|
|
|
|
|
|
|
new_unique_id = (
|
|
|
|
f"{location.station_latitude}, {location.station_longitude}, {mode}"
|
|
|
|
)
|
|
|
|
|
|
|
|
_LOGGER.info(
|
|
|
|
"Migrating unique_id from [%s] to [%s]",
|
|
|
|
entity_entry.unique_id,
|
|
|
|
new_unique_id,
|
|
|
|
)
|
|
|
|
return {"new_unique_id": new_unique_id}
|
|
|
|
|
2023-03-01 07:02:51 +00:00
|
|
|
await er.async_migrate_entries(hass, config_entry.entry_id, _async_migrator)
|
2020-10-21 21:41:06 +00:00
|
|
|
|
2020-01-21 16:04:22 +00:00
|
|
|
async_add_entities([IPMAWeather(location, api, config_entry.data)], True)
|
2019-02-08 09:55:58 +00:00
|
|
|
|
2018-06-03 21:01:48 +00:00
|
|
|
|
2023-06-27 14:00:10 +00:00
|
|
|
class IPMAWeather(WeatherEntity, IPMADevice):
|
2018-06-03 21:01:48 +00:00
|
|
|
"""Representation of a weather condition."""
|
|
|
|
|
2022-11-29 17:13:05 +00:00
|
|
|
_attr_native_pressure_unit = UnitOfPressure.HPA
|
|
|
|
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS
|
|
|
|
_attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR
|
2022-07-04 13:18:57 +00:00
|
|
|
|
2022-08-31 11:00:42 +00:00
|
|
|
_attr_attribution = ATTRIBUTION
|
|
|
|
|
2023-02-06 10:37:25 +00:00
|
|
|
def __init__(self, location: Location, api: IPMA_API, config) -> None:
|
2018-06-03 21:01:48 +00:00
|
|
|
"""Initialise the platform with a data instance and station name."""
|
2023-06-27 14:00:10 +00:00
|
|
|
IPMADevice.__init__(self, location)
|
2020-01-21 16:04:22 +00:00
|
|
|
self._api = api
|
2023-06-27 14:00:10 +00:00
|
|
|
self._attr_name = config.get(CONF_NAME, location.name)
|
2020-02-14 17:04:41 +00:00
|
|
|
self._mode = config.get(CONF_MODE)
|
2022-08-31 11:00:42 +00:00
|
|
|
self._period = 1 if config.get(CONF_MODE) == "hourly" else 24
|
2020-01-21 16:04:22 +00:00
|
|
|
self._observation = None
|
2022-08-31 11:00:42 +00:00
|
|
|
self._forecast: list[Forecast] = []
|
2023-06-27 14:00:10 +00:00
|
|
|
self._attr_unique_id = f"{self._location.station_latitude}, {self._location.station_longitude}, {self._mode}"
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
2022-08-30 17:21:08 +00:00
|
|
|
async def async_update(self) -> None:
|
2018-06-03 21:01:48 +00:00
|
|
|
"""Update Condition and Forecast."""
|
2021-11-04 15:07:50 +00:00
|
|
|
async with async_timeout.timeout(10):
|
2020-01-21 16:04:22 +00:00
|
|
|
new_observation = await self._location.observation(self._api)
|
2022-08-31 11:00:42 +00:00
|
|
|
new_forecast = await self._location.forecast(self._api, self._period)
|
2020-01-21 16:04:22 +00:00
|
|
|
|
|
|
|
if new_observation:
|
|
|
|
self._observation = new_observation
|
|
|
|
else:
|
|
|
|
_LOGGER.warning("Could not update weather observation")
|
|
|
|
|
|
|
|
if new_forecast:
|
2020-02-14 17:04:41 +00:00
|
|
|
self._forecast = new_forecast
|
2020-01-21 16:04:22 +00:00
|
|
|
else:
|
|
|
|
_LOGGER.warning("Could not update weather forecast")
|
2018-12-23 12:29:02 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.debug(
|
2022-08-31 11:00:42 +00:00
|
|
|
"Updated location %s based on %s, current observation %s",
|
2020-01-21 16:04:22 +00:00
|
|
|
self._location.name,
|
2022-08-31 11:00:42 +00:00
|
|
|
self._location.station,
|
2020-01-21 16:04:22 +00:00
|
|
|
self._observation,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2018-06-03 21:01:48 +00:00
|
|
|
|
2022-08-31 11:00:42 +00:00
|
|
|
def _condition_conversion(self, identifier, forecast_dt):
|
|
|
|
"""Convert from IPMA weather_type id to HA."""
|
|
|
|
if identifier == 1 and not is_up(self.hass, forecast_dt):
|
|
|
|
identifier = -identifier
|
|
|
|
|
|
|
|
return next(
|
|
|
|
(k for k, v in CONDITION_CLASSES.items() if identifier in v),
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
|
2018-06-03 21:01:48 +00:00
|
|
|
@property
|
|
|
|
def condition(self):
|
|
|
|
"""Return the current condition."""
|
2019-01-24 05:14:21 +00:00
|
|
|
if not self._forecast:
|
|
|
|
return
|
|
|
|
|
2022-08-31 11:00:42 +00:00
|
|
|
return self._condition_conversion(self._forecast[0].weather_type.id, None)
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
@property
|
2022-07-04 13:18:57 +00:00
|
|
|
def native_temperature(self):
|
2018-06-03 21:01:48 +00:00
|
|
|
"""Return the current temperature."""
|
2020-01-21 16:04:22 +00:00
|
|
|
if not self._observation:
|
2019-02-08 09:55:58 +00:00
|
|
|
return None
|
|
|
|
|
2020-01-21 16:04:22 +00:00
|
|
|
return self._observation.temperature
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
@property
|
2022-07-04 13:18:57 +00:00
|
|
|
def native_pressure(self):
|
2018-06-03 21:01:48 +00:00
|
|
|
"""Return the current pressure."""
|
2020-01-21 16:04:22 +00:00
|
|
|
if not self._observation:
|
2019-02-08 09:55:58 +00:00
|
|
|
return None
|
|
|
|
|
2020-01-21 16:04:22 +00:00
|
|
|
return self._observation.pressure
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def humidity(self):
|
|
|
|
"""Return the name of the sensor."""
|
2020-01-21 16:04:22 +00:00
|
|
|
if not self._observation:
|
2019-02-08 09:55:58 +00:00
|
|
|
return None
|
|
|
|
|
2020-01-21 16:04:22 +00:00
|
|
|
return self._observation.humidity
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
@property
|
2022-07-04 13:18:57 +00:00
|
|
|
def native_wind_speed(self):
|
2018-06-03 21:01:48 +00:00
|
|
|
"""Return the current windspeed."""
|
2020-01-21 16:04:22 +00:00
|
|
|
if not self._observation:
|
2019-02-08 09:55:58 +00:00
|
|
|
return None
|
|
|
|
|
2020-01-21 16:04:22 +00:00
|
|
|
return self._observation.wind_intensity_km
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def wind_bearing(self):
|
|
|
|
"""Return the current wind bearing (degrees)."""
|
2020-01-21 16:04:22 +00:00
|
|
|
if not self._observation:
|
2019-02-08 09:55:58 +00:00
|
|
|
return None
|
|
|
|
|
2020-01-21 16:04:22 +00:00
|
|
|
return self._observation.wind_direction
|
2018-06-03 21:01:48 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def forecast(self):
|
|
|
|
"""Return the forecast array."""
|
2020-01-21 16:04:22 +00:00
|
|
|
if not self._forecast:
|
|
|
|
return []
|
|
|
|
|
2022-08-31 11:00:42 +00:00
|
|
|
return [
|
|
|
|
{
|
|
|
|
ATTR_FORECAST_TIME: data_in.forecast_date,
|
|
|
|
ATTR_FORECAST_CONDITION: self._condition_conversion(
|
|
|
|
data_in.weather_type.id, data_in.forecast_date
|
|
|
|
),
|
|
|
|
ATTR_FORECAST_NATIVE_TEMP_LOW: data_in.min_temperature,
|
|
|
|
ATTR_FORECAST_NATIVE_TEMP: data_in.max_temperature,
|
|
|
|
ATTR_FORECAST_PRECIPITATION_PROBABILITY: data_in.precipitation_probability,
|
|
|
|
ATTR_FORECAST_NATIVE_WIND_SPEED: data_in.wind_strength,
|
|
|
|
ATTR_FORECAST_WIND_BEARING: data_in.wind_direction,
|
|
|
|
}
|
|
|
|
for data_in in self._forecast
|
|
|
|
]
|