Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for monitoring the local system."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 import contextlib
7 from dataclasses import dataclass
8 from datetime import datetime
9 from functools import lru_cache
10 import ipaddress
11 import logging
12 import socket
13 import sys
14 import time
15 from typing import Any, Literal
16 
18  DOMAIN as SENSOR_DOMAIN,
19  SensorDeviceClass,
20  SensorEntity,
21  SensorEntityDescription,
22  SensorStateClass,
23 )
24 from homeassistant.const import (
25  PERCENTAGE,
26  EntityCategory,
27  UnitOfDataRate,
28  UnitOfInformation,
29  UnitOfTemperature,
30 )
31 from homeassistant.core import HomeAssistant, callback
32 from homeassistant.helpers import entity_registry as er
33 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
34 from homeassistant.helpers.entity_platform import AddEntitiesCallback
35 from homeassistant.helpers.typing import StateType
36 from homeassistant.helpers.update_coordinator import CoordinatorEntity
37 from homeassistant.util import slugify
38 
39 from . import SystemMonitorConfigEntry
40 from .const import DOMAIN, NET_IO_TYPES
41 from .coordinator import SystemMonitorCoordinator
42 from .util import get_all_disk_mounts, get_all_network_interfaces, read_cpu_temperature
43 
44 _LOGGER = logging.getLogger(__name__)
45 
46 CONF_ARG = "arg"
47 
48 
49 SENSOR_TYPE_NAME = 0
50 SENSOR_TYPE_UOM = 1
51 SENSOR_TYPE_ICON = 2
52 SENSOR_TYPE_DEVICE_CLASS = 3
53 SENSOR_TYPE_MANDATORY_ARG = 4
54 
55 SIGNAL_SYSTEMMONITOR_UPDATE = "systemmonitor_update"
56 
57 
58 @lru_cache
59 def get_cpu_icon() -> Literal["mdi:cpu-64-bit", "mdi:cpu-32-bit"]:
60  """Return cpu icon."""
61  if sys.maxsize > 2**32:
62  return "mdi:cpu-64-bit"
63  return "mdi:cpu-32-bit"
64 
65 
66 def get_network(entity: SystemMonitorSensor) -> float | None:
67  """Return network in and out."""
68  counters = entity.coordinator.data.io_counters
69  if entity.argument in counters:
70  counter = counters[entity.argument][IO_COUNTER[entity.entity_description.key]]
71  return round(counter / 1024**2, 1)
72  return None
73 
74 
75 def get_packets(entity: SystemMonitorSensor) -> float | None:
76  """Return packets in and out."""
77  counters = entity.coordinator.data.io_counters
78  if entity.argument in counters:
79  return counters[entity.argument][IO_COUNTER[entity.entity_description.key]]
80  return None
81 
82 
83 def get_throughput(entity: SystemMonitorSensor) -> float | None:
84  """Return network throughput in and out."""
85  counters = entity.coordinator.data.io_counters
86  state = None
87  if entity.argument in counters:
88  counter = counters[entity.argument][IO_COUNTER[entity.entity_description.key]]
89  now = time.monotonic()
90  if (
91  (value := entity.value)
92  and (update_time := entity.update_time)
93  and value < counter
94  ):
95  state = round(
96  (counter - value) / 1000**2 / (now - update_time),
97  3,
98  )
99  entity.update_time = now
100  entity.value = counter
101  return state
102 
103 
105  entity: SystemMonitorSensor,
106 ) -> str | None:
107  """Return network ip address."""
108  addresses = entity.coordinator.data.addresses
109  if entity.argument in addresses:
110  for addr in addresses[entity.argument]:
111  if addr.family == IF_ADDRS_FAMILY[entity.entity_description.key]:
112  address = ipaddress.ip_address(addr.address)
113  if address.version == 6 and (
114  address.is_link_local or address.is_loopback
115  ):
116  continue
117  return addr.address
118  return None
119 
120 
121 @dataclass(frozen=True, kw_only=True)
123  """Describes System Monitor sensor entities."""
124 
125  value_fn: Callable[[SystemMonitorSensor], StateType | datetime]
126  add_to_update: Callable[[SystemMonitorSensor], tuple[str, str]]
127  none_is_unavailable: bool = False
128  mandatory_arg: bool = False
129  placeholder: str | None = None
130 
131 
132 SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = {
134  key="disk_free",
135  translation_key="disk_free",
136  placeholder="mount_point",
137  native_unit_of_measurement=UnitOfInformation.GIBIBYTES,
138  device_class=SensorDeviceClass.DATA_SIZE,
139  state_class=SensorStateClass.MEASUREMENT,
140  value_fn=lambda entity: round(
141  entity.coordinator.data.disk_usage[entity.argument].free / 1024**3, 1
142  )
143  if entity.argument in entity.coordinator.data.disk_usage
144  else None,
145  none_is_unavailable=True,
146  add_to_update=lambda entity: ("disks", entity.argument),
147  ),
149  key="disk_use",
150  translation_key="disk_use",
151  placeholder="mount_point",
152  native_unit_of_measurement=UnitOfInformation.GIBIBYTES,
153  device_class=SensorDeviceClass.DATA_SIZE,
154  state_class=SensorStateClass.MEASUREMENT,
155  value_fn=lambda entity: round(
156  entity.coordinator.data.disk_usage[entity.argument].used / 1024**3, 1
157  )
158  if entity.argument in entity.coordinator.data.disk_usage
159  else None,
160  none_is_unavailable=True,
161  add_to_update=lambda entity: ("disks", entity.argument),
162  ),
163  "disk_use_percent": SysMonitorSensorEntityDescription(
164  key="disk_use_percent",
165  translation_key="disk_use_percent",
166  placeholder="mount_point",
167  native_unit_of_measurement=PERCENTAGE,
168  state_class=SensorStateClass.MEASUREMENT,
169  value_fn=lambda entity: entity.coordinator.data.disk_usage[
170  entity.argument
171  ].percent
172  if entity.argument in entity.coordinator.data.disk_usage
173  else None,
174  none_is_unavailable=True,
175  add_to_update=lambda entity: ("disks", entity.argument),
176  ),
177  "ipv4_address": SysMonitorSensorEntityDescription(
178  key="ipv4_address",
179  translation_key="ipv4_address",
180  placeholder="ip_address",
181  mandatory_arg=True,
182  value_fn=get_ip_address,
183  add_to_update=lambda entity: ("addresses", ""),
184  ),
185  "ipv6_address": SysMonitorSensorEntityDescription(
186  key="ipv6_address",
187  translation_key="ipv6_address",
188  placeholder="ip_address",
189  mandatory_arg=True,
190  value_fn=get_ip_address,
191  add_to_update=lambda entity: ("addresses", ""),
192  ),
194  key="last_boot",
195  translation_key="last_boot",
196  device_class=SensorDeviceClass.TIMESTAMP,
197  value_fn=lambda entity: entity.coordinator.data.boot_time,
198  add_to_update=lambda entity: ("boot", ""),
199  ),
201  key="load_15m",
202  translation_key="load_15m",
203  icon=get_cpu_icon(),
204  state_class=SensorStateClass.MEASUREMENT,
205  value_fn=lambda entity: round(entity.coordinator.data.load[2], 2),
206  add_to_update=lambda entity: ("load", ""),
207  ),
209  key="load_1m",
210  translation_key="load_1m",
211  icon=get_cpu_icon(),
212  state_class=SensorStateClass.MEASUREMENT,
213  value_fn=lambda entity: round(entity.coordinator.data.load[0], 2),
214  add_to_update=lambda entity: ("load", ""),
215  ),
217  key="load_5m",
218  translation_key="load_5m",
219  icon=get_cpu_icon(),
220  state_class=SensorStateClass.MEASUREMENT,
221  value_fn=lambda entity: round(entity.coordinator.data.load[1], 2),
222  add_to_update=lambda entity: ("load", ""),
223  ),
224  "memory_free": SysMonitorSensorEntityDescription(
225  key="memory_free",
226  translation_key="memory_free",
227  native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
228  device_class=SensorDeviceClass.DATA_SIZE,
229  state_class=SensorStateClass.MEASUREMENT,
230  value_fn=lambda entity: round(
231  entity.coordinator.data.memory.available / 1024**2, 1
232  ),
233  add_to_update=lambda entity: ("memory", ""),
234  ),
235  "memory_use": SysMonitorSensorEntityDescription(
236  key="memory_use",
237  translation_key="memory_use",
238  native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
239  device_class=SensorDeviceClass.DATA_SIZE,
240  state_class=SensorStateClass.MEASUREMENT,
241  value_fn=lambda entity: round(
242  (
243  entity.coordinator.data.memory.total
244  - entity.coordinator.data.memory.available
245  )
246  / 1024**2,
247  1,
248  ),
249  add_to_update=lambda entity: ("memory", ""),
250  ),
251  "memory_use_percent": SysMonitorSensorEntityDescription(
252  key="memory_use_percent",
253  translation_key="memory_use_percent",
254  native_unit_of_measurement=PERCENTAGE,
255  state_class=SensorStateClass.MEASUREMENT,
256  value_fn=lambda entity: entity.coordinator.data.memory.percent,
257  add_to_update=lambda entity: ("memory", ""),
258  ),
259  "network_in": SysMonitorSensorEntityDescription(
260  key="network_in",
261  translation_key="network_in",
262  placeholder="interface",
263  native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
264  device_class=SensorDeviceClass.DATA_SIZE,
265  state_class=SensorStateClass.TOTAL_INCREASING,
266  mandatory_arg=True,
267  value_fn=get_network,
268  add_to_update=lambda entity: ("io_counters", ""),
269  ),
270  "network_out": SysMonitorSensorEntityDescription(
271  key="network_out",
272  translation_key="network_out",
273  placeholder="interface",
274  native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
275  device_class=SensorDeviceClass.DATA_SIZE,
276  state_class=SensorStateClass.TOTAL_INCREASING,
277  mandatory_arg=True,
278  value_fn=get_network,
279  add_to_update=lambda entity: ("io_counters", ""),
280  ),
281  "packets_in": SysMonitorSensorEntityDescription(
282  key="packets_in",
283  translation_key="packets_in",
284  placeholder="interface",
285  state_class=SensorStateClass.TOTAL_INCREASING,
286  mandatory_arg=True,
287  value_fn=get_packets,
288  add_to_update=lambda entity: ("io_counters", ""),
289  ),
290  "packets_out": SysMonitorSensorEntityDescription(
291  key="packets_out",
292  translation_key="packets_out",
293  placeholder="interface",
294  state_class=SensorStateClass.TOTAL_INCREASING,
295  mandatory_arg=True,
296  value_fn=get_packets,
297  add_to_update=lambda entity: ("io_counters", ""),
298  ),
299  "throughput_network_in": SysMonitorSensorEntityDescription(
300  key="throughput_network_in",
301  translation_key="throughput_network_in",
302  placeholder="interface",
303  native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND,
304  device_class=SensorDeviceClass.DATA_RATE,
305  state_class=SensorStateClass.MEASUREMENT,
306  mandatory_arg=True,
307  value_fn=get_throughput,
308  add_to_update=lambda entity: ("io_counters", ""),
309  ),
310  "throughput_network_out": SysMonitorSensorEntityDescription(
311  key="throughput_network_out",
312  translation_key="throughput_network_out",
313  placeholder="interface",
314  native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND,
315  device_class=SensorDeviceClass.DATA_RATE,
316  state_class=SensorStateClass.MEASUREMENT,
317  mandatory_arg=True,
318  value_fn=get_throughput,
319  add_to_update=lambda entity: ("io_counters", ""),
320  ),
321  "processor_use": SysMonitorSensorEntityDescription(
322  key="processor_use",
323  translation_key="processor_use",
324  native_unit_of_measurement=PERCENTAGE,
325  icon=get_cpu_icon(),
326  state_class=SensorStateClass.MEASUREMENT,
327  value_fn=lambda entity: (
328  round(entity.coordinator.data.cpu_percent)
329  if entity.coordinator.data.cpu_percent
330  else None
331  ),
332  add_to_update=lambda entity: ("cpu_percent", ""),
333  ),
334  "processor_temperature": SysMonitorSensorEntityDescription(
335  key="processor_temperature",
336  translation_key="processor_temperature",
337  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
338  device_class=SensorDeviceClass.TEMPERATURE,
339  state_class=SensorStateClass.MEASUREMENT,
340  value_fn=lambda entity: read_cpu_temperature(
341  entity.coordinator.data.temperatures
342  ),
343  none_is_unavailable=True,
344  add_to_update=lambda entity: ("temperatures", ""),
345  ),
347  key="swap_free",
348  translation_key="swap_free",
349  native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
350  device_class=SensorDeviceClass.DATA_SIZE,
351  state_class=SensorStateClass.MEASUREMENT,
352  value_fn=lambda entity: round(entity.coordinator.data.swap.free / 1024**2, 1),
353  add_to_update=lambda entity: ("swap", ""),
354  ),
356  key="swap_use",
357  translation_key="swap_use",
358  native_unit_of_measurement=UnitOfInformation.MEBIBYTES,
359  device_class=SensorDeviceClass.DATA_SIZE,
360  state_class=SensorStateClass.MEASUREMENT,
361  value_fn=lambda entity: round(entity.coordinator.data.swap.used / 1024**2, 1),
362  add_to_update=lambda entity: ("swap", ""),
363  ),
364  "swap_use_percent": SysMonitorSensorEntityDescription(
365  key="swap_use_percent",
366  translation_key="swap_use_percent",
367  native_unit_of_measurement=PERCENTAGE,
368  state_class=SensorStateClass.MEASUREMENT,
369  value_fn=lambda entity: entity.coordinator.data.swap.percent,
370  add_to_update=lambda entity: ("swap", ""),
371  ),
372 }
373 
374 
375 def check_legacy_resource(resource: str, resources: set[str]) -> bool:
376  """Return True if legacy resource was configured."""
377  # This function to check legacy resources can be removed
378  # once we are removing the import from YAML
379  if resource in resources:
380  _LOGGER.debug("Checking %s in %s returns True", resource, ", ".join(resources))
381  return True
382  _LOGGER.debug("Checking %s in %s returns False", resource, ", ".join(resources))
383  return False
384 
385 
386 IO_COUNTER = {
387  "network_out": 0,
388  "network_in": 1,
389  "packets_out": 2,
390  "packets_in": 3,
391  "throughput_network_out": 0,
392  "throughput_network_in": 1,
393 }
394 IF_ADDRS_FAMILY = {"ipv4_address": socket.AF_INET, "ipv6_address": socket.AF_INET6}
395 
396 
398  hass: HomeAssistant,
399  entry: SystemMonitorConfigEntry,
400  async_add_entities: AddEntitiesCallback,
401 ) -> None:
402  """Set up System Monitor sensors based on a config entry."""
403  entities: list[SystemMonitorSensor] = []
404  legacy_resources: set[str] = set(entry.options.get("resources", []))
405  loaded_resources: set[str] = set()
406  coordinator = entry.runtime_data.coordinator
407  psutil_wrapper = entry.runtime_data.psutil_wrapper
408  sensor_data = coordinator.data
409 
410  def get_arguments() -> dict[str, Any]:
411  """Return startup information."""
412  return {
413  "disk_arguments": get_all_disk_mounts(hass, psutil_wrapper),
414  "network_arguments": get_all_network_interfaces(hass, psutil_wrapper),
415  }
416 
417  cpu_temperature: float | None = None
418  with contextlib.suppress(AttributeError):
419  cpu_temperature = read_cpu_temperature(sensor_data.temperatures)
420 
421  startup_arguments = await hass.async_add_executor_job(get_arguments)
422  startup_arguments["cpu_temperature"] = cpu_temperature
423 
424  _LOGGER.debug("Setup from options %s", entry.options)
425 
426  for _type, sensor_description in SENSOR_TYPES.items():
427  if _type.startswith("disk_"):
428  for argument in startup_arguments["disk_arguments"]:
429  is_enabled = check_legacy_resource(
430  f"{_type}_{argument}", legacy_resources
431  )
432  loaded_resources.add(slugify(f"{_type}_{argument}"))
433  entities.append(
435  coordinator,
436  sensor_description,
437  entry.entry_id,
438  argument,
439  is_enabled,
440  )
441  )
442  continue
443 
444  if _type.startswith("ipv"):
445  for argument in startup_arguments["network_arguments"]:
446  is_enabled = check_legacy_resource(
447  f"{_type}_{argument}", legacy_resources
448  )
449  loaded_resources.add(slugify(f"{_type}_{argument}"))
450  entities.append(
452  coordinator,
453  sensor_description,
454  entry.entry_id,
455  argument,
456  is_enabled,
457  )
458  )
459  continue
460 
461  if _type == "last_boot":
462  argument = ""
463  is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
464  loaded_resources.add(slugify(f"{_type}_{argument}"))
465  entities.append(
467  coordinator,
468  sensor_description,
469  entry.entry_id,
470  argument,
471  is_enabled,
472  )
473  )
474  continue
475 
476  if _type.startswith("load_"):
477  argument = ""
478  is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
479  loaded_resources.add(slugify(f"{_type}_{argument}"))
480  entities.append(
482  coordinator,
483  sensor_description,
484  entry.entry_id,
485  argument,
486  is_enabled,
487  )
488  )
489  continue
490 
491  if _type.startswith("memory_"):
492  argument = ""
493  is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
494  loaded_resources.add(slugify(f"{_type}_{argument}"))
495  entities.append(
497  coordinator,
498  sensor_description,
499  entry.entry_id,
500  argument,
501  is_enabled,
502  )
503  )
504 
505  if _type in NET_IO_TYPES:
506  for argument in startup_arguments["network_arguments"]:
507  is_enabled = check_legacy_resource(
508  f"{_type}_{argument}", legacy_resources
509  )
510  loaded_resources.add(slugify(f"{_type}_{argument}"))
511  entities.append(
513  coordinator,
514  sensor_description,
515  entry.entry_id,
516  argument,
517  is_enabled,
518  )
519  )
520  continue
521 
522  if _type == "processor_use":
523  argument = ""
524  is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
525  loaded_resources.add(slugify(f"{_type}_{argument}"))
526  entities.append(
528  coordinator,
529  sensor_description,
530  entry.entry_id,
531  argument,
532  is_enabled,
533  )
534  )
535  continue
536 
537  if _type == "processor_temperature":
538  if not startup_arguments["cpu_temperature"]:
539  # Don't load processor temperature sensor if we can't read it.
540  continue
541  argument = ""
542  is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
543  loaded_resources.add(slugify(f"{_type}_{argument}"))
544  entities.append(
546  coordinator,
547  sensor_description,
548  entry.entry_id,
549  argument,
550  is_enabled,
551  )
552  )
553  continue
554 
555  if _type.startswith("swap_"):
556  argument = ""
557  is_enabled = check_legacy_resource(f"{_type}_{argument}", legacy_resources)
558  loaded_resources.add(slugify(f"{_type}_{argument}"))
559  entities.append(
561  coordinator,
562  sensor_description,
563  entry.entry_id,
564  argument,
565  is_enabled,
566  )
567  )
568 
569  # Ensure legacy imported disk_* resources are loaded if they are not part
570  # of mount points automatically discovered
571  for resource in legacy_resources:
572  if resource.startswith("disk_"):
573  check_resource = slugify(resource)
574  _LOGGER.debug(
575  "Check resource %s already loaded in %s",
576  check_resource,
577  loaded_resources,
578  )
579  if check_resource not in loaded_resources:
580  loaded_resources.add(check_resource)
581  split_index = resource.rfind("_")
582  _type = resource[:split_index]
583  argument = resource[split_index + 1 :]
584  _LOGGER.debug("Loading legacy %s with argument %s", _type, argument)
585  entities.append(
587  coordinator,
588  SENSOR_TYPES[_type],
589  entry.entry_id,
590  argument,
591  True,
592  )
593  )
594 
595  @callback
596  def clean_obsolete_entities() -> None:
597  """Remove entities which are disabled and not supported from setup."""
598  entity_registry = er.async_get(hass)
599  entities = entity_registry.entities.get_entries_for_config_entry_id(
600  entry.entry_id
601  )
602  for entity in entities:
603  if (
604  entity.unique_id not in loaded_resources
605  and entity.disabled is True
606  and (
607  entity_id := entity_registry.async_get_entity_id(
608  SENSOR_DOMAIN, DOMAIN, entity.unique_id
609  )
610  )
611  ):
612  entity_registry.async_remove(entity_id)
613 
614  clean_obsolete_entities()
615 
616  async_add_entities(entities)
617 
618 
619 class SystemMonitorSensor(CoordinatorEntity[SystemMonitorCoordinator], SensorEntity):
620  """Implementation of a system monitor sensor."""
621 
622  _attr_has_entity_name = True
623  _attr_entity_category = EntityCategory.DIAGNOSTIC
624  entity_description: SysMonitorSensorEntityDescription
625  argument: str
626 
627  def __init__(
628  self,
629  coordinator: SystemMonitorCoordinator,
630  sensor_description: SysMonitorSensorEntityDescription,
631  entry_id: str,
632  argument: str,
633  legacy_enabled: bool = False,
634  ) -> None:
635  """Initialize the sensor."""
636  super().__init__(coordinator)
637  self.entity_descriptionentity_description = sensor_description
638  if self.entity_descriptionentity_description.placeholder:
639  self._attr_translation_placeholders_attr_translation_placeholders = {
640  self.entity_descriptionentity_description.placeholder: argument
641  }
642  self._attr_unique_id: str = slugify(f"{sensor_description.key}_{argument}")
643  self._attr_entity_registry_enabled_default_attr_entity_registry_enabled_default = legacy_enabled
644  self._attr_device_info_attr_device_info = DeviceInfo(
645  entry_type=DeviceEntryType.SERVICE,
646  identifiers={(DOMAIN, entry_id)},
647  manufacturer="System Monitor",
648  name="System Monitor",
649  )
650  self.argumentargument = argument
651  self.value: int | None = None
652  self.update_time: float | None = None
653  self._attr_native_value_attr_native_value = self.entity_descriptionentity_description.value_fn(self)
654 
655  async def async_added_to_hass(self) -> None:
656  """When added to hass."""
657  self.coordinator.update_subscribers[
658  self.entity_descriptionentity_description.add_to_update(self)
659  ].add(self.entity_identity_id)
660  return await super().async_added_to_hass()
661 
662  async def async_will_remove_from_hass(self) -> None:
663  """When removed from hass."""
664  self.coordinator.update_subscribers[
665  self.entity_descriptionentity_description.add_to_update(self)
666  ].remove(self.entity_identity_id)
667  return await super().async_will_remove_from_hass()
668 
669  @callback
670  def _handle_coordinator_update(self) -> None:
671  """Handle updated data from the coordinator."""
672  # Set the native value here so we can use it in available property
673  # without having to recalculate it
674  self._attr_native_value_attr_native_value = self.entity_descriptionentity_description.value_fn(self)
676 
677  @property
678  def available(self) -> bool:
679  """Return if entity is available."""
680  if self.entity_descriptionentity_description.none_is_unavailable:
681  return (
682  self.coordinator.last_update_success is True
683  and self.native_valuenative_value is not None
684  )
685  return super().available
StateType|date|datetime|Decimal native_value(self)
Definition: __init__.py:460
None __init__(self, SystemMonitorCoordinator coordinator, SysMonitorSensorEntityDescription sensor_description, str entry_id, str argument, bool legacy_enabled=False)
Definition: sensor.py:634
argparse.Namespace get_arguments()
Definition: __main__.py:81
bool add(self, _T matcher)
Definition: match.py:185
bool remove(self, _T matcher)
Definition: match.py:214
bool check_legacy_resource(str resource, set[str] resources)
Definition: sensor.py:375
float|None get_packets(SystemMonitorSensor entity)
Definition: sensor.py:75
float|None get_throughput(SystemMonitorSensor entity)
Definition: sensor.py:83
str|None get_ip_address(SystemMonitorSensor entity)
Definition: sensor.py:106
Literal["mdi:cpu-64-bit", "mdi:cpu-32-bit"] get_cpu_icon()
Definition: sensor.py:59
None async_setup_entry(HomeAssistant hass, SystemMonitorConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:401
float|None get_network(SystemMonitorSensor entity)
Definition: sensor.py:66
float|None read_cpu_temperature(dict[str, list[shwtemp]] temps)
Definition: util.py:82
set[str] get_all_disk_mounts(HomeAssistant hass, ha_psutil.PsutilWrapper psutil_wrapper)
Definition: util.py:20
set[str] get_all_network_interfaces(HomeAssistant hass, ha_psutil.PsutilWrapper psutil_wrapper)
Definition: util.py:59