Home Assistant Unofficial Reference 2024.12.1
switch.py
Go to the documentation of this file.
1 """Support for Homekit switches."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 from typing import Any
7 
8 from aiohomekit.model.characteristics import (
9  Characteristic,
10  CharacteristicsTypes,
11  InUseValues,
12  IsConfiguredValues,
13 )
14 from aiohomekit.model.services import Service, ServicesTypes
15 
16 from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
17 from homeassistant.config_entries import ConfigEntry
18 from homeassistant.const import EntityCategory, Platform
19 from homeassistant.core import HomeAssistant, callback
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21 from homeassistant.helpers.typing import ConfigType
22 
23 from . import KNOWN_DEVICES
24 from .connection import HKDevice
25 from .entity import CharacteristicEntity, HomeKitEntity
26 
27 OUTLET_IN_USE = "outlet_in_use"
28 
29 ATTR_IN_USE = "in_use"
30 ATTR_IS_CONFIGURED = "is_configured"
31 ATTR_REMAINING_DURATION = "remaining_duration"
32 
33 
34 @dataclass(frozen=True)
36  """Describes Homekit button."""
37 
38  true_value: bool = True
39  false_value: bool = False
40 
41 
42 SWITCH_ENTITIES: dict[str, DeclarativeSwitchEntityDescription] = {
43  CharacteristicsTypes.VENDOR_AQARA_PAIRING_MODE: DeclarativeSwitchEntityDescription(
44  key=CharacteristicsTypes.VENDOR_AQARA_PAIRING_MODE,
45  name="Pairing Mode",
46  translation_key="pairing_mode",
47  entity_category=EntityCategory.CONFIG,
48  ),
49  CharacteristicsTypes.VENDOR_AQARA_E1_PAIRING_MODE: DeclarativeSwitchEntityDescription(
50  key=CharacteristicsTypes.VENDOR_AQARA_E1_PAIRING_MODE,
51  name="Pairing Mode",
52  translation_key="pairing_mode",
53  entity_category=EntityCategory.CONFIG,
54  ),
55  CharacteristicsTypes.LOCK_PHYSICAL_CONTROLS: DeclarativeSwitchEntityDescription(
56  key=CharacteristicsTypes.LOCK_PHYSICAL_CONTROLS,
57  name="Lock Physical Controls",
58  translation_key="lock_physical_controls",
59  entity_category=EntityCategory.CONFIG,
60  ),
61  CharacteristicsTypes.MUTE: DeclarativeSwitchEntityDescription(
62  key=CharacteristicsTypes.MUTE,
63  name="Mute",
64  translation_key="mute",
65  entity_category=EntityCategory.CONFIG,
66  ),
67  CharacteristicsTypes.VENDOR_AIRVERSA_SLEEP_MODE: DeclarativeSwitchEntityDescription(
68  key=CharacteristicsTypes.VENDOR_AIRVERSA_SLEEP_MODE,
69  name="Sleep Mode",
70  translation_key="sleep_mode",
71  entity_category=EntityCategory.CONFIG,
72  ),
73 }
74 
75 
77  """Representation of a Homekit switch."""
78 
79  def get_characteristic_types(self) -> list[str]:
80  """Define the homekit characteristics the entity cares about."""
81  return [CharacteristicsTypes.ON, CharacteristicsTypes.OUTLET_IN_USE]
82 
83  @property
84  def is_on(self) -> bool:
85  """Return true if device is on."""
86  return self.serviceservice.value(CharacteristicsTypes.ON)
87 
88  async def async_turn_on(self, **kwargs: Any) -> None:
89  """Turn the specified switch on."""
90  await self.async_put_characteristicsasync_put_characteristics({CharacteristicsTypes.ON: True})
91 
92  async def async_turn_off(self, **kwargs: Any) -> None:
93  """Turn the specified switch off."""
94  await self.async_put_characteristicsasync_put_characteristics({CharacteristicsTypes.ON: False})
95 
96  @property
97  def extra_state_attributes(self) -> dict[str, Any] | None:
98  """Return the optional state attributes."""
99  outlet_in_use = self.serviceservice.value(CharacteristicsTypes.OUTLET_IN_USE)
100  if outlet_in_use is not None:
101  return {OUTLET_IN_USE: outlet_in_use}
102  return None
103 
104 
106  """Representation of a Homekit faucet."""
107 
108  def get_characteristic_types(self) -> list[str]:
109  """Define the homekit characteristics the entity cares about."""
110  return [CharacteristicsTypes.ACTIVE]
111 
112  @property
113  def is_on(self) -> bool:
114  """Return true if device is on."""
115  return self.serviceservice.value(CharacteristicsTypes.ACTIVE)
116 
117  async def async_turn_on(self, **kwargs: Any) -> None:
118  """Turn the specified faucet on."""
119  await self.async_put_characteristicsasync_put_characteristics({CharacteristicsTypes.ACTIVE: True})
120 
121  async def async_turn_off(self, **kwargs: Any) -> None:
122  """Turn the specified faucet off."""
123  await self.async_put_characteristicsasync_put_characteristics({CharacteristicsTypes.ACTIVE: False})
124 
125 
127  """Represents a valve in an irrigation system."""
128 
129  _attr_translation_key = "valve"
130 
131  def get_characteristic_types(self) -> list[str]:
132  """Define the homekit characteristics the entity cares about."""
133  return [
134  CharacteristicsTypes.ACTIVE,
135  CharacteristicsTypes.IN_USE,
136  CharacteristicsTypes.IS_CONFIGURED,
137  CharacteristicsTypes.REMAINING_DURATION,
138  ]
139 
140  async def async_turn_on(self, **kwargs: Any) -> None:
141  """Turn the specified valve on."""
142  await self.async_put_characteristicsasync_put_characteristics({CharacteristicsTypes.ACTIVE: True})
143 
144  async def async_turn_off(self, **kwargs: Any) -> None:
145  """Turn the specified valve off."""
146  await self.async_put_characteristicsasync_put_characteristics({CharacteristicsTypes.ACTIVE: False})
147 
148  @property
149  def is_on(self) -> bool:
150  """Return true if device is on."""
151  return self.serviceservice.value(CharacteristicsTypes.ACTIVE)
152 
153  @property
154  def extra_state_attributes(self) -> dict[str, Any]:
155  """Return the optional state attributes."""
156  attrs = {}
157 
158  in_use = self.serviceservice.value(CharacteristicsTypes.IN_USE)
159  if in_use is not None:
160  attrs[ATTR_IN_USE] = in_use == InUseValues.IN_USE
161 
162  is_configured = self.serviceservice.value(CharacteristicsTypes.IS_CONFIGURED)
163  if is_configured is not None:
164  attrs[ATTR_IS_CONFIGURED] = is_configured == IsConfiguredValues.CONFIGURED
165 
166  remaining = self.serviceservice.value(CharacteristicsTypes.REMAINING_DURATION)
167  if remaining is not None:
168  attrs[ATTR_REMAINING_DURATION] = remaining
169 
170  return attrs
171 
172 
174  """Representation of a Homekit switch backed by a single characteristic."""
175 
176  def __init__(
177  self,
178  conn: HKDevice,
179  info: ConfigType,
180  char: Characteristic,
181  description: DeclarativeSwitchEntityDescription,
182  ) -> None:
183  """Initialise a HomeKit switch."""
184  self.entity_description: DeclarativeSwitchEntityDescription = description
185  super().__init__(conn, info, char)
186 
187  @property
188  def name(self) -> str:
189  """Return the name of the device if any."""
190  if name := self.accessoryaccessory.name:
191  return f"{name} {self.entity_description.name}"
192  return f"{self.entity_description.name}"
193 
194  def get_characteristic_types(self) -> list[str]:
195  """Define the homekit characteristics the entity cares about."""
196  return [self._char_char.type]
197 
198  @property
199  def is_on(self) -> bool:
200  """Return true if device is on."""
201  return self._char_char.value == self.entity_description.true_value
202 
203  async def async_turn_on(self, **kwargs: Any) -> None:
204  """Turn the specified switch on."""
205  await self.async_put_characteristicsasync_put_characteristics(
206  {self._char_char.type: self.entity_description.true_value}
207  )
208 
209  async def async_turn_off(self, **kwargs: Any) -> None:
210  """Turn the specified switch off."""
211  await self.async_put_characteristicsasync_put_characteristics(
212  {self._char_char.type: self.entity_description.false_value}
213  )
214 
215 
216 ENTITY_TYPES: dict[str, type[HomeKitSwitch | HomeKitFaucet | HomeKitValve]] = {
217  ServicesTypes.SWITCH: HomeKitSwitch,
218  ServicesTypes.OUTLET: HomeKitSwitch,
219  ServicesTypes.FAUCET: HomeKitFaucet,
220  ServicesTypes.VALVE: HomeKitValve,
221 }
222 
223 
225  hass: HomeAssistant,
226  config_entry: ConfigEntry,
227  async_add_entities: AddEntitiesCallback,
228 ) -> None:
229  """Set up Homekit switches."""
230  hkid: str = config_entry.data["AccessoryPairingID"]
231  conn: HKDevice = hass.data[KNOWN_DEVICES][hkid]
232 
233  @callback
234  def async_add_service(service: Service) -> bool:
235  if not (entity_class := ENTITY_TYPES.get(service.type)):
236  return False
237  info = {"aid": service.accessory.aid, "iid": service.iid}
238  entity: HomeKitSwitch | HomeKitFaucet | HomeKitValve = entity_class(conn, info)
239  conn.async_migrate_unique_id(
240  entity.old_unique_id, entity.unique_id, Platform.SWITCH
241  )
242  async_add_entities([entity])
243  return True
244 
245  conn.add_listener(async_add_service)
246 
247  @callback
248  def async_add_characteristic(char: Characteristic) -> bool:
249  if not (description := SWITCH_ENTITIES.get(char.type)):
250  return False
251 
252  info = {"aid": char.service.accessory.aid, "iid": char.service.iid}
253  entity = DeclarativeCharacteristicSwitch(conn, info, char, description)
254  conn.async_migrate_unique_id(
255  entity.old_unique_id, entity.unique_id, Platform.SWITCH
256  )
257  async_add_entities([entity])
258  return True
259 
260  conn.add_char_factory(async_add_characteristic)
None async_put_characteristics(self, dict[str, Any] characteristics)
Definition: entity.py:125
None __init__(self, HKDevice conn, ConfigType info, Characteristic char, DeclarativeSwitchEntityDescription description)
Definition: switch.py:182
bool async_add_characteristic(Characteristic char)
Definition: number.py:78
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: switch.py:228