Home Assistant Unofficial Reference 2024.12.1
lock.py
Go to the documentation of this file.
1 """Platform allowing several locks to be grouped into one lock."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 import voluptuous as vol
9 
11  DOMAIN as LOCK_DOMAIN,
12  PLATFORM_SCHEMA as LOCK_PLATFORM_SCHEMA,
13  LockEntity,
14  LockEntityFeature,
15  LockState,
16 )
17 from homeassistant.config_entries import ConfigEntry
18 from homeassistant.const import (
19  ATTR_ENTITY_ID,
20  CONF_ENTITIES,
21  CONF_NAME,
22  CONF_UNIQUE_ID,
23  SERVICE_LOCK,
24  SERVICE_OPEN,
25  SERVICE_UNLOCK,
26  STATE_UNAVAILABLE,
27  STATE_UNKNOWN,
28 )
29 from homeassistant.core import HomeAssistant, callback
30 from homeassistant.helpers import config_validation as cv, entity_registry as er
31 from homeassistant.helpers.entity_platform import AddEntitiesCallback
32 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
33 
34 from .entity import GroupEntity
35 
36 DEFAULT_NAME = "Lock Group"
37 
38 # No limit on parallel updates to enable a group calling another group
39 PARALLEL_UPDATES = 0
40 
41 PLATFORM_SCHEMA = LOCK_PLATFORM_SCHEMA.extend(
42  {
43  vol.Required(CONF_ENTITIES): cv.entities_domain(LOCK_DOMAIN),
44  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
45  vol.Optional(CONF_UNIQUE_ID): cv.string,
46  }
47 )
48 
49 _LOGGER = logging.getLogger(__name__)
50 
51 
53  hass: HomeAssistant,
54  config: ConfigType,
55  async_add_entities: AddEntitiesCallback,
56  discovery_info: DiscoveryInfoType | None = None,
57 ) -> None:
58  """Set up the Lock Group platform."""
60  [
61  LockGroup(
62  config.get(CONF_UNIQUE_ID),
63  config[CONF_NAME],
64  config[CONF_ENTITIES],
65  )
66  ]
67  )
68 
69 
71  hass: HomeAssistant,
72  config_entry: ConfigEntry,
73  async_add_entities: AddEntitiesCallback,
74 ) -> None:
75  """Initialize Lock Group config entry."""
76  registry = er.async_get(hass)
77  entities = er.async_validate_entity_ids(
78  registry, config_entry.options[CONF_ENTITIES]
79  )
81  [
82  LockGroup(
83  config_entry.entry_id,
84  config_entry.title,
85  entities,
86  )
87  ]
88  )
89 
90 
91 @callback
93  hass: HomeAssistant, name: str, validated_config: dict[str, Any]
94 ) -> LockGroup:
95  """Create a preview sensor."""
96  return LockGroup(
97  None,
98  name,
99  validated_config[CONF_ENTITIES],
100  )
101 
102 
104  """Representation of a lock group."""
105 
106  _attr_available = False
107  _attr_should_poll = False
108 
109  def __init__(
110  self,
111  unique_id: str | None,
112  name: str,
113  entity_ids: list[str],
114  ) -> None:
115  """Initialize a lock group."""
116  self._entity_ids_entity_ids = entity_ids
117  self._attr_supported_features_attr_supported_features = LockEntityFeature.OPEN
118 
119  self._attr_name_attr_name = name
120  self._attr_extra_state_attributes_attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids}
121  self._attr_unique_id_attr_unique_id = unique_id
122 
123  async def async_lock(self, **kwargs: Any) -> None:
124  """Forward the lock command to all locks in the group."""
125  data = {ATTR_ENTITY_ID: self._entity_ids_entity_ids}
126  _LOGGER.debug("Forwarded lock command: %s", data)
127 
128  await self.hasshass.services.async_call(
129  LOCK_DOMAIN,
130  SERVICE_LOCK,
131  data,
132  blocking=True,
133  context=self._context_context,
134  )
135 
136  async def async_unlock(self, **kwargs: Any) -> None:
137  """Forward the unlock command to all locks in the group."""
138  data = {ATTR_ENTITY_ID: self._entity_ids_entity_ids}
139  await self.hasshass.services.async_call(
140  LOCK_DOMAIN,
141  SERVICE_UNLOCK,
142  data,
143  blocking=True,
144  context=self._context_context,
145  )
146 
147  async def async_open(self, **kwargs: Any) -> None:
148  """Forward the open command to all locks in the group."""
149  data = {ATTR_ENTITY_ID: self._entity_ids_entity_ids}
150  await self.hasshass.services.async_call(
151  LOCK_DOMAIN,
152  SERVICE_OPEN,
153  data,
154  blocking=True,
155  context=self._context_context,
156  )
157 
158  @callback
159  def async_update_group_state(self) -> None:
160  """Query all members and determine the lock group state."""
161  states = [
162  state.state
163  for entity_id in self._entity_ids_entity_ids
164  if (state := self.hasshass.states.get(entity_id)) is not None
165  ]
166 
167  valid_state = any(
168  state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) for state in states
169  )
170 
171  if not valid_state:
172  # Set as unknown if any member is unknown or unavailable
173  self._attr_is_jammed_attr_is_jammed = None
174  self._attr_is_locking_attr_is_locking = None
175  self._attr_is_opening_attr_is_opening = None
176  self._attr_is_open_attr_is_open = None
177  self._attr_is_unlocking_attr_is_unlocking = None
178  self._attr_is_locked_attr_is_locked = None
179  else:
180  # Set attributes based on member states and let the lock entity sort out the correct state
181  self._attr_is_jammed_attr_is_jammed = LockState.JAMMED in states
182  self._attr_is_locking_attr_is_locking = LockState.LOCKING in states
183  self._attr_is_opening_attr_is_opening = LockState.OPENING in states
184  self._attr_is_open_attr_is_open = LockState.OPEN in states
185  self._attr_is_unlocking_attr_is_unlocking = LockState.UNLOCKING in states
186  self._attr_is_locked_attr_is_locked = all(state == LockState.LOCKED for state in states)
187 
188  self._attr_available_attr_available_attr_available = any(state != STATE_UNAVAILABLE for state in states)
None async_unlock(self, **Any kwargs)
Definition: lock.py:136
None __init__(self, str|None unique_id, str name, list[str] entity_ids)
Definition: lock.py:114
None async_open(self, **Any kwargs)
Definition: lock.py:147
None async_lock(self, **Any kwargs)
Definition: lock.py:123
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: lock.py:74
LockGroup async_create_preview_lock(HomeAssistant hass, str name, dict[str, Any] validated_config)
Definition: lock.py:94
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: lock.py:57