Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """AVM FRITZ!Box binary sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime, timedelta
8 import logging
9 
10 from fritzconnection.lib.fritzstatus import FritzStatus
11 
13  SensorDeviceClass,
14  SensorEntity,
15  SensorEntityDescription,
16  SensorStateClass,
17 )
18 from homeassistant.config_entries import ConfigEntry
19 from homeassistant.const import (
20  SIGNAL_STRENGTH_DECIBELS,
21  EntityCategory,
22  UnitOfDataRate,
23  UnitOfInformation,
24 )
25 from homeassistant.core import HomeAssistant
26 from homeassistant.helpers.entity_platform import AddEntitiesCallback
27 from homeassistant.helpers.typing import StateType
28 from homeassistant.util.dt import utcnow
29 
30 from .const import DOMAIN, DSL_CONNECTION, UPTIME_DEVIATION
31 from .coordinator import AvmWrapper, ConnectionInfo
32 from .entity import FritzBoxBaseCoordinatorEntity, FritzEntityDescription
33 
34 _LOGGER = logging.getLogger(__name__)
35 
36 
37 def _uptime_calculation(seconds_uptime: float, last_value: datetime | None) -> datetime:
38  """Calculate uptime with deviation."""
39  delta_uptime = utcnow() - timedelta(seconds=seconds_uptime)
40 
41  if (
42  not last_value
43  or abs((delta_uptime - last_value).total_seconds()) > UPTIME_DEVIATION
44  ):
45  return delta_uptime
46 
47  return last_value
48 
49 
51  status: FritzStatus, last_value: datetime
52 ) -> datetime:
53  """Return uptime from device."""
54  return _uptime_calculation(status.device_uptime, last_value)
55 
56 
58  status: FritzStatus, last_value: datetime | None
59 ) -> datetime:
60  """Return uptime from connection."""
61  return _uptime_calculation(status.connection_uptime, last_value)
62 
63 
64 def _retrieve_external_ip_state(status: FritzStatus, last_value: str) -> str:
65  """Return external ip from device."""
66  return status.external_ip # type: ignore[no-any-return]
67 
68 
69 def _retrieve_external_ipv6_state(status: FritzStatus, last_value: str) -> str:
70  """Return external ipv6 from device."""
71  return str(status.external_ipv6)
72 
73 
74 def _retrieve_kb_s_sent_state(status: FritzStatus, last_value: str) -> float:
75  """Return upload transmission rate."""
76  return round(status.transmission_rate[0] / 1000, 1) # type: ignore[no-any-return]
77 
78 
79 def _retrieve_kb_s_received_state(status: FritzStatus, last_value: str) -> float:
80  """Return download transmission rate."""
81  return round(status.transmission_rate[1] / 1000, 1) # type: ignore[no-any-return]
82 
83 
84 def _retrieve_max_kb_s_sent_state(status: FritzStatus, last_value: str) -> float:
85  """Return upload max transmission rate."""
86  return round(status.max_bit_rate[0] / 1000, 1) # type: ignore[no-any-return]
87 
88 
89 def _retrieve_max_kb_s_received_state(status: FritzStatus, last_value: str) -> float:
90  """Return download max transmission rate."""
91  return round(status.max_bit_rate[1] / 1000, 1) # type: ignore[no-any-return]
92 
93 
94 def _retrieve_gb_sent_state(status: FritzStatus, last_value: str) -> float:
95  """Return upload total data."""
96  return round(status.bytes_sent / 1000 / 1000 / 1000, 1) # type: ignore[no-any-return]
97 
98 
99 def _retrieve_gb_received_state(status: FritzStatus, last_value: str) -> float:
100  """Return download total data."""
101  return round(status.bytes_received / 1000 / 1000 / 1000, 1) # type: ignore[no-any-return]
102 
103 
104 def _retrieve_link_kb_s_sent_state(status: FritzStatus, last_value: str) -> float:
105  """Return upload link rate."""
106  return round(status.max_linked_bit_rate[0] / 1000, 1) # type: ignore[no-any-return]
107 
108 
109 def _retrieve_link_kb_s_received_state(status: FritzStatus, last_value: str) -> float:
110  """Return download link rate."""
111  return round(status.max_linked_bit_rate[1] / 1000, 1) # type: ignore[no-any-return]
112 
113 
115  status: FritzStatus, last_value: str
116 ) -> float:
117  """Return upload noise margin."""
118  return status.noise_margin[0] / 10 # type: ignore[no-any-return]
119 
120 
122  status: FritzStatus, last_value: str
123 ) -> float:
124  """Return download noise margin."""
125  return status.noise_margin[1] / 10 # type: ignore[no-any-return]
126 
127 
129  status: FritzStatus, last_value: str
130 ) -> float:
131  """Return upload line attenuation."""
132  return status.attenuation[0] / 10 # type: ignore[no-any-return]
133 
134 
136  status: FritzStatus, last_value: str
137 ) -> float:
138  """Return download line attenuation."""
139  return status.attenuation[1] / 10 # type: ignore[no-any-return]
140 
141 
142 @dataclass(frozen=True, kw_only=True)
144  """Describes Fritz sensor entity."""
145 
146  is_suitable: Callable[[ConnectionInfo], bool] = lambda info: info.wan_enabled
147 
148 
149 SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
151  key="external_ip",
152  translation_key="external_ip",
153  value_fn=_retrieve_external_ip_state,
154  ),
156  key="external_ipv6",
157  translation_key="external_ipv6",
158  value_fn=_retrieve_external_ipv6_state,
159  is_suitable=lambda info: info.ipv6_active,
160  ),
162  key="device_uptime",
163  translation_key="device_uptime",
164  device_class=SensorDeviceClass.TIMESTAMP,
165  entity_category=EntityCategory.DIAGNOSTIC,
166  value_fn=_retrieve_device_uptime_state,
167  is_suitable=lambda info: True,
168  ),
170  key="connection_uptime",
171  translation_key="connection_uptime",
172  device_class=SensorDeviceClass.TIMESTAMP,
173  entity_category=EntityCategory.DIAGNOSTIC,
174  value_fn=_retrieve_connection_uptime_state,
175  ),
177  key="kb_s_sent",
178  translation_key="kb_s_sent",
179  state_class=SensorStateClass.MEASUREMENT,
180  native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
181  device_class=SensorDeviceClass.DATA_RATE,
182  value_fn=_retrieve_kb_s_sent_state,
183  ),
185  key="kb_s_received",
186  translation_key="kb_s_received",
187  state_class=SensorStateClass.MEASUREMENT,
188  native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
189  device_class=SensorDeviceClass.DATA_RATE,
190  value_fn=_retrieve_kb_s_received_state,
191  ),
193  key="max_kb_s_sent",
194  translation_key="max_kb_s_sent",
195  native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
196  device_class=SensorDeviceClass.DATA_RATE,
197  entity_category=EntityCategory.DIAGNOSTIC,
198  value_fn=_retrieve_max_kb_s_sent_state,
199  ),
201  key="max_kb_s_received",
202  translation_key="max_kb_s_received",
203  native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
204  device_class=SensorDeviceClass.DATA_RATE,
205  entity_category=EntityCategory.DIAGNOSTIC,
206  value_fn=_retrieve_max_kb_s_received_state,
207  ),
209  key="gb_sent",
210  translation_key="gb_sent",
211  state_class=SensorStateClass.TOTAL_INCREASING,
212  native_unit_of_measurement=UnitOfInformation.GIGABYTES,
213  device_class=SensorDeviceClass.DATA_SIZE,
214  value_fn=_retrieve_gb_sent_state,
215  ),
217  key="gb_received",
218  translation_key="gb_received",
219  state_class=SensorStateClass.TOTAL_INCREASING,
220  native_unit_of_measurement=UnitOfInformation.GIGABYTES,
221  device_class=SensorDeviceClass.DATA_SIZE,
222  value_fn=_retrieve_gb_received_state,
223  ),
225  key="link_kb_s_sent",
226  translation_key="link_kb_s_sent",
227  native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
228  device_class=SensorDeviceClass.DATA_RATE,
229  value_fn=_retrieve_link_kb_s_sent_state,
230  ),
232  key="link_kb_s_received",
233  translation_key="link_kb_s_received",
234  native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
235  device_class=SensorDeviceClass.DATA_RATE,
236  value_fn=_retrieve_link_kb_s_received_state,
237  ),
239  key="link_noise_margin_sent",
240  translation_key="link_noise_margin_sent",
241  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
242  value_fn=_retrieve_link_noise_margin_sent_state,
243  is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
244  ),
246  key="link_noise_margin_received",
247  translation_key="link_noise_margin_received",
248  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
249  value_fn=_retrieve_link_noise_margin_received_state,
250  is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
251  ),
253  key="link_attenuation_sent",
254  translation_key="link_attenuation_sent",
255  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
256  value_fn=_retrieve_link_attenuation_sent_state,
257  is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
258  ),
260  key="link_attenuation_received",
261  translation_key="link_attenuation_received",
262  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
263  value_fn=_retrieve_link_attenuation_received_state,
264  is_suitable=lambda info: info.wan_enabled and info.connection == DSL_CONNECTION,
265  ),
266 )
267 
268 
270  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
271 ) -> None:
272  """Set up entry."""
273  _LOGGER.debug("Setting up FRITZ!Box sensors")
274  avm_wrapper: AvmWrapper = hass.data[DOMAIN][entry.entry_id]
275 
276  connection_info = await avm_wrapper.async_get_connection_info()
277 
278  entities = [
279  FritzBoxSensor(avm_wrapper, entry.title, description)
280  for description in SENSOR_TYPES
281  if description.is_suitable(connection_info)
282  ]
283 
284  async_add_entities(entities)
285 
286 
288  """Define FRITZ!Box connectivity class."""
289 
290  entity_description: FritzSensorEntityDescription
291 
292  @property
293  def native_value(self) -> StateType:
294  """Return the value reported by the sensor."""
295  return self.coordinator.data["entity_states"].get(self.entity_descriptionentity_description.key)
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
float _retrieve_kb_s_sent_state(FritzStatus status, str last_value)
Definition: sensor.py:74
float _retrieve_max_kb_s_received_state(FritzStatus status, str last_value)
Definition: sensor.py:89
float _retrieve_link_noise_margin_received_state(FritzStatus status, str last_value)
Definition: sensor.py:123
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:271
str _retrieve_external_ipv6_state(FritzStatus status, str last_value)
Definition: sensor.py:69
float _retrieve_gb_sent_state(FritzStatus status, str last_value)
Definition: sensor.py:94
float _retrieve_link_attenuation_received_state(FritzStatus status, str last_value)
Definition: sensor.py:137
str _retrieve_external_ip_state(FritzStatus status, str last_value)
Definition: sensor.py:64
float _retrieve_max_kb_s_sent_state(FritzStatus status, str last_value)
Definition: sensor.py:84
float _retrieve_link_noise_margin_sent_state(FritzStatus status, str last_value)
Definition: sensor.py:116
float _retrieve_gb_received_state(FritzStatus status, str last_value)
Definition: sensor.py:99
float _retrieve_kb_s_received_state(FritzStatus status, str last_value)
Definition: sensor.py:79
datetime _retrieve_connection_uptime_state(FritzStatus status, datetime|None last_value)
Definition: sensor.py:59
float _retrieve_link_attenuation_sent_state(FritzStatus status, str last_value)
Definition: sensor.py:130
datetime _retrieve_device_uptime_state(FritzStatus status, datetime last_value)
Definition: sensor.py:52
float _retrieve_link_kb_s_received_state(FritzStatus status, str last_value)
Definition: sensor.py:109
float _retrieve_link_kb_s_sent_state(FritzStatus status, str last_value)
Definition: sensor.py:104
datetime _uptime_calculation(float seconds_uptime, datetime|None last_value)
Definition: sensor.py:37