Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """DataUpdateCoordinator for Smlight."""
2 
3 from __future__ import annotations
4 
5 from abc import abstractmethod
6 from dataclasses import dataclass
7 from typing import TYPE_CHECKING
8 
9 from pysmlight import Api2, Info, Sensors
10 from pysmlight.const import Settings, SettingsProp
11 from pysmlight.exceptions import SmlightAuthError, SmlightConnectionError
12 from pysmlight.web import Firmware
13 
14 from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
15 from homeassistant.core import HomeAssistant
16 from homeassistant.exceptions import ConfigEntryAuthFailed
17 from homeassistant.helpers import issue_registry as ir
18 from homeassistant.helpers.device_registry import format_mac
19 from homeassistant.helpers.issue_registry import IssueSeverity
20 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
21 
22 from .const import DOMAIN, LOGGER, SCAN_FIRMWARE_INTERVAL, SCAN_INTERVAL
23 
24 if TYPE_CHECKING:
25  from . import SmConfigEntry
26 
27 
28 @dataclass
29 class SmData:
30  """SMLIGHT data stored in the DataUpdateCoordinator."""
31 
32  sensors: Sensors
33  info: Info
34 
35 
36 @dataclass
37 class SmFwData:
38  """SMLIGHT firmware data stored in the FirmwareUpdateCoordinator."""
39 
40  info: Info
41  esp_firmware: list[Firmware] | None
42  zb_firmware: list[Firmware] | None
43 
44 
45 class SmBaseDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
46  """Base Coordinator for SMLIGHT."""
47 
48  config_entry: SmConfigEntry
49 
50  def __init__(self, hass: HomeAssistant, host: str, client: Api2) -> None:
51  """Initialize the coordinator."""
52  super().__init__(
53  hass,
54  LOGGER,
55  name=f"{DOMAIN}_{host}",
56  update_interval=SCAN_INTERVAL,
57  )
58 
59  self.clientclient = client
60  self.unique_idunique_id: str | None = None
61  self.legacy_apilegacy_api: int = 0
62 
63  async def _async_setup(self) -> None:
64  """Authenticate if needed during initial setup."""
65  if await self.clientclient.check_auth_needed():
66  if (
67  CONF_USERNAME in self.config_entryconfig_entry.data
68  and CONF_PASSWORD in self.config_entryconfig_entry.data
69  ):
70  try:
71  await self.clientclient.authenticate(
72  self.config_entryconfig_entry.data[CONF_USERNAME],
73  self.config_entryconfig_entry.data[CONF_PASSWORD],
74  )
75  except SmlightAuthError as err:
76  raise ConfigEntryAuthFailed from err
77  else:
78  # Auth required but no credentials available
79  raise ConfigEntryAuthFailed
80 
81  info = await self.clientclient.get_info()
82  self.unique_idunique_id = format_mac(info.MAC)
83  self.legacy_apilegacy_api = info.legacy_api
84  if info.legacy_api == 2:
85  ir.async_create_issue(
86  self.hasshass,
87  DOMAIN,
88  "unsupported_firmware",
89  is_fixable=False,
90  is_persistent=False,
91  learn_more_url="https://smlight.tech/flasher/#SLZB-06",
92  severity=IssueSeverity.ERROR,
93  translation_key="unsupported_firmware",
94  )
95 
96  async def _async_update_data(self) -> _DataT:
97  try:
98  return await self._internal_update_data_internal_update_data()
99  except SmlightAuthError as err:
100  raise ConfigEntryAuthFailed from err
101 
102  except SmlightConnectionError as err:
103  raise UpdateFailed(err) from err
104 
105  @abstractmethod
106  async def _internal_update_data(self) -> _DataT:
107  """Update coordinator data."""
108 
109 
110 class SmDataUpdateCoordinator(SmBaseDataUpdateCoordinator[SmData]):
111  """Class to manage fetching SMLIGHT sensor data."""
112 
113  def update_setting(self, setting: Settings, value: bool | int) -> None:
114  """Update the sensor value from event."""
115 
116  prop = SettingsProp[setting.name].value
117  setattr(self.datadata.sensors, prop, value)
118 
119  self.async_set_updated_dataasync_set_updated_data(self.datadata)
120 
121  async def _internal_update_data(self) -> SmData:
122  """Fetch sensor data from the SMLIGHT device."""
123  sensors = Sensors()
124  if not self.legacy_apilegacy_api:
125  sensors = await self.clientclient.get_sensors()
126 
127  return SmData(
128  sensors=sensors,
129  info=await self.clientclient.get_info(),
130  )
131 
132 
134  """Class to manage fetching SMLIGHT firmware update data from cloud."""
135 
136  def __init__(self, hass: HomeAssistant, host: str, client: Api2) -> None:
137  """Initialize the coordinator."""
138  super().__init__(hass, host, client)
139 
140  self.update_intervalupdate_intervalupdate_intervalupdate_intervalupdate_interval = SCAN_FIRMWARE_INTERVAL
141  # only one update can run at a time (core or zibgee)
142  self.in_progressin_progress = False
143 
144  async def _internal_update_data(self) -> SmFwData:
145  """Fetch data from the SMLIGHT device."""
146  info = await self.clientclient.get_info()
147 
148  return SmFwData(
149  info=info,
150  esp_firmware=await self.clientclient.get_firmware_version(info.fw_channel),
151  zb_firmware=await self.clientclient.get_firmware_version(
152  info.fw_channel, device=info.model, mode="zigbee"
153  ),
154  )
None __init__(self, HomeAssistant hass, str host, Api2 client)
Definition: coordinator.py:50
None update_setting(self, Settings setting, bool|int value)
Definition: coordinator.py:113
None __init__(self, HomeAssistant hass, str host, Api2 client)
Definition: coordinator.py:136
dict[str, Any]|None get_info(HomeAssistant hass)
Definition: coordinator.py:69
def authenticate(HomeAssistant hass, host, port, servers)
Definition: config_flow.py:104