Home Assistant Unofficial Reference 2024.12.1
weather.py
Go to the documentation of this file.
1 """Demo platform that offers fake meteorological data."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 
8  ATTR_CONDITION_CLOUDY,
9  ATTR_CONDITION_EXCEPTIONAL,
10  ATTR_CONDITION_FOG,
11  ATTR_CONDITION_HAIL,
12  ATTR_CONDITION_LIGHTNING,
13  ATTR_CONDITION_LIGHTNING_RAINY,
14  ATTR_CONDITION_PARTLYCLOUDY,
15  ATTR_CONDITION_POURING,
16  ATTR_CONDITION_RAINY,
17  ATTR_CONDITION_SNOWY,
18  ATTR_CONDITION_SNOWY_RAINY,
19  ATTR_CONDITION_SUNNY,
20  ATTR_CONDITION_WINDY,
21  ATTR_CONDITION_WINDY_VARIANT,
22  Forecast,
23  WeatherEntity,
24  WeatherEntityFeature,
25 )
26 from homeassistant.config_entries import ConfigEntry
27 from homeassistant.const import UnitOfPressure, UnitOfSpeed, UnitOfTemperature
28 from homeassistant.core import HomeAssistant
29 from homeassistant.helpers.entity_platform import AddEntitiesCallback
30 from homeassistant.helpers.event import async_track_time_interval
31 import homeassistant.util.dt as dt_util
32 
33 CONDITION_CLASSES: dict[str, list[str]] = {
34  ATTR_CONDITION_CLOUDY: [],
35  ATTR_CONDITION_FOG: [],
36  ATTR_CONDITION_HAIL: [],
37  ATTR_CONDITION_LIGHTNING: [],
38  ATTR_CONDITION_LIGHTNING_RAINY: [],
39  ATTR_CONDITION_PARTLYCLOUDY: [],
40  ATTR_CONDITION_POURING: [],
41  ATTR_CONDITION_RAINY: ["shower rain"],
42  ATTR_CONDITION_SNOWY: [],
43  ATTR_CONDITION_SNOWY_RAINY: [],
44  ATTR_CONDITION_SUNNY: ["sunshine"],
45  ATTR_CONDITION_WINDY: [],
46  ATTR_CONDITION_WINDY_VARIANT: [],
47  ATTR_CONDITION_EXCEPTIONAL: [],
48 }
49 CONDITION_MAP = {
50  cond_code: cond_ha
51  for cond_ha, cond_codes in CONDITION_CLASSES.items()
52  for cond_code in cond_codes
53 }
54 
55 
57  hass: HomeAssistant,
58  config_entry: ConfigEntry,
59  async_add_entities: AddEntitiesCallback,
60 ) -> None:
61  """Set up the Demo config entry."""
63  [
65  "Legacy + daily weather",
66  "Sunshine",
67  21.6414,
68  92,
69  1099,
70  0.5,
71  UnitOfTemperature.CELSIUS,
72  UnitOfPressure.HPA,
73  UnitOfSpeed.METERS_PER_SECOND,
74  [
75  [ATTR_CONDITION_RAINY, 1, 22, 15, 60],
76  [ATTR_CONDITION_RAINY, 5, 19, 8, 30],
77  [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
78  [ATTR_CONDITION_SUNNY, 0, 12, 6, 0],
79  [ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20],
80  [ATTR_CONDITION_RAINY, 15, 18, 7, 0],
81  [ATTR_CONDITION_FOG, 0.2, 21, 12, 100],
82  ],
83  None,
84  None,
85  ),
87  "Daily + hourly weather",
88  "Shower rain",
89  -12,
90  54,
91  987,
92  4.8,
93  UnitOfTemperature.FAHRENHEIT,
94  UnitOfPressure.INHG,
95  UnitOfSpeed.MILES_PER_HOUR,
96  [
97  [ATTR_CONDITION_SNOWY, 2, -10, -15, 60],
98  [ATTR_CONDITION_PARTLYCLOUDY, 1, -13, -14, 25],
99  [ATTR_CONDITION_SUNNY, 0, -18, -22, 70],
100  [ATTR_CONDITION_SUNNY, 0.1, -23, -23, 90],
101  [ATTR_CONDITION_SNOWY, 4, -19, -20, 40],
102  [ATTR_CONDITION_SUNNY, 0.3, -14, -19, 0],
103  [ATTR_CONDITION_SUNNY, 0, -9, -12, 0],
104  ],
105  [
106  [ATTR_CONDITION_SUNNY, 2, -10, -15, 60],
107  [ATTR_CONDITION_SUNNY, 1, -13, -14, 25],
108  [ATTR_CONDITION_SUNNY, 0, -18, -22, 70],
109  [ATTR_CONDITION_SUNNY, 0.1, -23, -23, 90],
110  [ATTR_CONDITION_SUNNY, 4, -19, -20, 40],
111  [ATTR_CONDITION_SUNNY, 0.3, -14, -19, 0],
112  [ATTR_CONDITION_SUNNY, 0, -9, -12, 0],
113  ],
114  None,
115  ),
116  DemoWeather(
117  "Daily + bi-daily + hourly weather",
118  "Sunshine",
119  21.6414,
120  92,
121  1099,
122  0.5,
123  UnitOfTemperature.CELSIUS,
124  UnitOfPressure.HPA,
125  UnitOfSpeed.METERS_PER_SECOND,
126  [
127  [ATTR_CONDITION_RAINY, 1, 22, 15, 60],
128  [ATTR_CONDITION_RAINY, 5, 19, 8, 30],
129  [ATTR_CONDITION_RAINY, 0, 15, 9, 10],
130  [ATTR_CONDITION_RAINY, 0, 12, 6, 0],
131  [ATTR_CONDITION_RAINY, 2, 14, 7, 20],
132  [ATTR_CONDITION_RAINY, 15, 18, 7, 0],
133  [ATTR_CONDITION_RAINY, 0.2, 21, 12, 100],
134  ],
135  [
136  [ATTR_CONDITION_CLOUDY, 1, 22, 15, 60],
137  [ATTR_CONDITION_CLOUDY, 5, 19, 8, 30],
138  [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
139  [ATTR_CONDITION_CLOUDY, 0, 12, 6, 0],
140  [ATTR_CONDITION_CLOUDY, 2, 14, 7, 20],
141  [ATTR_CONDITION_CLOUDY, 15, 18, 7, 0],
142  [ATTR_CONDITION_CLOUDY, 0.2, 21, 12, 100],
143  ],
144  [
145  [ATTR_CONDITION_RAINY, 1, 22, 15, 60, True],
146  [ATTR_CONDITION_RAINY, 5, 19, 8, 30, False],
147  [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10, True],
148  [ATTR_CONDITION_SUNNY, 0, 12, 6, 0, False],
149  [ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20, True],
150  [ATTR_CONDITION_RAINY, 15, 18, 7, 0, False],
151  [ATTR_CONDITION_FOG, 0.2, 21, 12, 100, True],
152  ],
153  ),
154  DemoWeather(
155  "Hourly + bi-daily weather",
156  "Sunshine",
157  21.6414,
158  92,
159  1099,
160  0.5,
161  UnitOfTemperature.CELSIUS,
162  UnitOfPressure.HPA,
163  UnitOfSpeed.METERS_PER_SECOND,
164  None,
165  [
166  [ATTR_CONDITION_CLOUDY, 1, 22, 15, 60],
167  [ATTR_CONDITION_CLOUDY, 5, 19, 8, 30],
168  [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
169  [ATTR_CONDITION_CLOUDY, 0, 12, 6, 0],
170  [ATTR_CONDITION_CLOUDY, 2, 14, 7, 20],
171  [ATTR_CONDITION_CLOUDY, 15, 18, 7, 0],
172  [ATTR_CONDITION_CLOUDY, 0.2, 21, 12, 100],
173  ],
174  [
175  [ATTR_CONDITION_RAINY, 1, 22, 15, 60, True],
176  [ATTR_CONDITION_RAINY, 5, 19, 8, 30, False],
177  [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10, True],
178  [ATTR_CONDITION_SUNNY, 0, 12, 6, 0, False],
179  [ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20, True],
180  [ATTR_CONDITION_RAINY, 15, 18, 7, 0, False],
181  [ATTR_CONDITION_FOG, 0.2, 21, 12, 100, True],
182  ],
183  ),
184  DemoWeather(
185  "Daily + broken bi-daily weather",
186  "Sunshine",
187  21.6414,
188  92,
189  1099,
190  0.5,
191  UnitOfTemperature.CELSIUS,
192  UnitOfPressure.HPA,
193  UnitOfSpeed.METERS_PER_SECOND,
194  [
195  [ATTR_CONDITION_RAINY, 1, 22, 15, 60],
196  [ATTR_CONDITION_RAINY, 5, 19, 8, 30],
197  [ATTR_CONDITION_RAINY, 0, 15, 9, 10],
198  [ATTR_CONDITION_RAINY, 0, 12, 6, 0],
199  [ATTR_CONDITION_RAINY, 2, 14, 7, 20],
200  [ATTR_CONDITION_RAINY, 15, 18, 7, 0],
201  [ATTR_CONDITION_RAINY, 0.2, 21, 12, 100],
202  ],
203  None,
204  [
205  [ATTR_CONDITION_RAINY, 1, 22, 15, 60],
206  [ATTR_CONDITION_RAINY, 5, 19, 8, 30],
207  [ATTR_CONDITION_CLOUDY, 0, 15, 9, 10],
208  [ATTR_CONDITION_SUNNY, 0, 12, 6, 0],
209  [ATTR_CONDITION_PARTLYCLOUDY, 2, 14, 7, 20],
210  [ATTR_CONDITION_RAINY, 15, 18, 7, 0],
211  [ATTR_CONDITION_FOG, 0.2, 21, 12, 100],
212  ],
213  ),
214  ]
215  )
216 
217 
219  """Representation of a weather condition."""
220 
221  _attr_attribution = "Powered by Home Assistant"
222  _attr_should_poll = False
223 
224  def __init__(
225  self,
226  name: str,
227  condition: str,
228  temperature: float,
229  humidity: float,
230  pressure: float,
231  wind_speed: float,
232  temperature_unit: str,
233  pressure_unit: str,
234  wind_speed_unit: str,
235  forecast_daily: list[list] | None,
236  forecast_hourly: list[list] | None,
237  forecast_twice_daily: list[list] | None,
238  ) -> None:
239  """Initialize the Demo weather."""
240  self._attr_name_attr_name = f"Test Weather {name}"
241  self._attr_unique_id_attr_unique_id = f"test-weather-{name.lower()}"
242  self._condition_condition = condition
243  self._native_temperature_native_temperature = temperature
244  self._native_temperature_unit_native_temperature_unit = temperature_unit
245  self._humidity_humidity = humidity
246  self._native_pressure_native_pressure = pressure
247  self._native_pressure_unit_native_pressure_unit = pressure_unit
248  self._native_wind_speed_native_wind_speed = wind_speed
249  self._native_wind_speed_unit_native_wind_speed_unit = wind_speed_unit
250  self._forecast_daily_forecast_daily = forecast_daily
251  self._forecast_hourly_forecast_hourly = forecast_hourly
252  self._forecast_twice_daily_forecast_twice_daily = forecast_twice_daily
253  self._attr_supported_features_attr_supported_features = 0
254  if self._forecast_daily_forecast_daily:
255  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_DAILY
256  if self._forecast_hourly_forecast_hourly:
257  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_HOURLY
258  if self._forecast_twice_daily_forecast_twice_daily:
259  self._attr_supported_features_attr_supported_features |= WeatherEntityFeature.FORECAST_TWICE_DAILY
260 
261  async def async_added_to_hass(self) -> None:
262  """Set up a timer updating the forecasts."""
263 
264  async def update_forecasts(_) -> None:
265  if self._forecast_daily_forecast_daily:
266  self._forecast_daily_forecast_daily = (
267  self._forecast_daily_forecast_daily[1:] + self._forecast_daily_forecast_daily[:1]
268  )
269  if self._forecast_hourly_forecast_hourly:
270  self._forecast_hourly_forecast_hourly = (
271  self._forecast_hourly_forecast_hourly[1:] + self._forecast_hourly_forecast_hourly[:1]
272  )
273  if self._forecast_twice_daily_forecast_twice_daily:
274  self._forecast_twice_daily_forecast_twice_daily = (
275  self._forecast_twice_daily_forecast_twice_daily[1:] + self._forecast_twice_daily_forecast_twice_daily[:1]
276  )
277  await self.async_update_listenersasync_update_listeners(None)
278 
279  self.async_on_removeasync_on_remove(
281  self.hasshass, update_forecasts, timedelta(seconds=30)
282  )
283  )
284 
285  @property
286  def native_temperature(self) -> float:
287  """Return the temperature."""
288  return self._native_temperature_native_temperature
289 
290  @property
291  def native_temperature_unit(self) -> str:
292  """Return the unit of measurement."""
293  return self._native_temperature_unit_native_temperature_unit
294 
295  @property
296  def humidity(self) -> float:
297  """Return the humidity."""
298  return self._humidity_humidity
299 
300  @property
301  def native_wind_speed(self) -> float:
302  """Return the wind speed."""
303  return self._native_wind_speed_native_wind_speed
304 
305  @property
306  def native_wind_speed_unit(self) -> str:
307  """Return the wind speed."""
308  return self._native_wind_speed_unit_native_wind_speed_unit
309 
310  @property
311  def native_pressure(self) -> float:
312  """Return the pressure."""
313  return self._native_pressure_native_pressure
314 
315  @property
316  def native_pressure_unit(self) -> str:
317  """Return the pressure."""
318  return self._native_pressure_unit_native_pressure_unit
319 
320  @property
321  def condition(self) -> str:
322  """Return the weather condition."""
323  return CONDITION_MAP[self._condition_condition.lower()]
324 
325  async def async_forecast_daily(self) -> list[Forecast]:
326  """Return the daily forecast."""
327  if self._forecast_daily_forecast_daily is None:
328  return []
329  reftime = dt_util.now().replace(hour=16, minute=00)
330 
331  forecast_data = []
332  for entry in self._forecast_daily_forecast_daily:
333  data_dict = Forecast(
334  datetime=reftime.isoformat(),
335  condition=entry[0],
336  precipitation=entry[1],
337  temperature=entry[2],
338  templow=entry[3],
339  precipitation_probability=entry[4],
340  )
341  reftime = reftime + timedelta(hours=24)
342  forecast_data.append(data_dict)
343 
344  return forecast_data
345 
346  async def async_forecast_hourly(self) -> list[Forecast]:
347  """Return the hourly forecast."""
348  if self._forecast_hourly_forecast_hourly is None:
349  return []
350  reftime = dt_util.now().replace(hour=16, minute=00)
351 
352  forecast_data = []
353  for entry in self._forecast_hourly_forecast_hourly:
354  data_dict = Forecast(
355  datetime=reftime.isoformat(),
356  condition=entry[0],
357  precipitation=entry[1],
358  temperature=entry[2],
359  templow=entry[3],
360  precipitation_probability=entry[4],
361  )
362  reftime = reftime + timedelta(hours=1)
363  forecast_data.append(data_dict)
364 
365  return forecast_data
366 
367  async def async_forecast_twice_daily(self) -> list[Forecast]:
368  """Return the twice daily forecast."""
369  if self._forecast_twice_daily_forecast_twice_daily is None:
370  return []
371  reftime = dt_util.now().replace(hour=11, minute=00)
372 
373  forecast_data = []
374  for entry in self._forecast_twice_daily_forecast_twice_daily:
375  try:
376  data_dict = Forecast(
377  datetime=reftime.isoformat(),
378  condition=entry[0],
379  precipitation=entry[1],
380  temperature=entry[2],
381  templow=entry[3],
382  precipitation_probability=entry[4],
383  is_daytime=entry[5],
384  )
385  reftime = reftime + timedelta(hours=12)
386  forecast_data.append(data_dict)
387  except IndexError:
388  continue
389 
390  return forecast_data
None __init__(self, str name, str condition, float temperature, float humidity, float pressure, float wind_speed, str temperature_unit, str pressure_unit, str wind_speed_unit, list[list]|None forecast_daily, list[list]|None forecast_hourly, list[list]|None forecast_twice_daily)
Definition: weather.py:238
None async_update_listeners(self, Iterable[Literal["daily", "hourly", "twice_daily"]]|None forecast_types)
Definition: __init__.py:961
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: weather.py:60
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
Definition: event.py:1679