Home Assistant Unofficial Reference 2024.12.1
device_tracker.py
Go to the documentation of this file.
1 """Support for Keenetic routers as device tracker."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 from ndms2_client import Device
8 
10  DOMAIN as DEVICE_TRACKER_DOMAIN,
11  ScannerEntity,
12 )
13 from homeassistant.config_entries import ConfigEntry
14 from homeassistant.core import HomeAssistant, callback
15 from homeassistant.helpers import entity_registry as er
16 from homeassistant.helpers.dispatcher import async_dispatcher_connect
17 from homeassistant.helpers.entity_platform import AddEntitiesCallback
18 import homeassistant.util.dt as dt_util
19 
20 from .const import DOMAIN, ROUTER
21 from .router import KeeneticRouter
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 
27  hass: HomeAssistant,
28  config_entry: ConfigEntry,
29  async_add_entities: AddEntitiesCallback,
30 ) -> None:
31  """Set up device tracker for Keenetic NDMS2 component."""
32  router: KeeneticRouter = hass.data[DOMAIN][config_entry.entry_id][ROUTER]
33 
34  tracked: set[str] = set()
35 
36  @callback
37  def update_from_router():
38  """Update the status of devices."""
39  update_items(router, async_add_entities, tracked)
40 
41  update_from_router()
42 
43  registry = er.async_get(hass)
44  # Restore devices that are not a part of active clients list.
45  restored = []
46  for entity_entry in registry.entities.get_entries_for_config_entry_id(
47  config_entry.entry_id
48  ):
49  if entity_entry.domain == DEVICE_TRACKER_DOMAIN:
50  mac = entity_entry.unique_id.partition("_")[0]
51  if mac not in tracked:
52  tracked.add(mac)
53  restored.append(
55  Device(
56  mac=mac,
57  # restore the original name as set by the router before
58  name=entity_entry.original_name,
59  ip=None,
60  interface=None,
61  ),
62  router,
63  )
64  )
65 
66  async_add_entities(restored)
67 
68  async_dispatcher_connect(hass, router.signal_update, update_from_router)
69 
70 
71 @callback
72 def update_items(router: KeeneticRouter, async_add_entities, tracked: set[str]):
73  """Update tracked device state from the hub."""
74  new_tracked: list[KeeneticTracker] = []
75  for mac, device in router.last_devices.items():
76  if mac not in tracked:
77  tracked.add(mac)
78  new_tracked.append(KeeneticTracker(device, router))
79 
80  async_add_entities(new_tracked)
81 
82 
83 class KeeneticTracker(ScannerEntity):
84  """Representation of network device."""
85 
86  _attr_should_poll = False
87 
88  def __init__(self, device: Device, router: KeeneticRouter) -> None:
89  """Initialize the tracked device."""
90  self._device_device = device
91  self._router_router = router
92  self._last_seen_last_seen = (
93  dt_util.utcnow() if device.mac in router.last_devices else None
94  )
95 
96  @property
97  def is_connected(self) -> bool:
98  """Return true if the device is connected to the network."""
99  return (
100  self._last_seen_last_seen is not None
101  and (dt_util.utcnow() - self._last_seen_last_seen)
102  < self._router_router.consider_home_interval
103  )
104 
105  @property
106  def name(self) -> str:
107  """Return the name of the device."""
108  return self._device_device.name or self._device_device.mac
109 
110  @property
111  def unique_id(self) -> str:
112  """Return a unique identifier for this device."""
113  return f"{self._device.mac}_{self._router.config_entry.entry_id}"
114 
115  @property
116  def ip_address(self) -> str | None:
117  """Return the primary ip address of the device."""
118  return self._device_device.ip if self.is_connectedis_connected else None
119 
120  @property
121  def mac_address(self) -> str:
122  """Return the mac address of the device."""
123  return self._device_device.mac
124 
125  @property
126  def available(self) -> bool:
127  """Return if controller is available."""
128  return self._router_router.available
129 
130  @property
132  """Return the device state attributes."""
133  if self.is_connectedis_connected:
134  return {
135  "interface": self._device_device.interface,
136  }
137  return None
138 
139  async def async_added_to_hass(self) -> None:
140  """Client entity created."""
141  _LOGGER.debug("New network device tracker %s (%s)", self.namename, self.unique_idunique_id)
142 
143  @callback
144  def update_device() -> None:
145  _LOGGER.debug(
146  "Updating Keenetic tracked device %s (%s)",
147  self.entity_id,
148  self.unique_idunique_id,
149  )
150  new_device = self._router_router.last_devices.get(self._device_device.mac)
151  if new_device:
152  self._device_device = new_device
153  self._last_seen_last_seen = dt_util.utcnow()
154 
155  self.async_write_ha_state()
156 
157  self.async_on_remove(
159  self.hass, self._router_router.signal_update, update_device
160  )
161  )
None __init__(self, Device device, KeeneticRouter router)
def update_items(KeeneticRouter router, async_add_entities, set[str] tracked)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
str|None update_device(HomeAssistant hass, ConfigEntry config_entry, ConfigType config)
Definition: entity.py:1512
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103