Home Assistant Unofficial Reference 2024.12.1
hub.py
Go to the documentation of this file.
1 """UniFi Network abstraction."""
2 
3 from __future__ import annotations
4 
5 from datetime import datetime
6 from typing import TYPE_CHECKING
7 
8 import aiounifi
9 
10 from homeassistant.core import Event, HomeAssistant, callback
11 from homeassistant.helpers import device_registry as dr
13  DeviceEntry,
14  DeviceEntryType,
15  DeviceInfo,
16 )
17 from homeassistant.helpers.dispatcher import async_dispatcher_send
18 
19 from ..const import ATTR_MANUFACTURER, CONF_SITE_ID, DOMAIN as UNIFI_DOMAIN, PLATFORMS
20 from .config import UnifiConfig
21 from .entity_helper import UnifiEntityHelper
22 from .entity_loader import UnifiEntityLoader
23 from .websocket import UnifiWebsocket
24 
25 if TYPE_CHECKING:
26  from .. import UnifiConfigEntry
27 
28 
29 class UnifiHub:
30  """Manages a single UniFi Network instance."""
31 
32  def __init__(
33  self,
34  hass: HomeAssistant,
35  config_entry: UnifiConfigEntry,
36  api: aiounifi.Controller,
37  ) -> None:
38  """Initialize the system."""
39  self.hasshass = hass
40  self.apiapi = api
41  self.configconfig = UnifiConfig.from_config_entry(config_entry)
42  self.entity_loaderentity_loader = UnifiEntityLoader(self)
43  self._entity_helper_entity_helper = UnifiEntityHelper(hass, api)
44  self.websocketwebsocket = UnifiWebsocket(hass, api, self.signal_reachablesignal_reachable)
45 
46  self.sitesite = config_entry.data[CONF_SITE_ID]
47  self.is_adminis_admin = False
48 
49  @property
50  def available(self) -> bool:
51  """Websocket connection state."""
52  return self.websocketwebsocket.available
53 
54  @property
55  def signal_heartbeat_missed(self) -> str:
56  """Event to signal new heartbeat missed."""
57  return self._entity_helper_entity_helper.signal_heartbeat
58 
59  @callback
60  def update_heartbeat(self, unique_id: str, heartbeat_expire_time: datetime) -> None:
61  """Update device time in heartbeat monitor."""
62  self._entity_helper_entity_helper.update_heartbeat(unique_id, heartbeat_expire_time)
63 
64  @callback
65  def remove_heartbeat(self, unique_id: str) -> None:
66  """Update device time in heartbeat monitor."""
67  self._entity_helper_entity_helper.remove_heartbeat(unique_id)
68 
69  @callback
71  self, device_id: str, port_idx: int, poe_mode: str
72  ) -> None:
73  """Queue commands to execute them together per device."""
74  self._entity_helper_entity_helper.queue_poe_port_command(device_id, port_idx, poe_mode)
75 
76  @property
77  def signal_reachable(self) -> str:
78  """Integration specific event to signal a change in connection status."""
79  return f"unifi-reachable-{self.config.entry.entry_id}"
80 
81  @property
82  def signal_options_update(self) -> str:
83  """Event specific per UniFi entry to signal new options."""
84  return f"unifi-options-{self.config.entry.entry_id}"
85 
86  async def initialize(self) -> None:
87  """Set up a UniFi Network instance."""
88  await self.entity_loaderentity_loader.initialize()
89  self._entity_helper_entity_helper.initialize()
90 
91  assert self.configconfig.entry.unique_id is not None
92  self.is_adminis_admin = self.apiapi.sites[self.configconfig.entry.unique_id].role == "admin"
93 
94  self.configconfig.entry.add_update_listener(self.async_config_entry_updatedasync_config_entry_updated)
95 
96  @property
97  def device_info(self) -> DeviceInfo:
98  """UniFi Network device info."""
99  assert self.configconfig.entry.unique_id is not None
100 
101  version: str | None = None
102  if sysinfo := next(iter(self.apiapi.system_information.values()), None):
103  version = sysinfo.version
104 
105  return DeviceInfo(
106  entry_type=DeviceEntryType.SERVICE,
107  identifiers={(UNIFI_DOMAIN, self.configconfig.entry.unique_id)},
108  manufacturer=ATTR_MANUFACTURER,
109  model="UniFi Network Application",
110  name="UniFi Network",
111  sw_version=version,
112  )
113 
114  @callback
115  def async_update_device_registry(self) -> DeviceEntry:
116  """Update device registry."""
117  device_registry = dr.async_get(self.hasshass)
118  return device_registry.async_get_or_create(
119  config_entry_id=self.configconfig.entry.entry_id, **self.device_infodevice_info
120  )
121 
122  @staticmethod
124  hass: HomeAssistant, config_entry: UnifiConfigEntry
125  ) -> None:
126  """Handle signals of config entry being updated.
127 
128  If config entry is updated due to reauth flow
129  the entry might already have been reset and thus is not available.
130  """
131  hub = config_entry.runtime_data
132  hub.config = UnifiConfig.from_config_entry(config_entry)
133  async_dispatcher_send(hass, hub.signal_options_update)
134 
135  @callback
136  def shutdown(self, event: Event) -> None:
137  """Wrap the call to unifi.close.
138 
139  Used as an argument to EventBus.async_listen_once.
140  """
141  self.websocketwebsocket.stop()
142 
143  async def async_reset(self) -> bool:
144  """Reset this hub to default state.
145 
146  Will cancel any scheduled setup retry and will unload
147  the config entry.
148  """
149  await self.websocketwebsocket.stop_and_wait()
150 
151  unload_ok = await self.hasshass.config_entries.async_unload_platforms(
152  self.configconfig.entry, PLATFORMS
153  )
154 
155  if not unload_ok:
156  return False
157 
158  self._entity_helper_entity_helper.reset()
159 
160  return True
None update_heartbeat(self, str unique_id, datetime heartbeat_expire_time)
Definition: hub.py:60
DeviceEntry async_update_device_registry(self)
Definition: hub.py:115
None async_config_entry_updated(HomeAssistant hass, UnifiConfigEntry config_entry)
Definition: hub.py:125
None shutdown(self, Event event)
Definition: hub.py:136
None remove_heartbeat(self, str unique_id)
Definition: hub.py:65
None __init__(self, HomeAssistant hass, UnifiConfigEntry config_entry, aiounifi.Controller api)
Definition: hub.py:37
None queue_poe_port_command(self, str device_id, int port_idx, str poe_mode)
Definition: hub.py:72
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:193