Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Integration to UniFi Network and its various features."""
2 
3 from aiounifi.models.client import Client
4 
5 from homeassistant.config_entries import ConfigEntry
6 from homeassistant.const import EVENT_HOMEASSISTANT_STOP
7 from homeassistant.core import HomeAssistant, callback
8 from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
9 from homeassistant.helpers import config_validation as cv
10 from homeassistant.helpers.device_registry import DeviceEntry
11 from homeassistant.helpers.storage import Store
12 from homeassistant.helpers.typing import ConfigType
13 
14 from .const import DOMAIN as UNIFI_DOMAIN, PLATFORMS, UNIFI_WIRELESS_CLIENTS
15 from .errors import AuthenticationRequired, CannotConnect
16 from .hub import UnifiHub, get_unifi_api
17 from .services import async_setup_services
18 
19 type UnifiConfigEntry = ConfigEntry[UnifiHub]
20 
21 SAVE_DELAY = 10
22 STORAGE_KEY = "unifi_data"
23 STORAGE_VERSION = 1
24 
25 CONFIG_SCHEMA = cv.config_entry_only_config_schema(UNIFI_DOMAIN)
26 
27 
28 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
29  """Integration doesn't support configuration through configuration.yaml."""
31 
32  hass.data[UNIFI_WIRELESS_CLIENTS] = wireless_clients = UnifiWirelessClients(hass)
33  await wireless_clients.async_load()
34 
35  return True
36 
37 
39  hass: HomeAssistant, config_entry: UnifiConfigEntry
40 ) -> bool:
41  """Set up the UniFi Network integration."""
42  try:
43  api = await get_unifi_api(hass, config_entry.data)
44 
45  except CannotConnect as err:
46  raise ConfigEntryNotReady from err
47 
48  except AuthenticationRequired as err:
49  raise ConfigEntryAuthFailed from err
50 
51  hub = config_entry.runtime_data = UnifiHub(hass, config_entry, api)
52  await hub.initialize()
53 
54  await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
55  hub.async_update_device_registry()
56  hub.entity_loader.load_entities()
57 
58  hub.websocket.start()
59 
60  config_entry.async_on_unload(
61  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, hub.shutdown)
62  )
63  return True
64 
65 
67  hass: HomeAssistant, config_entry: UnifiConfigEntry
68 ) -> bool:
69  """Unload a config entry."""
70  return await config_entry.runtime_data.async_reset()
71 
72 
74  hass: HomeAssistant, config_entry: UnifiConfigEntry, device_entry: DeviceEntry
75 ) -> bool:
76  """Remove config entry from a device."""
77  hub = config_entry.runtime_data
78  return not any(
79  identifier
80  for _, identifier in device_entry.connections
81  if identifier in hub.api.clients or identifier in hub.api.devices
82  )
83 
84 
86  """Class to store clients known to be wireless.
87 
88  This is needed since wireless devices going offline
89  might get marked as wired by UniFi.
90  """
91 
92  def __init__(self, hass: HomeAssistant) -> None:
93  """Set up client storage."""
94  self.hasshass = hass
95  self.datadata: dict[str, dict[str, list[str]] | list[str]] = {}
96  self.wireless_clients: set[str] = set()
97  self._store: Store = Store(hass, STORAGE_VERSION, STORAGE_KEY)
98 
99  async def async_load(self) -> None:
100  """Load data from file."""
101  if (data := await self._store.async_load()) is not None:
102  self.datadata = data
103  if "wireless_clients" not in data:
104  data["wireless_clients"] = [
105  obj_id
106  for config_entry in data
107  for obj_id in data[config_entry]["wireless_devices"]
108  ]
109  self.wireless_clients.update(data["wireless_clients"])
110 
111  @callback
112  def is_wireless(self, client: Client) -> bool:
113  """Is client known to be wireless.
114 
115  Store if client is wireless and not known.
116  """
117  if not client.is_wired and client.mac not in self.wireless_clients:
118  self.wireless_clients.add(client.mac)
119  self._store.async_delay_save(self._data_to_save_data_to_save, SAVE_DELAY)
120 
121  return client.mac in self.wireless_clients
122 
123  @callback
124  def update_clients(self, clients: set[Client]) -> None:
125  """Update data and schedule to save to file."""
126  self.wireless_clients.update(
127  {client.mac for client in clients if not client.is_wired}
128  )
129  self._store.async_delay_save(self._data_to_save_data_to_save, SAVE_DELAY)
130 
131  @callback
132  def _data_to_save(self) -> dict[str, dict[str, list[str]] | list[str]]:
133  """Return data of UniFi wireless clients to store in a file."""
134  self.datadata["wireless_clients"] = list(self.wireless_clients)
135  return self.datadata
136 
137  def __contains__(self, obj_id: int | str) -> bool:
138  """Validate membership of item ID."""
139  return obj_id in self.wireless_clients
None update_clients(self, set[Client] clients)
Definition: __init__.py:124
dict[str, dict[str, list[str]]|list[str]] _data_to_save(self)
Definition: __init__.py:132
None __init__(self, HomeAssistant hass)
Definition: __init__.py:92
bool add(self, _T matcher)
Definition: match.py:185
None async_setup_services(HomeAssistant hass)
Definition: __init__.py:72
IssData update(pyiss.ISS iss)
Definition: __init__.py:33
aiounifi.Controller get_unifi_api(HomeAssistant hass, MappingProxyType[str, Any] config)
Definition: api.py:31
bool async_setup_entry(HomeAssistant hass, UnifiConfigEntry config_entry)
Definition: __init__.py:40
bool async_remove_config_entry_device(HomeAssistant hass, UnifiConfigEntry config_entry, DeviceEntry device_entry)
Definition: __init__.py:75
bool async_unload_entry(HomeAssistant hass, UnifiConfigEntry config_entry)
Definition: __init__.py:68
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:28
None async_delay_save(self, Callable[[], _T] data_func, float delay=0)
Definition: storage.py:444