Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Component for wiffi support."""
2 
3 from datetime import timedelta
4 import errno
5 import logging
6 
7 from wiffi import WiffiTcpServer
8 
9 from homeassistant.config_entries import ConfigEntry
10 from homeassistant.const import CONF_PORT, Platform
11 from homeassistant.core import HomeAssistant, callback
12 from homeassistant.exceptions import ConfigEntryNotReady
13 from homeassistant.helpers.dispatcher import async_dispatcher_send
14 from homeassistant.helpers.event import async_track_time_interval
15 
16 from .const import (
17  CHECK_ENTITIES_SIGNAL,
18  CREATE_ENTITY_SIGNAL,
19  DOMAIN,
20  UPDATE_ENTITY_SIGNAL,
21 )
22 from .entity import generate_unique_id
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 
27 PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
28 
29 
30 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
31  """Set up wiffi from a config entry, config_entry contains data from config entry database."""
32  if not entry.update_listeners:
33  entry.add_update_listener(async_update_options)
34 
35  # create api object
36  api = WiffiIntegrationApi(hass)
37  api.async_setup(entry)
38 
39  # store api object
40  hass.data.setdefault(DOMAIN, {})[entry.entry_id] = api
41 
42  try:
43  await api.server.start_server()
44  except OSError as exc:
45  if exc.errno != errno.EADDRINUSE:
46  _LOGGER.error("Start_server failed, errno: %d", exc.errno)
47  return False
48  _LOGGER.error("Port %s already in use", entry.data[CONF_PORT])
49  raise ConfigEntryNotReady from exc
50 
51  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
52 
53  return True
54 
55 
56 async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
57  """Update options."""
58  await hass.config_entries.async_reload(entry.entry_id)
59 
60 
61 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
62  """Unload a config entry."""
63  api: WiffiIntegrationApi = hass.data[DOMAIN][entry.entry_id]
64  await api.server.close_server()
65 
66  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
67  if unload_ok:
68  api = hass.data[DOMAIN].pop(entry.entry_id)
69  api.shutdown()
70 
71  return unload_ok
72 
73 
75  """API object for wiffi handling. Stored in hass.data."""
76 
77  def __init__(self, hass):
78  """Initialize the instance."""
79  self._hass_hass = hass
80  self._server_server = None
81  self._known_devices_known_devices = {}
82  self._periodic_callback_periodic_callback = None
83 
84  def async_setup(self, config_entry):
85  """Set up api instance."""
86  self._server_server = WiffiTcpServer(config_entry.data[CONF_PORT], self)
87  self._periodic_callback_periodic_callback = async_track_time_interval(
88  self._hass_hass, self._periodic_tick_periodic_tick, timedelta(seconds=10)
89  )
90 
91  def shutdown(self):
92  """Shutdown wiffi api.
93 
94  Remove listener for periodic callbacks.
95  """
96  if (remove_listener := self._periodic_callback_periodic_callback) is not None:
97  remove_listener()
98 
99  async def __call__(self, device, metrics):
100  """Process callback from TCP server if new data arrives from a device."""
101  if device.mac_address not in self._known_devices_known_devices:
102  # add empty set for new device
103  self._known_devices_known_devices[device.mac_address] = set()
104 
105  for metric in metrics:
106  if metric.id not in self._known_devices_known_devices[device.mac_address]:
107  self._known_devices_known_devices[device.mac_address].add(metric.id)
108  async_dispatcher_send(self._hass_hass, CREATE_ENTITY_SIGNAL, device, metric)
109  else:
111  self._hass_hass,
112  f"{UPDATE_ENTITY_SIGNAL}-{generate_unique_id(device, metric)}",
113  device,
114  metric,
115  )
116 
117  @property
118  def server(self):
119  """Return TCP server instance for start + close."""
120  return self._server_server
121 
122  @callback
123  def _periodic_tick(self, now=None):
124  """Check if any entity has timed out because it has not been updated."""
125  async_dispatcher_send(self._hass_hass, CHECK_ENTITIES_SIGNAL)
bool add(self, _T matcher)
Definition: match.py:185
None async_update_options(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:56
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:30
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:61
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:193
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
Definition: event.py:1679