Home Assistant Unofficial Reference 2024.12.1
util.py
Go to the documentation of this file.
1 """Utility functions for Home Assistant SkyConnect integration."""
2 
3 from __future__ import annotations
4 
5 from collections import defaultdict
6 from dataclasses import dataclass
7 import logging
8 from typing import cast
9 
10 from universal_silabs_flasher.const import ApplicationType
11 
12 from homeassistant.components.hassio import AddonError, AddonState
13 from homeassistant.config_entries import ConfigEntry, ConfigEntryState
14 from homeassistant.core import HomeAssistant, callback
15 from homeassistant.helpers.hassio import is_hassio
16 from homeassistant.helpers.singleton import singleton
17 
18 from .const import (
19  OTBR_ADDON_MANAGER_DATA,
20  OTBR_ADDON_NAME,
21  OTBR_ADDON_SLUG,
22  ZHA_DOMAIN,
23  ZIGBEE_FLASHER_ADDON_MANAGER_DATA,
24  ZIGBEE_FLASHER_ADDON_NAME,
25  ZIGBEE_FLASHER_ADDON_SLUG,
26 )
27 from .silabs_multiprotocol_addon import (
28  WaitingAddonManager,
29  get_multiprotocol_addon_manager,
30 )
31 
32 _LOGGER = logging.getLogger(__name__)
33 
34 
35 def get_zha_device_path(config_entry: ConfigEntry) -> str | None:
36  """Get the device path from a ZHA config entry."""
37  return cast(str | None, config_entry.data.get("device", {}).get("path", None))
38 
39 
40 @singleton(OTBR_ADDON_MANAGER_DATA)
41 @callback
42 def get_otbr_addon_manager(hass: HomeAssistant) -> WaitingAddonManager:
43  """Get the OTBR add-on manager."""
44  return WaitingAddonManager(
45  hass,
46  _LOGGER,
47  OTBR_ADDON_NAME,
48  OTBR_ADDON_SLUG,
49  )
50 
51 
52 @singleton(ZIGBEE_FLASHER_ADDON_MANAGER_DATA)
53 @callback
54 def get_zigbee_flasher_addon_manager(hass: HomeAssistant) -> WaitingAddonManager:
55  """Get the flasher add-on manager."""
56  return WaitingAddonManager(
57  hass,
58  _LOGGER,
59  ZIGBEE_FLASHER_ADDON_NAME,
60  ZIGBEE_FLASHER_ADDON_SLUG,
61  )
62 
63 
64 @dataclass(slots=True, kw_only=True)
66  """Firmware guess."""
67 
68  is_running: bool
69  firmware_type: ApplicationType
70  source: str
71 
72 
73 async def guess_firmware_type(hass: HomeAssistant, device_path: str) -> FirmwareGuess:
74  """Guess the firmware type based on installed addons and other integrations."""
75  device_guesses: defaultdict[str | None, list[FirmwareGuess]] = defaultdict(list)
76 
77  for zha_config_entry in hass.config_entries.async_entries(ZHA_DOMAIN):
78  zha_path = get_zha_device_path(zha_config_entry)
79 
80  if zha_path is not None:
81  device_guesses[zha_path].append(
83  is_running=(zha_config_entry.state == ConfigEntryState.LOADED),
84  firmware_type=ApplicationType.EZSP,
85  source="zha",
86  )
87  )
88 
89  if is_hassio(hass):
90  otbr_addon_manager = get_otbr_addon_manager(hass)
91 
92  try:
93  otbr_addon_info = await otbr_addon_manager.async_get_addon_info()
94  except AddonError:
95  pass
96  else:
97  if otbr_addon_info.state != AddonState.NOT_INSTALLED:
98  otbr_path = otbr_addon_info.options.get("device")
99  device_guesses[otbr_path].append(
101  is_running=(otbr_addon_info.state == AddonState.RUNNING),
102  firmware_type=ApplicationType.SPINEL,
103  source="otbr",
104  )
105  )
106 
107  multipan_addon_manager = await get_multiprotocol_addon_manager(hass)
108 
109  try:
110  multipan_addon_info = await multipan_addon_manager.async_get_addon_info()
111  except AddonError:
112  pass
113  else:
114  if multipan_addon_info.state != AddonState.NOT_INSTALLED:
115  multipan_path = multipan_addon_info.options.get("device")
116  device_guesses[multipan_path].append(
118  is_running=(multipan_addon_info.state == AddonState.RUNNING),
119  firmware_type=ApplicationType.CPC,
120  source="multiprotocol",
121  )
122  )
123 
124  # Fall back to EZSP if we can't guess the firmware type
125  if device_path not in device_guesses:
126  return FirmwareGuess(
127  is_running=False, firmware_type=ApplicationType.EZSP, source="unknown"
128  )
129 
130  # Prioritizes guesses that were pulled from a running addon or integration but keep
131  # the sort order we defined above
132  guesses = sorted(
133  device_guesses[device_path],
134  key=lambda guess: guess.is_running,
135  )
136 
137  assert guesses
138 
139  return guesses[-1]
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
bool is_hassio(HomeAssistant hass)
Definition: __init__.py:302
WaitingAddonManager get_zigbee_flasher_addon_manager(HomeAssistant hass)
Definition: util.py:54
FirmwareGuess guess_firmware_type(HomeAssistant hass, str device_path)
Definition: util.py:73
WaitingAddonManager get_otbr_addon_manager(HomeAssistant hass)
Definition: util.py:42
str|None get_zha_device_path(ConfigEntry config_entry)
Definition: util.py:35