Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Amcrest IP camera sensors."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 from typing import TYPE_CHECKING
8 
9 from amcrest import AmcrestError
10 
11 from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
12 from homeassistant.const import CONF_NAME, CONF_SENSORS, PERCENTAGE
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.dispatcher import async_dispatcher_connect
15 from homeassistant.helpers.entity_platform import AddEntitiesCallback
16 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
17 
18 from .const import DATA_AMCREST, DEVICES, SENSOR_SCAN_INTERVAL_SECS, SERVICE_UPDATE
19 from .helpers import log_update_error, service_signal
20 
21 if TYPE_CHECKING:
22  from . import AmcrestDevice
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 SCAN_INTERVAL = timedelta(seconds=SENSOR_SCAN_INTERVAL_SECS)
27 
28 SENSOR_PTZ_PRESET = "ptz_preset"
29 SENSOR_SDCARD = "sdcard"
30 
31 SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
33  key=SENSOR_PTZ_PRESET,
34  name="PTZ Preset",
35  icon="mdi:camera-iris",
36  ),
38  key=SENSOR_SDCARD,
39  name="SD Used",
40  native_unit_of_measurement=PERCENTAGE,
41  icon="mdi:sd",
42  ),
43 )
44 
45 SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
46 
47 
49  hass: HomeAssistant,
50  config: ConfigType,
51  async_add_entities: AddEntitiesCallback,
52  discovery_info: DiscoveryInfoType | None = None,
53 ) -> None:
54  """Set up a sensor for an Amcrest IP Camera."""
55  if discovery_info is None:
56  return
57 
58  name = discovery_info[CONF_NAME]
59  device = hass.data[DATA_AMCREST][DEVICES][name]
60  sensors = discovery_info[CONF_SENSORS]
62  [
63  AmcrestSensor(name, device, description)
64  for description in SENSOR_TYPES
65  if description.key in sensors
66  ],
67  True,
68  )
69 
70 
72  """A sensor implementation for Amcrest IP camera."""
73 
74  def __init__(
75  self, name: str, device: AmcrestDevice, description: SensorEntityDescription
76  ) -> None:
77  """Initialize a sensor for Amcrest camera."""
78  self.entity_descriptionentity_description = description
79  self._signal_name_signal_name = name
80  self._api_api = device.api
81  self._channel_channel = device.channel
82 
83  self._attr_name_attr_name = f"{name} {description.name}"
84  self._attr_extra_state_attributes_attr_extra_state_attributes = {}
85 
86  @property
87  def available(self) -> bool:
88  """Return True if entity is available."""
89  return self._api_api.available
90 
91  async def async_update(self) -> None:
92  """Get the latest data and updates the state."""
93  if not self.availableavailableavailable:
94  return
95  _LOGGER.debug("Updating %s sensor", self.namename)
96 
97  sensor_type = self.entity_descriptionentity_description.key
98 
99  try:
100  if self._attr_unique_id_attr_unique_id is None and (
101  serial_number := (await self._api_api.async_serial_number)
102  ):
103  self._attr_unique_id_attr_unique_id = f"{serial_number}-{sensor_type}-{self._channel}"
104 
105  if sensor_type == SENSOR_PTZ_PRESET:
106  self._attr_native_value_attr_native_value = await self._api_api.async_ptz_presets_count
107 
108  elif sensor_type == SENSOR_SDCARD:
109  storage = await self._api_api.async_storage_all
110  try:
111  self._attr_extra_state_attributes_attr_extra_state_attributes["Total"] = (
112  f"{storage['total'][0]:.2f} {storage['total'][1]}"
113  )
114  except ValueError:
115  self._attr_extra_state_attributes_attr_extra_state_attributes["Total"] = (
116  f"{storage['total'][0]} {storage['total'][1]}"
117  )
118  try:
119  self._attr_extra_state_attributes_attr_extra_state_attributes["Used"] = (
120  f"{storage['used'][0]:.2f} {storage['used'][1]}"
121  )
122  except ValueError:
123  self._attr_extra_state_attributes_attr_extra_state_attributes["Used"] = (
124  f"{storage['used'][0]} {storage['used'][1]}"
125  )
126  try:
127  self._attr_native_value_attr_native_value = f"{storage['used_percent']:.2f}"
128  except ValueError:
129  self._attr_native_value_attr_native_value = storage["used_percent"]
130  except AmcrestError as error:
131  log_update_error(_LOGGER, "update", self.namename, "sensor", error)
132 
133  async def async_added_to_hass(self) -> None:
134  """Subscribe to update signal."""
135  self.async_on_removeasync_on_remove(
137  self.hasshass,
138  service_signal(SERVICE_UPDATE, self._signal_name_signal_name),
139  self.async_write_ha_stateasync_write_ha_state,
140  )
141  )
None __init__(self, str name, AmcrestDevice device, SensorEntityDescription description)
Definition: sensor.py:76
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
str|UndefinedType|None name(self)
Definition: entity.py:738
None log_update_error(logging.Logger logger, str action, str|UndefinedType|None name, str entity_type, Exception error, int level=logging.ERROR)
Definition: helpers.py:24
str service_signal(str service, *str args)
Definition: helpers.py:12
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:53
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103