Home Assistant Unofficial Reference 2024.12.1
weather.py
Go to the documentation of this file.
1 """Support for Buienradar.nl weather service."""
2 
3 import logging
4 
5 from buienradar.constants import (
6  CONDCODE,
7  CONDITION,
8  DATETIME,
9  MAX_TEMP,
10  MIN_TEMP,
11  RAIN,
12  WINDAZIMUTH,
13  WINDSPEED,
14 )
15 
17  ATTR_CONDITION_CLOUDY,
18  ATTR_CONDITION_EXCEPTIONAL,
19  ATTR_CONDITION_FOG,
20  ATTR_CONDITION_HAIL,
21  ATTR_CONDITION_LIGHTNING,
22  ATTR_CONDITION_LIGHTNING_RAINY,
23  ATTR_CONDITION_PARTLYCLOUDY,
24  ATTR_CONDITION_POURING,
25  ATTR_CONDITION_RAINY,
26  ATTR_CONDITION_SNOWY,
27  ATTR_CONDITION_SNOWY_RAINY,
28  ATTR_CONDITION_SUNNY,
29  ATTR_CONDITION_WINDY,
30  ATTR_CONDITION_WINDY_VARIANT,
31  ATTR_FORECAST_CONDITION,
32  ATTR_FORECAST_NATIVE_PRECIPITATION,
33  ATTR_FORECAST_NATIVE_TEMP,
34  ATTR_FORECAST_NATIVE_TEMP_LOW,
35  ATTR_FORECAST_NATIVE_WIND_SPEED,
36  ATTR_FORECAST_TIME,
37  ATTR_FORECAST_WIND_BEARING,
38  Forecast,
39  WeatherEntity,
40  WeatherEntityFeature,
41 )
42 from homeassistant.const import (
43  CONF_LATITUDE,
44  CONF_LONGITUDE,
45  CONF_NAME,
46  Platform,
47  UnitOfLength,
48  UnitOfPrecipitationDepth,
49  UnitOfPressure,
50  UnitOfSpeed,
51  UnitOfTemperature,
52 )
53 from homeassistant.core import HomeAssistant, callback
54 from homeassistant.helpers.entity_platform import AddEntitiesCallback
55 
56 from . import BuienRadarConfigEntry
57 from .const import DEFAULT_TIMEFRAME
58 from .util import BrData
59 
60 _LOGGER = logging.getLogger(__name__)
61 
62 CONF_FORECAST = "forecast"
63 
64 DATA_CONDITION = "buienradar_condition"
65 
66 CONDITION_CLASSES = {
67  ATTR_CONDITION_CLOUDY: ("c", "p"),
68  ATTR_CONDITION_FOG: ("d", "n"),
69  ATTR_CONDITION_HAIL: (),
70  ATTR_CONDITION_LIGHTNING: ("g",),
71  ATTR_CONDITION_LIGHTNING_RAINY: ("s",),
72  ATTR_CONDITION_PARTLYCLOUDY: (
73  "b",
74  "j",
75  "o",
76  "r",
77  ),
78  ATTR_CONDITION_POURING: ("l", "q"),
79  ATTR_CONDITION_RAINY: ("f", "h", "k", "m"),
80  ATTR_CONDITION_SNOWY: ("u", "i", "v", "t"),
81  ATTR_CONDITION_SNOWY_RAINY: ("w",),
82  ATTR_CONDITION_SUNNY: ("a",),
83  ATTR_CONDITION_WINDY: (),
84  ATTR_CONDITION_WINDY_VARIANT: (),
85  ATTR_CONDITION_EXCEPTIONAL: (),
86 }
87 CONDITION_MAP = {
88  cond_code: cond_ha
89  for cond_ha, cond_codes in CONDITION_CLASSES.items()
90  for cond_code in cond_codes
91 }
92 
93 
95  hass: HomeAssistant,
96  entry: BuienRadarConfigEntry,
97  async_add_entities: AddEntitiesCallback,
98 ) -> None:
99  """Set up the buienradar platform."""
100  config = entry.data
101 
102  latitude = config.get(CONF_LATITUDE, hass.config.latitude)
103  longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
104 
105  if None in (latitude, longitude):
106  _LOGGER.error("Latitude or longitude not set in Home Assistant config")
107  return
108 
109  coordinates = {CONF_LATITUDE: float(latitude), CONF_LONGITUDE: float(longitude)}
110 
111  # create weather entity:
112  _LOGGER.debug("Initializing buienradar weather: coordinates %s", coordinates)
113  entities = [BrWeather(config, coordinates)]
114 
115  # create weather data:
116  data = BrData(hass, coordinates, DEFAULT_TIMEFRAME, entities)
117  entry.runtime_data[Platform.WEATHER] = data
118  await data.async_update()
119 
120  async_add_entities(entities)
121 
122 
124  """Representation of a weather condition."""
125 
126  _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
127  _attr_native_pressure_unit = UnitOfPressure.HPA
128  _attr_native_temperature_unit = UnitOfTemperature.CELSIUS
129  _attr_native_visibility_unit = UnitOfLength.METERS
130  _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND
131  _attr_should_poll = False
132  _attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
133 
134  def __init__(self, config, coordinates) -> None:
135  """Initialize the platform with a data instance and station name."""
136  self._stationname_stationname = config.get(CONF_NAME, "Buienradar")
137  self._attr_name_attr_name = self._stationname_stationname or f"BR {'(unknown station)'}"
138 
139  self._attr_unique_id_attr_unique_id = (
140  f"{coordinates[CONF_LATITUDE]:2.6f}{coordinates[CONF_LONGITUDE]:2.6f}"
141  )
142  self._forecast_forecast: list | None = None
143 
144  @callback
145  def data_updated(self, data: BrData) -> None:
146  """Update data."""
147  self._attr_attribution_attr_attribution = data.attribution
148  self._attr_condition_attr_condition = self._calc_condition_calc_condition(data)
149  self._forecast_forecast = self._calc_forecast_calc_forecast(data)
150  self._attr_humidity_attr_humidity = data.humidity
151  self._attr_name_attr_name = (
152  self._stationname_stationname or f"BR {data.stationname or '(unknown station)'}"
153  )
154  self._attr_native_pressure_attr_native_pressure = data.pressure
155  self._attr_native_temperature_attr_native_temperature = data.temperature
156  self._attr_native_visibility_attr_native_visibility = data.visibility
157  self._attr_native_wind_speed_attr_native_wind_speed = data.wind_speed
158  self._attr_wind_bearing_attr_wind_bearing = data.wind_bearing
159 
160  if not self.hasshass:
161  return
162  self.async_write_ha_stateasync_write_ha_state()
163  assert self.platformplatform.config_entry
164  self.platformplatform.config_entry.async_create_task(
165  self.hasshass, self.async_update_listenersasync_update_listeners(("daily",))
166  )
167 
168  def _calc_condition(self, data: BrData):
169  """Return the current condition."""
170  if data.condition and (ccode := data.condition.get(CONDCODE)):
171  return CONDITION_MAP.get(ccode)
172  return None
173 
174  def _calc_forecast(self, data: BrData):
175  """Return the forecast array."""
176  fcdata_out = []
177 
178  if not data.forecast:
179  return None
180 
181  for data_in in data.forecast:
182  # remap keys from external library to
183  # keys understood by the weather component:
184  condcode = data_in.get(CONDITION, {}).get(CONDCODE)
185  data_out = {
186  ATTR_FORECAST_TIME: data_in.get(DATETIME).isoformat(),
187  ATTR_FORECAST_CONDITION: CONDITION_MAP.get(condcode),
188  ATTR_FORECAST_NATIVE_TEMP_LOW: data_in.get(MIN_TEMP),
189  ATTR_FORECAST_NATIVE_TEMP: data_in.get(MAX_TEMP),
190  ATTR_FORECAST_NATIVE_PRECIPITATION: data_in.get(RAIN),
191  ATTR_FORECAST_WIND_BEARING: data_in.get(WINDAZIMUTH),
192  ATTR_FORECAST_NATIVE_WIND_SPEED: data_in.get(WINDSPEED),
193  }
194 
195  fcdata_out.append(data_out)
196 
197  return fcdata_out
198 
199  async def async_forecast_daily(self) -> list[Forecast] | None:
200  """Return the daily forecast in native units."""
201  return self._forecast_forecast
None __init__(self, config, coordinates)
Definition: weather.py:134
None async_update_listeners(self, Iterable[Literal["daily", "hourly", "twice_daily"]]|None forecast_types)
Definition: __init__.py:961
None async_setup_entry(HomeAssistant hass, BuienRadarConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: weather.py:98
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88