Home Assistant Unofficial Reference 2024.12.1
lock.py
Go to the documentation of this file.
1 """Support for HomeKit Controller locks."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from aiohomekit.model.characteristics import CharacteristicsTypes
8 from aiohomekit.model.services import Service, ServicesTypes
9 
10 from homeassistant.components.lock import LockEntity, LockState
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_UNKNOWN, Platform
13 from homeassistant.core import HomeAssistant, callback
14 from homeassistant.helpers.entity_platform import AddEntitiesCallback
15 
16 from . import KNOWN_DEVICES
17 from .connection import HKDevice
18 from .entity import HomeKitEntity
19 
20 CURRENT_STATE_MAP = {
21  0: LockState.UNLOCKED,
22  1: LockState.LOCKED,
23  2: LockState.JAMMED,
24  3: STATE_UNKNOWN,
25 }
26 
27 TARGET_STATE_MAP = {LockState.UNLOCKED: 0, LockState.LOCKED: 1}
28 
29 REVERSED_TARGET_STATE_MAP = {v: k for k, v in TARGET_STATE_MAP.items()}
30 
31 
33  hass: HomeAssistant,
34  config_entry: ConfigEntry,
35  async_add_entities: AddEntitiesCallback,
36 ) -> None:
37  """Set up Homekit lock."""
38  hkid: str = config_entry.data["AccessoryPairingID"]
39  conn: HKDevice = hass.data[KNOWN_DEVICES][hkid]
40 
41  @callback
42  def async_add_service(service: Service) -> bool:
43  if service.type != ServicesTypes.LOCK_MECHANISM:
44  return False
45  info = {"aid": service.accessory.aid, "iid": service.iid}
46  entity = HomeKitLock(conn, info)
47  conn.async_migrate_unique_id(
48  entity.old_unique_id, entity.unique_id, Platform.LOCK
49  )
50  async_add_entities([entity])
51  return True
52 
53  conn.add_listener(async_add_service)
54 
55 
57  """Representation of a HomeKit Controller Lock."""
58 
59  def get_characteristic_types(self) -> list[str]:
60  """Define the homekit characteristics the entity cares about."""
61  return [
62  CharacteristicsTypes.LOCK_MECHANISM_CURRENT_STATE,
63  CharacteristicsTypes.LOCK_MECHANISM_TARGET_STATE,
64  CharacteristicsTypes.BATTERY_LEVEL,
65  ]
66 
67  @property
68  def is_locked(self) -> bool | None:
69  """Return true if device is locked."""
70  value = self.serviceservice.value(CharacteristicsTypes.LOCK_MECHANISM_CURRENT_STATE)
71  if CURRENT_STATE_MAP[value] == STATE_UNKNOWN:
72  return None
73  return CURRENT_STATE_MAP[value] == LockState.LOCKED
74 
75  @property
76  def is_locking(self) -> bool:
77  """Return true if device is locking."""
78  current_value = self.serviceservice.value(
79  CharacteristicsTypes.LOCK_MECHANISM_CURRENT_STATE
80  )
81  target_value = self.serviceservice.value(
82  CharacteristicsTypes.LOCK_MECHANISM_TARGET_STATE
83  )
84  return (
85  CURRENT_STATE_MAP[current_value] == LockState.UNLOCKED
86  and REVERSED_TARGET_STATE_MAP.get(target_value) == LockState.LOCKED
87  )
88 
89  @property
90  def is_unlocking(self) -> bool:
91  """Return true if device is unlocking."""
92  current_value = self.serviceservice.value(
93  CharacteristicsTypes.LOCK_MECHANISM_CURRENT_STATE
94  )
95  target_value = self.serviceservice.value(
96  CharacteristicsTypes.LOCK_MECHANISM_TARGET_STATE
97  )
98  return (
99  CURRENT_STATE_MAP[current_value] == LockState.LOCKED
100  and REVERSED_TARGET_STATE_MAP.get(target_value) == LockState.UNLOCKED
101  )
102 
103  @property
104  def is_jammed(self) -> bool:
105  """Return true if device is jammed."""
106  value = self.serviceservice.value(CharacteristicsTypes.LOCK_MECHANISM_CURRENT_STATE)
107  return CURRENT_STATE_MAP[value] == LockState.JAMMED
108 
109  async def async_lock(self, **kwargs: Any) -> None:
110  """Lock the device."""
111  await self._set_lock_state_set_lock_state(LockState.LOCKED)
112 
113  async def async_unlock(self, **kwargs: Any) -> None:
114  """Unlock the device."""
115  await self._set_lock_state_set_lock_state(LockState.UNLOCKED)
116 
117  async def _set_lock_state(self, state: LockState) -> None:
118  """Send state command."""
119  await self.async_put_characteristicsasync_put_characteristics(
120  {CharacteristicsTypes.LOCK_MECHANISM_TARGET_STATE: TARGET_STATE_MAP[state]}
121  )
122  # Some locks need to be polled to update the current state
123  # after a target state change.
124  # https://github.com/home-assistant/core/issues/81887
125  await self._accessory_accessory.async_request_update()
126 
127  @property
128  def extra_state_attributes(self) -> dict[str, Any]:
129  """Return the optional state attributes."""
130  attributes = {}
131 
132  battery_level = self.serviceservice.value(CharacteristicsTypes.BATTERY_LEVEL)
133  if battery_level:
134  attributes[ATTR_BATTERY_LEVEL] = battery_level
135 
136  return attributes
None async_put_characteristics(self, dict[str, Any] characteristics)
Definition: entity.py:125
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: lock.py:36