Home Assistant Unofficial Reference 2024.12.1
bluetooth.py
Go to the documentation of this file.
1 """Bluetooth support for Ruuvi Gateway."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import time
7 
9  FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
10  MONOTONIC_TIME,
11  BaseHaRemoteScanner,
12  async_register_scanner,
13 )
14 from homeassistant.config_entries import ConfigEntry
15 from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
16 
17 from .coordinator import RuuviGatewayUpdateCoordinator
18 
19 _LOGGER = logging.getLogger(__name__)
20 
21 
22 class RuuviGatewayScanner(BaseHaRemoteScanner):
23  """Scanner for Ruuvi Gateway."""
24 
25  def __init__(
26  self,
27  scanner_id: str,
28  name: str,
29  *,
30  coordinator: RuuviGatewayUpdateCoordinator,
31  ) -> None:
32  """Initialize the scanner, using the given update coordinator as data source."""
33  super().__init__(
34  scanner_id,
35  name,
36  connector=None,
37  connectable=False,
38  )
39  self.coordinatorcoordinator = coordinator
40 
41  @callback
42  def _async_handle_new_data(self) -> None:
43  now = time.time()
44  monotonic_now = MONOTONIC_TIME()
45  for tag_data in self.coordinatorcoordinator.data:
46  data_age_seconds = now - tag_data.timestamp # Both are Unix time
47  if data_age_seconds > FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS:
48  # Don't process stale data at all
49  continue
50  anno = tag_data.parse_announcement()
51  self._async_on_advertisement(
52  address=tag_data.mac,
53  rssi=tag_data.rssi,
54  local_name=anno.local_name,
55  service_data=anno.service_data,
56  service_uuids=anno.service_uuids,
57  manufacturer_data=anno.manufacturer_data,
58  tx_power=anno.tx_power,
59  details={},
60  advertisement_monotonic_time=monotonic_now - data_age_seconds,
61  )
62 
63  @callback
64  def start_polling(self) -> CALLBACK_TYPE:
65  """Start polling; return a callback to stop polling."""
66  return self.coordinatorcoordinator.async_add_listener(self._async_handle_new_data_async_handle_new_data)
67 
68 
70  hass: HomeAssistant,
71  entry: ConfigEntry,
72  coordinator: RuuviGatewayUpdateCoordinator,
73 ) -> tuple[RuuviGatewayScanner, CALLBACK_TYPE]:
74  """Connect scanner and start polling."""
75  assert entry.unique_id is not None
76  source = str(entry.unique_id)
77  _LOGGER.debug(
78  "%s [%s]: Connecting scanner",
79  entry.title,
80  source,
81  )
82  scanner = RuuviGatewayScanner(
83  scanner_id=source,
84  name=entry.title,
85  coordinator=coordinator,
86  )
87  unload_callbacks = [
88  async_register_scanner(hass, scanner),
89  scanner.async_setup(),
90  scanner.start_polling(),
91  ]
92 
93  @callback
94  def _async_unload() -> None:
95  for unloader in unload_callbacks:
96  unloader()
97 
98  return (scanner, _async_unload)
None __init__(self, str scanner_id, str name, *RuuviGatewayUpdateCoordinator coordinator)
Definition: bluetooth.py:31
CALLBACK_TYPE async_register_scanner(HomeAssistant hass, BaseHaScanner scanner, int|None connection_slots=None)
Definition: api.py:181
None _async_unload(list[CALLBACK_TYPE] unload_callbacks)
Definition: bluetooth.py:19
None async_add_listener(HomeAssistant hass, Callable[[], None] listener)
Definition: __init__.py:82
tuple[RuuviGatewayScanner, CALLBACK_TYPE] async_connect_scanner(HomeAssistant hass, ConfigEntry entry, RuuviGatewayUpdateCoordinator coordinator)
Definition: bluetooth.py:73