mirror of https://github.com/home-assistant/core
391 lines
14 KiB
Python
391 lines
14 KiB
Python
"""Demo platform that offers fake meteorological data."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import timedelta
|
|
|
|
from homeassistant.components.weather import (
|
|
ATTR_CONDITION_CLOUDY,
|
|
ATTR_CONDITION_EXCEPTIONAL,
|
|
ATTR_CONDITION_FOG,
|
|
ATTR_CONDITION_HAIL,
|
|
ATTR_CONDITION_LIGHTNING,
|
|
ATTR_CONDITION_LIGHTNING_RAINY,
|
|
ATTR_CONDITION_PARTLYCLOUDY,
|
|
ATTR_CONDITION_POURING,
|
|
ATTR_CONDITION_RAINY,
|
|
ATTR_CONDITION_SNOWY,
|
|
ATTR_CONDITION_SNOWY_RAINY,
|
|
ATTR_CONDITION_SUNNY,
|
|
ATTR_CONDITION_WINDY,
|
|
ATTR_CONDITION_WINDY_VARIANT,
|
|
Forecast,
|
|
WeatherEntity,
|
|
WeatherEntityFeature,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import UnitOfPressure, UnitOfSpeed, UnitOfTemperature
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.event import async_track_time_interval
|
|
import homeassistant.util.dt as dt_util
|
|
|
|
CONDITION_CLASSES: dict[str, list[str]] = {
|
|
ATTR_CONDITION_CLOUDY: [],
|
|
ATTR_CONDITION_FOG: [],
|
|
ATTR_CONDITION_HAIL: [],
|
|
ATTR_CONDITION_LIGHTNING: [],
|
|
ATTR_CONDITION_LIGHTNING_RAINY: [],
|
|
ATTR_CONDITION_PARTLYCLOUDY: [],
|
|
ATTR_CONDITION_POURING: [],
|
|
ATTR_CONDITION_RAINY: ["shower rain"],
|
|
ATTR_CONDITION_SNOWY: [],
|
|
ATTR_CONDITION_SNOWY_RAINY: [],
|
|
ATTR_CONDITION_SUNNY: ["sunshine"],
|
|
ATTR_CONDITION_WINDY: [],
|
|
ATTR_CONDITION_WINDY_VARIANT: [],
|
|
ATTR_CONDITION_EXCEPTIONAL: [],
|
|
}
|
|
CONDITION_MAP = {
|
|
cond_code: cond_ha
|
|
for cond_ha, cond_codes in CONDITION_CLASSES.items()
|
|
for cond_code in cond_codes
|
|
}
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the Demo config entry."""
|
|
async_add_entities(
|
|
[
|
|
DemoWeather(
|
|
"Legacy + daily weather",
|
|
"Sunshine",
|
|
21.6414,
|
|
92,
|
|
1099,
|
|
0.5,
|
|
UnitOfTemperature.CELSIUS,
|
|
UnitOfPressure.HPA,
|
|
UnitOfSpeed.METERS_PER_SECOND,
|
|
[
|
|
[ATTR_CONDITION_RAINY, 1, 22, 15, 60],
|
|
[ATTR_CONDITION_RAINY, 5, 19, 8, 30],
|
|
[ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
|
|
[ATTR_CONDITION_SUNNY, 0, 12, 6, 0],
|
|
[ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20],
|
|
[ATTR_CONDITION_RAINY, 15, 18, 7, 0],
|
|
[ATTR_CONDITION_FOG, 0.2, 21, 12, 100],
|
|
],
|
|
None,
|
|
None,
|
|
),
|
|
DemoWeather(
|
|
"Daily + hourly weather",
|
|
"Shower rain",
|
|
-12,
|
|
54,
|
|
987,
|
|
4.8,
|
|
UnitOfTemperature.FAHRENHEIT,
|
|
UnitOfPressure.INHG,
|
|
UnitOfSpeed.MILES_PER_HOUR,
|
|
[
|
|
[ATTR_CONDITION_SNOWY, 2, -10, -15, 60],
|
|
[ATTR_CONDITION_PARTLYCLOUDY, 1, -13, -14, 25],
|
|
[ATTR_CONDITION_SUNNY, 0, -18, -22, 70],
|
|
[ATTR_CONDITION_SUNNY, 0.1, -23, -23, 90],
|
|
[ATTR_CONDITION_SNOWY, 4, -19, -20, 40],
|
|
[ATTR_CONDITION_SUNNY, 0.3, -14, -19, 0],
|
|
[ATTR_CONDITION_SUNNY, 0, -9, -12, 0],
|
|
],
|
|
[
|
|
[ATTR_CONDITION_SUNNY, 2, -10, -15, 60],
|
|
[ATTR_CONDITION_SUNNY, 1, -13, -14, 25],
|
|
[ATTR_CONDITION_SUNNY, 0, -18, -22, 70],
|
|
[ATTR_CONDITION_SUNNY, 0.1, -23, -23, 90],
|
|
[ATTR_CONDITION_SUNNY, 4, -19, -20, 40],
|
|
[ATTR_CONDITION_SUNNY, 0.3, -14, -19, 0],
|
|
[ATTR_CONDITION_SUNNY, 0, -9, -12, 0],
|
|
],
|
|
None,
|
|
),
|
|
DemoWeather(
|
|
"Daily + bi-daily + hourly weather",
|
|
"Sunshine",
|
|
21.6414,
|
|
92,
|
|
1099,
|
|
0.5,
|
|
UnitOfTemperature.CELSIUS,
|
|
UnitOfPressure.HPA,
|
|
UnitOfSpeed.METERS_PER_SECOND,
|
|
[
|
|
[ATTR_CONDITION_RAINY, 1, 22, 15, 60],
|
|
[ATTR_CONDITION_RAINY, 5, 19, 8, 30],
|
|
[ATTR_CONDITION_RAINY, 0, 15, 9, 10],
|
|
[ATTR_CONDITION_RAINY, 0, 12, 6, 0],
|
|
[ATTR_CONDITION_RAINY, 2, 14, 7, 20],
|
|
[ATTR_CONDITION_RAINY, 15, 18, 7, 0],
|
|
[ATTR_CONDITION_RAINY, 0.2, 21, 12, 100],
|
|
],
|
|
[
|
|
[ATTR_CONDITION_CLOUDY, 1, 22, 15, 60],
|
|
[ATTR_CONDITION_CLOUDY, 5, 19, 8, 30],
|
|
[ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
|
|
[ATTR_CONDITION_CLOUDY, 0, 12, 6, 0],
|
|
[ATTR_CONDITION_CLOUDY, 2, 14, 7, 20],
|
|
[ATTR_CONDITION_CLOUDY, 15, 18, 7, 0],
|
|
[ATTR_CONDITION_CLOUDY, 0.2, 21, 12, 100],
|
|
],
|
|
[
|
|
[ATTR_CONDITION_RAINY, 1, 22, 15, 60, True],
|
|
[ATTR_CONDITION_RAINY, 5, 19, 8, 30, False],
|
|
[ATTR_CONDITION_CLOUDY, 0, 15, 9, 10, True],
|
|
[ATTR_CONDITION_SUNNY, 0, 12, 6, 0, False],
|
|
[ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20, True],
|
|
[ATTR_CONDITION_RAINY, 15, 18, 7, 0, False],
|
|
[ATTR_CONDITION_FOG, 0.2, 21, 12, 100, True],
|
|
],
|
|
),
|
|
DemoWeather(
|
|
"Hourly + bi-daily weather",
|
|
"Sunshine",
|
|
21.6414,
|
|
92,
|
|
1099,
|
|
0.5,
|
|
UnitOfTemperature.CELSIUS,
|
|
UnitOfPressure.HPA,
|
|
UnitOfSpeed.METERS_PER_SECOND,
|
|
None,
|
|
[
|
|
[ATTR_CONDITION_CLOUDY, 1, 22, 15, 60],
|
|
[ATTR_CONDITION_CLOUDY, 5, 19, 8, 30],
|
|
[ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
|
|
[ATTR_CONDITION_CLOUDY, 0, 12, 6, 0],
|
|
[ATTR_CONDITION_CLOUDY, 2, 14, 7, 20],
|
|
[ATTR_CONDITION_CLOUDY, 15, 18, 7, 0],
|
|
[ATTR_CONDITION_CLOUDY, 0.2, 21, 12, 100],
|
|
],
|
|
[
|
|
[ATTR_CONDITION_RAINY, 1, 22, 15, 60, True],
|
|
[ATTR_CONDITION_RAINY, 5, 19, 8, 30, False],
|
|
[ATTR_CONDITION_CLOUDY, 0, 15, 9, 10, True],
|
|
[ATTR_CONDITION_SUNNY, 0, 12, 6, 0, False],
|
|
[ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20, True],
|
|
[ATTR_CONDITION_RAINY, 15, 18, 7, 0, False],
|
|
[ATTR_CONDITION_FOG, 0.2, 21, 12, 100, True],
|
|
],
|
|
),
|
|
DemoWeather(
|
|
"Daily + broken bi-daily weather",
|
|
"Sunshine",
|
|
21.6414,
|
|
92,
|
|
1099,
|
|
0.5,
|
|
UnitOfTemperature.CELSIUS,
|
|
UnitOfPressure.HPA,
|
|
UnitOfSpeed.METERS_PER_SECOND,
|
|
[
|
|
[ATTR_CONDITION_RAINY, 1, 22, 15, 60],
|
|
[ATTR_CONDITION_RAINY, 5, 19, 8, 30],
|
|
[ATTR_CONDITION_RAINY, 0, 15, 9, 10],
|
|
[ATTR_CONDITION_RAINY, 0, 12, 6, 0],
|
|
[ATTR_CONDITION_RAINY, 2, 14, 7, 20],
|
|
[ATTR_CONDITION_RAINY, 15, 18, 7, 0],
|
|
[ATTR_CONDITION_RAINY, 0.2, 21, 12, 100],
|
|
],
|
|
None,
|
|
[
|
|
[ATTR_CONDITION_RAINY, 1, 22, 15, 60],
|
|
[ATTR_CONDITION_RAINY, 5, 19, 8, 30],
|
|
[ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
|
|
[ATTR_CONDITION_SUNNY, 0, 12, 6, 0],
|
|
[ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20],
|
|
[ATTR_CONDITION_RAINY, 15, 18, 7, 0],
|
|
[ATTR_CONDITION_FOG, 0.2, 21, 12, 100],
|
|
],
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
class DemoWeather(WeatherEntity):
|
|
"""Representation of a weather condition."""
|
|
|
|
_attr_attribution = "Powered by Home Assistant"
|
|
_attr_should_poll = False
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
condition: str,
|
|
temperature: float,
|
|
humidity: float,
|
|
pressure: float,
|
|
wind_speed: float,
|
|
temperature_unit: str,
|
|
pressure_unit: str,
|
|
wind_speed_unit: str,
|
|
forecast_daily: list[list] | None,
|
|
forecast_hourly: list[list] | None,
|
|
forecast_twice_daily: list[list] | None,
|
|
) -> None:
|
|
"""Initialize the Demo weather."""
|
|
self._attr_name = f"Test Weather {name}"
|
|
self._attr_unique_id = f"test-weather-{name.lower()}"
|
|
self._condition = condition
|
|
self._native_temperature = temperature
|
|
self._native_temperature_unit = temperature_unit
|
|
self._humidity = humidity
|
|
self._native_pressure = pressure
|
|
self._native_pressure_unit = pressure_unit
|
|
self._native_wind_speed = wind_speed
|
|
self._native_wind_speed_unit = wind_speed_unit
|
|
self._forecast_daily = forecast_daily
|
|
self._forecast_hourly = forecast_hourly
|
|
self._forecast_twice_daily = forecast_twice_daily
|
|
self._attr_supported_features = 0
|
|
if self._forecast_daily:
|
|
self._attr_supported_features |= WeatherEntityFeature.FORECAST_DAILY
|
|
if self._forecast_hourly:
|
|
self._attr_supported_features |= WeatherEntityFeature.FORECAST_HOURLY
|
|
if self._forecast_twice_daily:
|
|
self._attr_supported_features |= WeatherEntityFeature.FORECAST_TWICE_DAILY
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
"""Set up a timer updating the forecasts."""
|
|
|
|
async def update_forecasts(_) -> None:
|
|
if self._forecast_daily:
|
|
self._forecast_daily = (
|
|
self._forecast_daily[1:] + self._forecast_daily[:1]
|
|
)
|
|
if self._forecast_hourly:
|
|
self._forecast_hourly = (
|
|
self._forecast_hourly[1:] + self._forecast_hourly[:1]
|
|
)
|
|
if self._forecast_twice_daily:
|
|
self._forecast_twice_daily = (
|
|
self._forecast_twice_daily[1:] + self._forecast_twice_daily[:1]
|
|
)
|
|
await self.async_update_listeners(None)
|
|
|
|
self.async_on_remove(
|
|
async_track_time_interval(
|
|
self.hass, update_forecasts, timedelta(seconds=30)
|
|
)
|
|
)
|
|
|
|
@property
|
|
def native_temperature(self) -> float:
|
|
"""Return the temperature."""
|
|
return self._native_temperature
|
|
|
|
@property
|
|
def native_temperature_unit(self) -> str:
|
|
"""Return the unit of measurement."""
|
|
return self._native_temperature_unit
|
|
|
|
@property
|
|
def humidity(self) -> float:
|
|
"""Return the humidity."""
|
|
return self._humidity
|
|
|
|
@property
|
|
def native_wind_speed(self) -> float:
|
|
"""Return the wind speed."""
|
|
return self._native_wind_speed
|
|
|
|
@property
|
|
def native_wind_speed_unit(self) -> str:
|
|
"""Return the wind speed."""
|
|
return self._native_wind_speed_unit
|
|
|
|
@property
|
|
def native_pressure(self) -> float:
|
|
"""Return the pressure."""
|
|
return self._native_pressure
|
|
|
|
@property
|
|
def native_pressure_unit(self) -> str:
|
|
"""Return the pressure."""
|
|
return self._native_pressure_unit
|
|
|
|
@property
|
|
def condition(self) -> str:
|
|
"""Return the weather condition."""
|
|
return CONDITION_MAP[self._condition.lower()]
|
|
|
|
async def async_forecast_daily(self) -> list[Forecast]:
|
|
"""Return the daily forecast."""
|
|
if self._forecast_daily is None:
|
|
return []
|
|
reftime = dt_util.now().replace(hour=16, minute=00)
|
|
|
|
forecast_data = []
|
|
for entry in self._forecast_daily:
|
|
data_dict = Forecast(
|
|
datetime=reftime.isoformat(),
|
|
condition=entry[0],
|
|
precipitation=entry[1],
|
|
temperature=entry[2],
|
|
templow=entry[3],
|
|
precipitation_probability=entry[4],
|
|
)
|
|
reftime = reftime + timedelta(hours=24)
|
|
forecast_data.append(data_dict)
|
|
|
|
return forecast_data
|
|
|
|
async def async_forecast_hourly(self) -> list[Forecast]:
|
|
"""Return the hourly forecast."""
|
|
if self._forecast_hourly is None:
|
|
return []
|
|
reftime = dt_util.now().replace(hour=16, minute=00)
|
|
|
|
forecast_data = []
|
|
for entry in self._forecast_hourly:
|
|
data_dict = Forecast(
|
|
datetime=reftime.isoformat(),
|
|
condition=entry[0],
|
|
precipitation=entry[1],
|
|
temperature=entry[2],
|
|
templow=entry[3],
|
|
precipitation_probability=entry[4],
|
|
)
|
|
reftime = reftime + timedelta(hours=1)
|
|
forecast_data.append(data_dict)
|
|
|
|
return forecast_data
|
|
|
|
async def async_forecast_twice_daily(self) -> list[Forecast]:
|
|
"""Return the twice daily forecast."""
|
|
if self._forecast_twice_daily is None:
|
|
return []
|
|
reftime = dt_util.now().replace(hour=11, minute=00)
|
|
|
|
forecast_data = []
|
|
for entry in self._forecast_twice_daily:
|
|
try:
|
|
data_dict = Forecast(
|
|
datetime=reftime.isoformat(),
|
|
condition=entry[0],
|
|
precipitation=entry[1],
|
|
temperature=entry[2],
|
|
templow=entry[3],
|
|
precipitation_probability=entry[4],
|
|
is_daytime=entry[5],
|
|
)
|
|
reftime = reftime + timedelta(hours=12)
|
|
forecast_data.append(data_dict)
|
|
except IndexError:
|
|
continue
|
|
|
|
return forecast_data
|