Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for August sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import Any, cast
8 
9 from yalexs.activity import ActivityType, LockOperationActivity
10 from yalexs.doorbell import Doorbell
11 from yalexs.keypad import KeypadDetail
12 from yalexs.lock import LockDetail
13 
15  RestoreSensor,
16  SensorDeviceClass,
17  SensorEntity,
18  SensorEntityDescription,
19  SensorStateClass,
20 )
21 from homeassistant.const import (
22  ATTR_ENTITY_PICTURE,
23  PERCENTAGE,
24  STATE_UNAVAILABLE,
25  EntityCategory,
26 )
27 from homeassistant.core import HomeAssistant, callback
28 from homeassistant.helpers.entity_platform import AddEntitiesCallback
29 
30 from . import AugustConfigEntry
31 from .const import (
32  ATTR_OPERATION_AUTORELOCK,
33  ATTR_OPERATION_KEYPAD,
34  ATTR_OPERATION_MANUAL,
35  ATTR_OPERATION_METHOD,
36  ATTR_OPERATION_REMOTE,
37  ATTR_OPERATION_TAG,
38  OPERATION_METHOD_AUTORELOCK,
39  OPERATION_METHOD_KEYPAD,
40  OPERATION_METHOD_MANUAL,
41  OPERATION_METHOD_MOBILE_DEVICE,
42  OPERATION_METHOD_REMOTE,
43  OPERATION_METHOD_TAG,
44 )
45 from .entity import AugustDescriptionEntity, AugustEntity
46 
47 
48 def _retrieve_device_battery_state(detail: LockDetail) -> int:
49  """Get the latest state of the sensor."""
50  return detail.battery_level
51 
52 
53 def _retrieve_linked_keypad_battery_state(detail: KeypadDetail) -> int | None:
54  """Get the latest state of the sensor."""
55  return detail.battery_percentage
56 
57 
58 @dataclass(frozen=True, kw_only=True)
59 class AugustSensorEntityDescription[T: LockDetail | KeypadDetail](
60  SensorEntityDescription
61 ):
62  """Mixin for required keys."""
63 
64  value_fn: Callable[[T], int | None]
65 
66 
67 SENSOR_TYPE_DEVICE_BATTERY = AugustSensorEntityDescription[LockDetail](
68  key="device_battery",
69  entity_category=EntityCategory.DIAGNOSTIC,
70  state_class=SensorStateClass.MEASUREMENT,
71  value_fn=_retrieve_device_battery_state,
72 )
73 
74 SENSOR_TYPE_KEYPAD_BATTERY = AugustSensorEntityDescription[KeypadDetail](
75  key="linked_keypad_battery",
76  entity_category=EntityCategory.DIAGNOSTIC,
77  state_class=SensorStateClass.MEASUREMENT,
78  value_fn=_retrieve_linked_keypad_battery_state,
79 )
80 
81 
83  hass: HomeAssistant,
84  config_entry: AugustConfigEntry,
85  async_add_entities: AddEntitiesCallback,
86 ) -> None:
87  """Set up the August sensors."""
88  data = config_entry.runtime_data
89  entities: list[SensorEntity] = []
90 
91  for device in data.locks:
92  detail = data.get_device_detail(device.device_id)
93  entities.append(AugustOperatorSensor(data, device, "lock_operator"))
94  if SENSOR_TYPE_DEVICE_BATTERY.value_fn(detail):
95  entities.append(
96  AugustBatterySensor[LockDetail](
97  data, device, SENSOR_TYPE_DEVICE_BATTERY
98  )
99  )
100  if keypad := detail.keypad:
101  entities.append(
102  AugustBatterySensor[KeypadDetail](
103  data, keypad, SENSOR_TYPE_KEYPAD_BATTERY
104  )
105  )
106 
107  entities.extend(
108  AugustBatterySensor[Doorbell](data, device, SENSOR_TYPE_DEVICE_BATTERY)
109  for device in data.doorbells
110  if SENSOR_TYPE_DEVICE_BATTERY.value_fn(data.get_device_detail(device.device_id))
111  )
112 
113  async_add_entities(entities)
114 
115 
117  """Representation of an August lock operation sensor."""
118 
119  _attr_translation_key = "operator"
120  _operated_remote: bool | None = None
121  _operated_keypad: bool | None = None
122  _operated_manual: bool | None = None
123  _operated_tag: bool | None = None
124  _operated_autorelock: bool | None = None
125 
126  @callback
127  def _update_from_data(self) -> None:
128  """Get the latest state of the sensor and update activity."""
129  self._attr_available_attr_available = True
130  if lock_activity := self._get_latest_get_latest({ActivityType.LOCK_OPERATION}):
131  lock_activity = cast(LockOperationActivity, lock_activity)
132  self._attr_native_value_attr_native_value = lock_activity.operated_by
133  self._operated_remote_operated_remote = lock_activity.operated_remote
134  self._operated_keypad_operated_keypad = lock_activity.operated_keypad
135  self._operated_manual_operated_manual = lock_activity.operated_manual
136  self._operated_tag_operated_tag = lock_activity.operated_tag
137  self._operated_autorelock_operated_autorelock = lock_activity.operated_autorelock
138  self._attr_entity_picture_attr_entity_picture = lock_activity.operator_thumbnail_url
139 
140  @property
141  def extra_state_attributes(self) -> dict[str, Any]:
142  """Return the device specific state attributes."""
143  attributes: dict[str, Any] = {}
144 
145  if self._operated_remote_operated_remote is not None:
146  attributes[ATTR_OPERATION_REMOTE] = self._operated_remote_operated_remote
147  if self._operated_keypad_operated_keypad is not None:
148  attributes[ATTR_OPERATION_KEYPAD] = self._operated_keypad_operated_keypad
149  if self._operated_manual_operated_manual is not None:
150  attributes[ATTR_OPERATION_MANUAL] = self._operated_manual_operated_manual
151  if self._operated_tag_operated_tag is not None:
152  attributes[ATTR_OPERATION_TAG] = self._operated_tag_operated_tag
153  if self._operated_autorelock_operated_autorelock is not None:
154  attributes[ATTR_OPERATION_AUTORELOCK] = self._operated_autorelock_operated_autorelock
155 
156  if self._operated_remote_operated_remote:
157  attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_REMOTE
158  elif self._operated_keypad_operated_keypad:
159  attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_KEYPAD
160  elif self._operated_manual_operated_manual:
161  attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_MANUAL
162  elif self._operated_tag_operated_tag:
163  attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_TAG
164  elif self._operated_autorelock_operated_autorelock:
165  attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_AUTORELOCK
166  else:
167  attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_MOBILE_DEVICE
168 
169  return attributes
170 
171  async def async_added_to_hass(self) -> None:
172  """Restore ATTR_CHANGED_BY on startup since it is likely no longer in the activity log."""
173  await super().async_added_to_hass()
174 
175  last_state = await self.async_get_last_state()
176  last_sensor_state = await self.async_get_last_sensor_dataasync_get_last_sensor_data()
177  if (
178  not last_state
179  or not last_sensor_state
180  or last_state.state == STATE_UNAVAILABLE
181  ):
182  return
183 
184  self._attr_native_value_attr_native_value = last_sensor_state.native_value
185  last_attrs = last_state.attributes
186  if ATTR_ENTITY_PICTURE in last_attrs:
187  self._attr_entity_picture_attr_entity_picture = last_attrs[ATTR_ENTITY_PICTURE]
188  if ATTR_OPERATION_REMOTE in last_attrs:
189  self._operated_remote_operated_remote = last_attrs[ATTR_OPERATION_REMOTE]
190  if ATTR_OPERATION_KEYPAD in last_attrs:
191  self._operated_keypad_operated_keypad = last_attrs[ATTR_OPERATION_KEYPAD]
192  if ATTR_OPERATION_MANUAL in last_attrs:
193  self._operated_manual_operated_manual = last_attrs[ATTR_OPERATION_MANUAL]
194  if ATTR_OPERATION_TAG in last_attrs:
195  self._operated_tag_operated_tag = last_attrs[ATTR_OPERATION_TAG]
196  if ATTR_OPERATION_AUTORELOCK in last_attrs:
197  self._operated_autorelock_operated_autorelock = last_attrs[ATTR_OPERATION_AUTORELOCK]
198 
199 
200 class AugustBatterySensor[T: LockDetail | KeypadDetail](
201  AugustDescriptionEntity, SensorEntity
202 ):
203  """Representation of an August sensor."""
204 
205  entity_description: AugustSensorEntityDescription[T]
206  _attr_device_class = SensorDeviceClass.BATTERY
207  _attr_native_unit_of_measurement = PERCENTAGE
208 
209  @callback
210  def _update_from_data(self) -> None:
211  """Get the latest state of the sensor."""
212  self._attr_native_value = self.entity_description.value_fn(self._detail)
213  self._attr_available = self._attr_native_value is not None
Activity|None _get_latest(self, set[ActivityType] activity_types)
Definition: entity.py:62
SensorExtraStoredData|None async_get_last_sensor_data(self)
Definition: __init__.py:934
int _retrieve_device_battery_state(LockDetail detail)
Definition: sensor.py:48
int|None _retrieve_linked_keypad_battery_state(KeypadDetail detail)
Definition: sensor.py:53
None async_setup_entry(HomeAssistant hass, AugustConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:86