Home Assistant Unofficial Reference 2024.12.1
lock.py
Go to the documentation of this file.
1 """Support for Verisure locks."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from typing import Any
7 
8 from verisure import Error as VerisureError
9 
10 from homeassistant.components.lock import LockEntity, LockState
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.const import ATTR_CODE
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.device_registry import DeviceInfo
16  AddEntitiesCallback,
17  async_get_current_platform,
18 )
19 from homeassistant.helpers.update_coordinator import CoordinatorEntity
20 
21 from .const import (
22  CONF_GIID,
23  CONF_LOCK_CODE_DIGITS,
24  DEFAULT_LOCK_CODE_DIGITS,
25  DOMAIN,
26  LOGGER,
27  SERVICE_DISABLE_AUTOLOCK,
28  SERVICE_ENABLE_AUTOLOCK,
29 )
30 from .coordinator import VerisureDataUpdateCoordinator
31 
32 
34  hass: HomeAssistant,
35  entry: ConfigEntry,
36  async_add_entities: AddEntitiesCallback,
37 ) -> None:
38  """Set up Verisure alarm control panel from a config entry."""
39  coordinator: VerisureDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
40 
41  platform = async_get_current_platform()
42  platform.async_register_entity_service(
43  SERVICE_DISABLE_AUTOLOCK,
44  None,
45  VerisureDoorlock.disable_autolock.__name__,
46  )
47  platform.async_register_entity_service(
48  SERVICE_ENABLE_AUTOLOCK,
49  None,
50  VerisureDoorlock.enable_autolock.__name__,
51  )
52 
54  VerisureDoorlock(coordinator, serial_number)
55  for serial_number in coordinator.data["locks"]
56  )
57 
58 
59 class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEntity):
60  """Representation of a Verisure doorlock."""
61 
62  _attr_has_entity_name = True
63  _attr_name = None
64 
65  def __init__(
66  self, coordinator: VerisureDataUpdateCoordinator, serial_number: str
67  ) -> None:
68  """Initialize the Verisure lock."""
69  super().__init__(coordinator)
70  self._attr_unique_id_attr_unique_id = serial_number
71 
72  self.serial_numberserial_number = serial_number
73  self._state_state: str | None = None
74 
75  @property
76  def device_info(self) -> DeviceInfo:
77  """Return device information about this entity."""
78  area = self.coordinator.data["locks"][self.serial_numberserial_number]["device"]["area"]
79  return DeviceInfo(
80  name=area,
81  manufacturer="Verisure",
82  model="Lockguard Smartlock",
83  identifiers={(DOMAIN, self.serial_numberserial_number)},
84  via_device=(DOMAIN, self.coordinator.entry.data[CONF_GIID]),
85  configuration_url="https://mypages.verisure.com",
86  )
87 
88  @property
89  def available(self) -> bool:
90  """Return True if entity is available."""
91  return (
92  super().available and self.serial_numberserial_number in self.coordinator.data["locks"]
93  )
94 
95  @property
96  def changed_by(self) -> str | None:
97  """Last change triggered by."""
98  return (
99  self.coordinator.data["locks"][self.serial_numberserial_number]
100  .get("user", {})
101  .get("name")
102  )
103 
104  @property
105  def changed_method(self) -> str:
106  """Last change method."""
107  return self.coordinator.data["locks"][self.serial_numberserial_number]["lockMethod"]
108 
109  @property
110  def code_format(self) -> str:
111  """Return the configured code format."""
112  digits = self.coordinator.entry.options.get(
113  CONF_LOCK_CODE_DIGITS, DEFAULT_LOCK_CODE_DIGITS
114  )
115  return f"^\\d{{{digits}}}$"
116 
117  @property
118  def is_locked(self) -> bool:
119  """Return true if lock is locked."""
120  return (
121  self.coordinator.data["locks"][self.serial_numberserial_number]["lockStatus"] == "LOCKED"
122  )
123 
124  @property
125  def extra_state_attributes(self) -> dict[str, str]:
126  """Return the state attributes."""
127  return {"method": self.changed_methodchanged_method}
128 
129  async def async_unlock(self, **kwargs: Any) -> None:
130  """Send unlock command."""
131  code = kwargs.get(ATTR_CODE)
132  if code:
133  await self.async_set_lock_stateasync_set_lock_state(code, LockState.UNLOCKED)
134 
135  async def async_lock(self, **kwargs: Any) -> None:
136  """Send lock command."""
137  code = kwargs.get(ATTR_CODE)
138  if code:
139  await self.async_set_lock_stateasync_set_lock_state(code, LockState.LOCKED)
140 
141  async def async_set_lock_state(self, code: str, state: LockState) -> None:
142  """Send set lock state command."""
143  command = (
144  self.coordinator.verisure.door_lock(self.serial_numberserial_number, code)
145  if state == LockState.LOCKED
146  else self.coordinator.verisure.door_unlock(self.serial_numberserial_number, code)
147  )
148  lock_request = await self.hasshasshass.async_add_executor_job(
149  self.coordinator.verisure.request,
150  command,
151  )
152  LOGGER.debug("Verisure doorlock %s", state)
153  transaction_id = lock_request.get("data", {}).get(command["operationName"])
154  target_state = "LOCKED" if state == LockState.LOCKED else "UNLOCKED"
155  lock_status = None
156  attempts = 0
157  while lock_status != "OK":
158  if attempts == 30:
159  break
160  if attempts > 1:
161  await asyncio.sleep(0.5)
162  attempts += 1
163  poll_data = await self.hasshasshass.async_add_executor_job(
164  self.coordinator.verisure.request,
165  self.coordinator.verisure.poll_lock_state(
166  transaction_id, self.serial_numberserial_number, target_state
167  ),
168  )
169  lock_status = (
170  poll_data.get("data", {})
171  .get("installation", {})
172  .get("doorLockStateChangePollResult", {})
173  .get("result")
174  )
175  if lock_status == "OK":
176  self._state_state = state
177 
178  def disable_autolock(self) -> None:
179  """Disable autolock on a doorlock."""
180  try:
181  command = self.coordinator.verisure.set_autolock_enabled(
182  self.serial_numberserial_number, auto_lock_enabled=False
183  )
184  self.coordinator.verisure.request(command)
185  LOGGER.debug("Disabling autolock on %s", self.serial_numberserial_number)
186  except VerisureError as ex:
187  LOGGER.error("Could not disable autolock, %s", ex)
188 
189  def enable_autolock(self) -> None:
190  """Enable autolock on a doorlock."""
191  try:
192  command = self.coordinator.verisure.set_autolock_enabled(
193  self.serial_numberserial_number, auto_lock_enabled=True
194  )
195  self.coordinator.verisure.request(command)
196  LOGGER.debug("Enabling autolock on %s", self.serial_numberserial_number)
197  except VerisureError as ex:
198  LOGGER.error("Could not enable autolock, %s", ex)
None __init__(self, VerisureDataUpdateCoordinator coordinator, str serial_number)
Definition: lock.py:67
None async_set_lock_state(self, str code, LockState state)
Definition: lock.py:141
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: lock.py:37