Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """DataUpdateCoordinator for WLED."""
2 
3 from __future__ import annotations
4 
5 from wled import (
6  WLED,
7  Device as WLEDDevice,
8  Releases,
9  WLEDConnectionClosedError,
10  WLEDError,
11  WLEDReleases,
12 )
13 
14 from homeassistant.config_entries import ConfigEntry
15 from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP
16 from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
17 from homeassistant.helpers.aiohttp_client import async_get_clientsession
18 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
19 
20 from .const import (
21  CONF_KEEP_MAIN_LIGHT,
22  DEFAULT_KEEP_MAIN_LIGHT,
23  DOMAIN,
24  LOGGER,
25  RELEASES_SCAN_INTERVAL,
26  SCAN_INTERVAL,
27 )
28 
29 
31  """Class to manage fetching WLED data from single endpoint."""
32 
33  keep_main_light: bool
34  config_entry: ConfigEntry
35 
36  def __init__(
37  self,
38  hass: HomeAssistant,
39  *,
40  entry: ConfigEntry,
41  ) -> None:
42  """Initialize global WLED data updater."""
43  self.keep_main_lightkeep_main_light = entry.options.get(
44  CONF_KEEP_MAIN_LIGHT, DEFAULT_KEEP_MAIN_LIGHT
45  )
46  self.wledwled = WLED(entry.data[CONF_HOST], session=async_get_clientsession(hass))
47  self.unsubunsub: CALLBACK_TYPE | None = None
48 
49  super().__init__(
50  hass,
51  LOGGER,
52  config_entry=entry,
53  name=DOMAIN,
54  update_interval=SCAN_INTERVAL,
55  )
56 
57  @property
58  def has_main_light(self) -> bool:
59  """Return if the coordinated device has a main light."""
60  return self.keep_main_lightkeep_main_light or (
61  self.datadata is not None and len(self.datadata.state.segments) > 1
62  )
63 
64  @callback
65  def _use_websocket(self) -> None:
66  """Use WebSocket for updates, instead of polling."""
67 
68  async def listen() -> None:
69  """Listen for state changes via WebSocket."""
70  try:
71  await self.wledwled.connect()
72  except WLEDError as err:
73  self.loggerlogger.info(err)
74  if self.unsubunsub:
75  self.unsubunsub()
76  self.unsubunsub = None
77  return
78 
79  try:
80  await self.wledwled.listen(callback=self.async_set_updated_dataasync_set_updated_data)
81  except WLEDConnectionClosedError as err:
82  self.last_update_successlast_update_successlast_update_success = False
83  self.loggerlogger.info(err)
84  except WLEDError as err:
85  self.last_update_successlast_update_successlast_update_success = False
86  self.async_update_listenersasync_update_listeners()
87  self.loggerlogger.error(err)
88 
89  # Ensure we are disconnected
90  await self.wledwled.disconnect()
91  if self.unsubunsub:
92  self.unsubunsub()
93  self.unsubunsub = None
94 
95  async def close_websocket(_: Event) -> None:
96  """Close WebSocket connection."""
97  self.unsubunsub = None
98  await self.wledwled.disconnect()
99 
100  # Clean disconnect WebSocket on Home Assistant shutdown
101  self.unsubunsub = self.hasshass.bus.async_listen_once(
102  EVENT_HOMEASSISTANT_STOP, close_websocket
103  )
104 
105  # Start listening
106  self.config_entryconfig_entry.async_create_background_task(
107  self.hasshass, listen(), "wled-listen"
108  )
109 
110  async def _async_update_data(self) -> WLEDDevice:
111  """Fetch data from WLED."""
112  try:
113  device = await self.wledwled.update()
114  except WLEDError as error:
115  raise UpdateFailed(f"Invalid response from API: {error}") from error
116 
117  # If the device supports a WebSocket, try activating it.
118  if (
119  device.info.websocket is not None
120  and not self.wledwled.connected
121  and not self.unsubunsub
122  ):
123  self._use_websocket_use_websocket()
124 
125  return device
126 
127 
129  """Class to manage fetching WLED releases."""
130 
131  def __init__(self, hass: HomeAssistant) -> None:
132  """Initialize global WLED releases updater."""
133  self.wledwled = WLEDReleases(session=async_get_clientsession(hass))
134  super().__init__(
135  hass,
136  LOGGER,
137  config_entry=None,
138  name=DOMAIN,
139  update_interval=RELEASES_SCAN_INTERVAL,
140  )
141 
142  async def _async_update_data(self) -> Releases:
143  """Fetch release data from WLED."""
144  try:
145  return await self.wledwled.releases()
146  except WLEDError as error:
147  raise UpdateFailed(f"Invalid response from GitHub API: {error}") from error
None __init__(self, HomeAssistant hass, *ConfigEntry entry)
Definition: coordinator.py:41
IssData update(pyiss.ISS iss)
Definition: __init__.py:33
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)