1 """Component to interface with locks that can be controlled remotely."""
3 from __future__
import annotations
5 from datetime
import timedelta
6 from enum
import IntFlag
10 from typing
import TYPE_CHECKING, Any, final
12 from propcache
import cached_property
13 import voluptuous
as vol
17 _DEPRECATED_STATE_JAMMED,
18 _DEPRECATED_STATE_LOCKED,
19 _DEPRECATED_STATE_LOCKING,
20 _DEPRECATED_STATE_UNLOCKED,
21 _DEPRECATED_STATE_UNLOCKING,
34 DeprecatedConstantEnum,
35 all_with_deprecated_constants,
36 check_if_deprecated_constant,
37 dir_with_deprecated_constants,
44 from .const
import DOMAIN, LockState
46 _LOGGER = logging.getLogger(__name__)
48 DATA_COMPONENT: HassKey[EntityComponent[LockEntity]] =
HassKey(DOMAIN)
49 ENTITY_ID_FORMAT = DOMAIN +
".{}"
50 PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
51 PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
54 ATTR_CHANGED_BY =
"changed_by"
55 CONF_DEFAULT_CODE =
"default_code"
59 LOCK_SERVICE_SCHEMA = cv.make_entity_service_schema(
60 {vol.Optional(ATTR_CODE): cv.string}
65 """Supported features of the lock entity."""
74 PROP_TO_ATTR = {
"changed_by": ATTR_CHANGED_BY,
"code_format": ATTR_CODE_FORMAT}
79 async
def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
80 """Track states and offer events for locks."""
81 component = hass.data[DATA_COMPONENT] = EntityComponent[LockEntity](
82 _LOGGER, DOMAIN, hass, SCAN_INTERVAL
85 await component.async_setup(config)
87 component.async_register_entity_service(
88 SERVICE_UNLOCK, LOCK_SERVICE_SCHEMA,
"async_handle_unlock_service"
90 component.async_register_entity_service(
91 SERVICE_LOCK, LOCK_SERVICE_SCHEMA,
"async_handle_lock_service"
93 component.async_register_entity_service(
96 "async_handle_open_service",
97 [LockEntityFeature.OPEN],
104 """Set up a config entry."""
109 """Unload a config entry."""
114 """A class that describes lock entities."""
117 CACHED_PROPERTIES_WITH_ATTR_ = {
126 "supported_features",
131 """Base class for lock entities."""
133 entity_description: LockEntityDescription
134 _attr_changed_by: str |
None =
None
135 _attr_code_format: str |
None =
None
136 _attr_is_locked: bool |
None =
None
137 _attr_is_locking: bool |
None =
None
138 _attr_is_open: bool |
None =
None
139 _attr_is_opening: bool |
None =
None
140 _attr_is_unlocking: bool |
None =
None
141 _attr_is_jammed: bool |
None =
None
142 _attr_state:
None =
None
144 _lock_option_default_code: str =
""
145 __code_format_cmp: re.Pattern[str] |
None =
None
150 """Add default lock code."""
151 code: str = data.pop(ATTR_CODE,
"")
157 raise ServiceValidationError(
158 translation_domain=DOMAIN,
159 translation_key=
"add_default_code",
160 translation_placeholders={
166 data[ATTR_CODE] = code
171 """Last change triggered by."""
172 return self._attr_changed_by
176 """Regex for code format or None if no code is required."""
177 return self._attr_code_format
182 """Return a compiled code_format."""
195 """Return true if the lock is locked."""
196 return self._attr_is_locked
200 """Return true if the lock is locking."""
201 return self._attr_is_locking
205 """Return true if the lock is unlocking."""
206 return self._attr_is_unlocking
210 """Return true if the lock is open."""
211 return self._attr_is_open
215 """Return true if the lock is opening."""
216 return self._attr_is_opening
220 """Return true if the lock is jammed (incomplete locking)."""
221 return self._attr_is_jammed
225 """Add default code and lock."""
228 def lock(self, **kwargs: Any) ->
None:
230 raise NotImplementedError
234 await self.
hasshass.async_add_executor_job(ft.partial(self.
locklock, **kwargs))
238 """Add default code and unlock."""
242 """Unlock the lock."""
243 raise NotImplementedError
246 """Unlock the lock."""
247 await self.
hasshass.async_add_executor_job(ft.partial(self.
unlockunlock, **kwargs))
251 """Add default code and open."""
254 def open(self, **kwargs: Any) ->
None:
255 """Open the door latch."""
256 raise NotImplementedError
259 """Open the door latch."""
260 await self.
hasshass.async_add_executor_job(ft.partial(self.
openopen, **kwargs))
265 """Return the state attributes."""
267 for prop, attr
in PROP_TO_ATTR.items():
268 if (value := getattr(self, prop))
is not None:
269 state_attr[attr] = value
275 """Return the state."""
277 return LockState.JAMMED
279 return LockState.OPENING
281 return LockState.LOCKING
283 return LockState.OPEN
285 return LockState.UNLOCKING
286 if (locked := self.
is_lockedis_locked)
is None:
288 return LockState.LOCKED
if locked
else LockState.UNLOCKED
292 """Return the list of supported features."""
293 features = self._attr_supported_features
294 if type(features)
is int:
301 """Call when the sensor entity is added to hass."""
309 """Run when the entity registry entry has been updated."""
314 """Read entity options from entity registry.
316 Called when the entity registry entry has been updated and before the lock is
317 added to the state machine.
320 if (lock_options := self.
registry_entryregistry_entry.options.get(DOMAIN))
and (
321 custom_default_lock_code := lock_options.get(CONF_DEFAULT_CODE)
324 custom_default_lock_code
333 __getattr__ = ft.partial(check_if_deprecated_constant, module_globals=globals())
334 __dir__ = ft.partial(
335 dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
LockEntityFeature supported_features(self)
None async_internal_added_to_hass(self)
bool|None is_locked(self)
dict[str, StateType] state_attributes(self)
bool|None is_jammed(self)
None async_open(self, **Any kwargs)
None async_registry_entry_updated(self)
bool|None is_opening(self)
str|None changed_by(self)
None async_unlock(self, **Any kwargs)
None unlock(self, **Any kwargs)
None async_lock(self, **Any kwargs)
str|None code_format(self)
None _async_read_entity_options(self)
bool|None is_unlocking(self)
None open(self, **Any kwargs)
None async_handle_unlock_service(self, **Any kwargs)
None lock(self, **Any kwargs)
None async_handle_open_service(self, **Any kwargs)
bool|None is_locking(self)
dict[Any, Any] add_default_code(self, dict[Any, Any] data)
re.Pattern[str]|None code_format_cmp(self)
_lock_option_default_code
None async_handle_lock_service(self, **Any kwargs)
None _report_deprecated_supported_features_values(self, IntFlag replacement)
list[_T] match(self, BluetoothServiceInfoBleak service_info)
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
bool async_setup(HomeAssistant hass, ConfigType config)
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
list[str] all_with_deprecated_constants(dict[str, Any] module_globals)