Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Netgear routers."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import date, datetime
8 from decimal import Decimal
9 import logging
10 
12  RestoreSensor,
13  SensorDeviceClass,
14  SensorEntity,
15  SensorEntityDescription,
16  SensorStateClass,
17 )
18 from homeassistant.config_entries import ConfigEntry
19 from homeassistant.const import (
20  PERCENTAGE,
21  EntityCategory,
22  UnitOfDataRate,
23  UnitOfInformation,
24  UnitOfTime,
25 )
26 from homeassistant.core import HomeAssistant, callback
27 from homeassistant.helpers.entity_platform import AddEntitiesCallback
28 from homeassistant.helpers.typing import StateType
29 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
30 
31 from .const import (
32  DOMAIN,
33  KEY_COORDINATOR,
34  KEY_COORDINATOR_LINK,
35  KEY_COORDINATOR_SPEED,
36  KEY_COORDINATOR_TRAFFIC,
37  KEY_COORDINATOR_UTIL,
38  KEY_ROUTER,
39 )
40 from .entity import NetgearDeviceEntity, NetgearRouterCoordinatorEntity
41 from .router import NetgearRouter
42 
43 _LOGGER = logging.getLogger(__name__)
44 
45 SENSOR_TYPES = {
47  key="type",
48  translation_key="link_type",
49  entity_category=EntityCategory.DIAGNOSTIC,
50  ),
51  "link_rate": SensorEntityDescription(
52  key="link_rate",
53  translation_key="link_rate",
54  native_unit_of_measurement="Mbps",
55  entity_category=EntityCategory.DIAGNOSTIC,
56  ),
57  "signal": SensorEntityDescription(
58  key="signal",
59  translation_key="signal_strength",
60  native_unit_of_measurement=PERCENTAGE,
61  entity_category=EntityCategory.DIAGNOSTIC,
62  ),
64  key="ssid",
65  translation_key="ssid",
66  entity_category=EntityCategory.DIAGNOSTIC,
67  ),
68  "conn_ap_mac": SensorEntityDescription(
69  key="conn_ap_mac",
70  translation_key="access_point_mac",
71  entity_category=EntityCategory.DIAGNOSTIC,
72  ),
73 }
74 
75 
76 @dataclass(frozen=True)
78  """Class describing Netgear sensor entities."""
79 
80  value: Callable = lambda data: data
81  index: int = 0
82 
83 
84 SENSOR_TRAFFIC_TYPES = [
86  key="NewTodayUpload",
87  translation_key="upload_today",
88  entity_category=EntityCategory.DIAGNOSTIC,
89  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
90  device_class=SensorDeviceClass.DATA_SIZE,
91  ),
93  key="NewTodayDownload",
94  translation_key="download_today",
95  entity_category=EntityCategory.DIAGNOSTIC,
96  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
97  device_class=SensorDeviceClass.DATA_SIZE,
98  ),
100  key="NewYesterdayUpload",
101  translation_key="upload_yesterday",
102  entity_category=EntityCategory.DIAGNOSTIC,
103  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
104  device_class=SensorDeviceClass.DATA_SIZE,
105  ),
107  key="NewYesterdayDownload",
108  translation_key="download_yesterday",
109  entity_category=EntityCategory.DIAGNOSTIC,
110  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
111  device_class=SensorDeviceClass.DATA_SIZE,
112  ),
114  key="NewWeekUpload",
115  translation_key="upload_week",
116  entity_category=EntityCategory.DIAGNOSTIC,
117  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
118  device_class=SensorDeviceClass.DATA_SIZE,
119  index=0,
120  value=lambda data: data[0],
121  ),
123  key="NewWeekUpload",
124  translation_key="upload_week_average",
125  entity_category=EntityCategory.DIAGNOSTIC,
126  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
127  device_class=SensorDeviceClass.DATA_SIZE,
128  index=1,
129  value=lambda data: data[1],
130  ),
132  key="NewWeekDownload",
133  translation_key="download_week",
134  entity_category=EntityCategory.DIAGNOSTIC,
135  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
136  device_class=SensorDeviceClass.DATA_SIZE,
137  index=0,
138  value=lambda data: data[0],
139  ),
141  key="NewWeekDownload",
142  translation_key="download_week_average",
143  entity_category=EntityCategory.DIAGNOSTIC,
144  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
145  device_class=SensorDeviceClass.DATA_SIZE,
146  index=1,
147  value=lambda data: data[1],
148  ),
150  key="NewMonthUpload",
151  translation_key="upload_month",
152  entity_category=EntityCategory.DIAGNOSTIC,
153  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
154  device_class=SensorDeviceClass.DATA_SIZE,
155  index=0,
156  value=lambda data: data[0],
157  ),
159  key="NewMonthUpload",
160  translation_key="upload_month_average",
161  entity_category=EntityCategory.DIAGNOSTIC,
162  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
163  device_class=SensorDeviceClass.DATA_SIZE,
164  index=1,
165  value=lambda data: data[1],
166  ),
168  key="NewMonthDownload",
169  translation_key="download_month",
170  entity_category=EntityCategory.DIAGNOSTIC,
171  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
172  device_class=SensorDeviceClass.DATA_SIZE,
173  index=0,
174  value=lambda data: data[0],
175  ),
177  key="NewMonthDownload",
178  translation_key="download_month_average",
179  entity_category=EntityCategory.DIAGNOSTIC,
180  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
181  device_class=SensorDeviceClass.DATA_SIZE,
182  index=1,
183  value=lambda data: data[1],
184  ),
186  key="NewLastMonthUpload",
187  translation_key="upload_last_month",
188  entity_category=EntityCategory.DIAGNOSTIC,
189  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
190  device_class=SensorDeviceClass.DATA_SIZE,
191  index=0,
192  value=lambda data: data[0],
193  ),
195  key="NewLastMonthUpload",
196  translation_key="upload_last_month_average",
197  entity_category=EntityCategory.DIAGNOSTIC,
198  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
199  device_class=SensorDeviceClass.DATA_SIZE,
200  index=1,
201  value=lambda data: data[1],
202  ),
204  key="NewLastMonthDownload",
205  translation_key="download_last_month",
206  entity_category=EntityCategory.DIAGNOSTIC,
207  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
208  device_class=SensorDeviceClass.DATA_SIZE,
209  index=0,
210  value=lambda data: data[0],
211  ),
213  key="NewLastMonthDownload",
214  translation_key="download_last_month_average",
215  entity_category=EntityCategory.DIAGNOSTIC,
216  native_unit_of_measurement=UnitOfInformation.MEGABYTES,
217  device_class=SensorDeviceClass.DATA_SIZE,
218  index=1,
219  value=lambda data: data[1],
220  ),
221 ]
222 
223 SENSOR_SPEED_TYPES = [
225  key="NewOOKLAUplinkBandwidth",
226  translation_key="uplink_bandwidth",
227  entity_category=EntityCategory.DIAGNOSTIC,
228  native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
229  device_class=SensorDeviceClass.DATA_RATE,
230  ),
232  key="NewOOKLADownlinkBandwidth",
233  translation_key="downlink_bandwidth",
234  entity_category=EntityCategory.DIAGNOSTIC,
235  native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
236  device_class=SensorDeviceClass.DATA_RATE,
237  ),
239  key="AveragePing",
240  translation_key="average_ping",
241  entity_category=EntityCategory.DIAGNOSTIC,
242  native_unit_of_measurement=UnitOfTime.MILLISECONDS,
243  ),
244 ]
245 
246 SENSOR_UTILIZATION = [
248  key="NewCPUUtilization",
249  translation_key="cpu_utilization",
250  entity_category=EntityCategory.DIAGNOSTIC,
251  native_unit_of_measurement=PERCENTAGE,
252  state_class=SensorStateClass.MEASUREMENT,
253  ),
255  key="NewMemoryUtilization",
256  translation_key="memory_utilization",
257  entity_category=EntityCategory.DIAGNOSTIC,
258  native_unit_of_measurement=PERCENTAGE,
259  state_class=SensorStateClass.MEASUREMENT,
260  ),
261 ]
262 
263 SENSOR_LINK_TYPES = [
265  key="NewEthernetLinkStatus",
266  translation_key="ethernet_link_status",
267  entity_category=EntityCategory.DIAGNOSTIC,
268  ),
269 ]
270 
271 
273  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
274 ) -> None:
275  """Set up device tracker for Netgear component."""
276  router = hass.data[DOMAIN][entry.entry_id][KEY_ROUTER]
277  coordinator = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR]
278  coordinator_traffic = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_TRAFFIC]
279  coordinator_speed = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_SPEED]
280  coordinator_utilization = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_UTIL]
281  coordinator_link = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_LINK]
282 
284  NetgearRouterSensorEntity(coordinator, router, description)
285  for (coordinator, descriptions) in (
286  (coordinator_traffic, SENSOR_TRAFFIC_TYPES),
287  (coordinator_speed, SENSOR_SPEED_TYPES),
288  (coordinator_utilization, SENSOR_UTILIZATION),
289  (coordinator_link, SENSOR_LINK_TYPES),
290  )
291  for description in descriptions
292  )
293 
294  # Entities per network device
295  tracked = set()
296  sensors = ["type", "link_rate", "signal"]
297  if router.method_version == 2:
298  sensors.extend(["ssid", "conn_ap_mac"])
299 
300  @callback
301  def new_device_callback() -> None:
302  """Add new devices if needed."""
303  if not coordinator.data:
304  return
305 
306  new_entities: list[NetgearSensorEntity] = []
307 
308  for mac, device in router.devices.items():
309  if mac in tracked:
310  continue
311 
312  new_entities.extend(
313  NetgearSensorEntity(coordinator, router, device, attribute)
314  for attribute in sensors
315  )
316  tracked.add(mac)
317 
318  async_add_entities(new_entities)
319 
320  entry.async_on_unload(coordinator.async_add_listener(new_device_callback))
321 
322  coordinator.data = True
323  new_device_callback()
324 
325 
327  """Representation of a device connected to a Netgear router."""
328 
329  _attr_entity_registry_enabled_default = False
330 
331  def __init__(
332  self,
333  coordinator: DataUpdateCoordinator,
334  router: NetgearRouter,
335  device: dict,
336  attribute: str,
337  ) -> None:
338  """Initialize a Netgear device."""
339  super().__init__(coordinator, router, device)
340  self._attribute_attribute = attribute
341  self.entity_descriptionentity_description = SENSOR_TYPES[attribute]
342  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{self._mac}-{attribute}"
343  self._state_state = device.get(attribute)
344 
345  @property
346  def native_value(self):
347  """Return the state of the sensor."""
348  return self._state_state
349 
350  @callback
351  def async_update_device(self) -> None:
352  """Update the Netgear device."""
353  self._device_device_device = self._router_router.devices[self._mac_mac]
354  self._active_active_active = self._device_device_device["active"]
355  if self._device_device_device.get(self._attribute_attribute) is not None:
356  self._state_state = self._device_device_device[self._attribute_attribute]
357 
358 
360  """Representation of a device connected to a Netgear router."""
361 
362  _attr_entity_registry_enabled_default = False
363  entity_description: NetgearSensorEntityDescription
364 
365  def __init__(
366  self,
367  coordinator: DataUpdateCoordinator,
368  router: NetgearRouter,
369  entity_description: NetgearSensorEntityDescription,
370  ) -> None:
371  """Initialize a Netgear device."""
372  super().__init__(coordinator, router)
373  self.entity_descriptionentity_description = entity_description
374  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{router.serial_number}-{entity_description.key}-{entity_description.index}"
375 
376  self._value_value: StateType | date | datetime | Decimal = None
377  self.async_update_deviceasync_update_deviceasync_update_device()
378 
379  @property
380  def native_value(self):
381  """Return the state of the sensor."""
382  return self._value_value
383 
384  async def async_added_to_hass(self) -> None:
385  """Handle entity which will be added."""
386  await super().async_added_to_hass()
387  if self.coordinator.data is None:
388  sensor_data = await self.async_get_last_sensor_dataasync_get_last_sensor_data()
389  if sensor_data is not None:
390  self._value_value = sensor_data.native_value
391  else:
392  await self.coordinator.async_request_refresh()
393 
394  @callback
395  def async_update_device(self) -> None:
396  """Update the Netgear device."""
397  if self.coordinator.data is None:
398  return
399 
400  data = self.coordinator.data.get(self.entity_descriptionentity_description.key)
401  if data is None:
402  self._value_value = None
403  _LOGGER.debug(
404  "key '%s' not in Netgear router response '%s'",
405  self.entity_descriptionentity_description.key,
406  data,
407  )
408  return
409 
410  self._value_value = self.entity_descriptionentity_description.value(data)
None __init__(self, DataUpdateCoordinator coordinator, NetgearRouter router, NetgearSensorEntityDescription entity_description)
Definition: sensor.py:370
None __init__(self, DataUpdateCoordinator coordinator, NetgearRouter router, dict device, str attribute)
Definition: sensor.py:337
SensorExtraStoredData|None async_get_last_sensor_data(self)
Definition: __init__.py:934
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:274