Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Weather data coordinator for the HKO API."""
2 
3 from asyncio import timeout
4 from datetime import timedelta
5 import logging
6 from typing import Any
7 
8 from aiohttp import ClientSession
9 from hko import HKO, HKOError
10 
12  ATTR_CONDITION_CLOUDY,
13  ATTR_CONDITION_FOG,
14  ATTR_CONDITION_HAIL,
15  ATTR_CONDITION_LIGHTNING_RAINY,
16  ATTR_CONDITION_PARTLYCLOUDY,
17  ATTR_CONDITION_POURING,
18  ATTR_CONDITION_RAINY,
19  ATTR_CONDITION_SNOWY,
20  ATTR_CONDITION_SNOWY_RAINY,
21  ATTR_CONDITION_SUNNY,
22  ATTR_CONDITION_WINDY,
23  ATTR_CONDITION_WINDY_VARIANT,
24  ATTR_FORECAST_CONDITION,
25  ATTR_FORECAST_TEMP,
26  ATTR_FORECAST_TEMP_LOW,
27  ATTR_FORECAST_TIME,
28 )
29 from homeassistant.core import HomeAssistant
30 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
31 
32 from .const import (
33  API_CURRENT,
34  API_DATA,
35  API_FORECAST,
36  API_FORECAST_DATE,
37  API_FORECAST_ICON,
38  API_FORECAST_MAX_TEMP,
39  API_FORECAST_MIN_TEMP,
40  API_FORECAST_WEATHER,
41  API_HUMIDITY,
42  API_PLACE,
43  API_TEMPERATURE,
44  API_VALUE,
45  API_WEATHER_FORECAST,
46  DOMAIN,
47  ICON_CONDITION_MAP,
48  WEATHER_INFO_AT_TIMES_AT_FIRST,
49  WEATHER_INFO_CLOUD,
50  WEATHER_INFO_FINE,
51  WEATHER_INFO_FOG,
52  WEATHER_INFO_HEAVY,
53  WEATHER_INFO_INTERVAL,
54  WEATHER_INFO_ISOLATED,
55  WEATHER_INFO_MIST,
56  WEATHER_INFO_OVERCAST,
57  WEATHER_INFO_PERIOD,
58  WEATHER_INFO_RAIN,
59  WEATHER_INFO_SHOWER,
60  WEATHER_INFO_SNOW,
61  WEATHER_INFO_SUNNY,
62  WEATHER_INFO_THUNDERSTORM,
63  WEATHER_INFO_WIND,
64 )
65 
66 _LOGGER = logging.getLogger(__name__)
67 
68 
70  """HKO Update Coordinator."""
71 
72  def __init__(
73  self, hass: HomeAssistant, session: ClientSession, district: str, location: str
74  ) -> None:
75  """Update data via library."""
76  self.locationlocation = location
77  self.districtdistrict = district
78  self.hkohko = HKO(session)
79 
80  super().__init__(
81  hass,
82  _LOGGER,
83  name=DOMAIN,
84  update_interval=timedelta(minutes=15),
85  )
86 
87  async def _async_update_data(self) -> dict[str, Any]:
88  """Update data via HKO library."""
89  try:
90  async with timeout(60):
91  rhrread = await self.hkohko.weather("rhrread")
92  fnd = await self.hkohko.weather("fnd")
93  except HKOError as error:
94  raise UpdateFailed(error) from error
95  return {
96  API_CURRENT: self._convert_current_convert_current(rhrread),
97  API_FORECAST: [
98  self._convert_forecast_convert_forecast(item) for item in fnd[API_WEATHER_FORECAST]
99  ],
100  }
101 
102  def _convert_current(self, data: dict[str, Any]) -> dict[str, Any]:
103  """Return temperature and humidity in the appropriate format."""
104  return {
105  API_HUMIDITY: data[API_HUMIDITY][API_DATA][0][API_VALUE],
106  API_TEMPERATURE: next(
107  (
108  item[API_VALUE]
109  for item in data[API_TEMPERATURE][API_DATA]
110  if item[API_PLACE] == self.locationlocation
111  ),
112  0,
113  ),
114  }
115 
116  def _convert_forecast(self, data: dict[str, Any]) -> dict[str, Any]:
117  """Return daily forecast in the appropriate format."""
118  date = data[API_FORECAST_DATE]
119  return {
120  ATTR_FORECAST_CONDITION: self._convert_icon_condition_convert_icon_condition(
121  data[API_FORECAST_ICON], data[API_FORECAST_WEATHER]
122  ),
123  ATTR_FORECAST_TEMP: data[API_FORECAST_MAX_TEMP][API_VALUE],
124  ATTR_FORECAST_TEMP_LOW: data[API_FORECAST_MIN_TEMP][API_VALUE],
125  ATTR_FORECAST_TIME: f"{date[0:4]}-{date[4:6]}-{date[6:8]}T00:00:00+08:00",
126  }
127 
128  def _convert_icon_condition(self, icon_code: int, info: str) -> str:
129  """Return the condition corresponding to an icon code."""
130  for condition, codes in ICON_CONDITION_MAP.items():
131  if icon_code in codes:
132  return condition
133  return self._convert_info_condition_convert_info_condition(info)
134 
135  def _convert_info_condition(self, info: str) -> str:
136  """Return the condition corresponding to the weather info."""
137  info = info.lower()
138  if WEATHER_INFO_RAIN in info:
139  return ATTR_CONDITION_HAIL
140  if WEATHER_INFO_SNOW in info and WEATHER_INFO_RAIN in info:
141  return ATTR_CONDITION_SNOWY_RAINY
142  if WEATHER_INFO_SNOW in info:
143  return ATTR_CONDITION_SNOWY
144  if WEATHER_INFO_FOG in info or WEATHER_INFO_MIST in info:
145  return ATTR_CONDITION_FOG
146  if WEATHER_INFO_WIND in info and WEATHER_INFO_CLOUD in info:
147  return ATTR_CONDITION_WINDY_VARIANT
148  if WEATHER_INFO_WIND in info:
149  return ATTR_CONDITION_WINDY
150  if WEATHER_INFO_THUNDERSTORM in info and WEATHER_INFO_ISOLATED not in info:
151  return ATTR_CONDITION_LIGHTNING_RAINY
152  if (
153  (
154  WEATHER_INFO_RAIN in info
155  or WEATHER_INFO_SHOWER in info
156  or WEATHER_INFO_THUNDERSTORM in info
157  )
158  and WEATHER_INFO_HEAVY in info
159  and WEATHER_INFO_SUNNY not in info
160  and WEATHER_INFO_FINE not in info
161  and WEATHER_INFO_AT_TIMES_AT_FIRST not in info
162  ):
163  return ATTR_CONDITION_POURING
164  if (
165  (
166  WEATHER_INFO_RAIN in info
167  or WEATHER_INFO_SHOWER in info
168  or WEATHER_INFO_THUNDERSTORM in info
169  )
170  and WEATHER_INFO_SUNNY not in info
171  and WEATHER_INFO_FINE not in info
172  ):
173  return ATTR_CONDITION_RAINY
174  if (WEATHER_INFO_CLOUD in info or WEATHER_INFO_OVERCAST in info) and not (
175  WEATHER_INFO_INTERVAL in info or WEATHER_INFO_PERIOD in info
176  ):
177  return ATTR_CONDITION_CLOUDY
178  if (WEATHER_INFO_SUNNY in info) and (
179  WEATHER_INFO_INTERVAL in info or WEATHER_INFO_PERIOD in info
180  ):
181  return ATTR_CONDITION_PARTLYCLOUDY
182  if (
183  WEATHER_INFO_SUNNY in info or WEATHER_INFO_FINE in info
184  ) and WEATHER_INFO_SHOWER not in info:
185  return ATTR_CONDITION_SUNNY
186  return ATTR_CONDITION_PARTLYCLOUDY
str _convert_icon_condition(self, int icon_code, str info)
Definition: coordinator.py:128
dict[str, Any] _convert_forecast(self, dict[str, Any] data)
Definition: coordinator.py:116
None __init__(self, HomeAssistant hass, ClientSession session, str district, str location)
Definition: coordinator.py:74
dict[str, Any] _convert_current(self, dict[str, Any] data)
Definition: coordinator.py:102