Home Assistant Unofficial Reference 2024.12.1
switch.py
Go to the documentation of this file.
1 """Provides a switch for Home Connect."""
2 
3 import contextlib
4 import logging
5 from typing import Any
6 
7 from homeconnect.api import HomeConnectError
8 
9 from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
10 from homeassistant.core import HomeAssistant
11 from homeassistant.exceptions import ServiceValidationError
12 from homeassistant.helpers.entity_platform import AddEntitiesCallback
13 
14 from . import HomeConnectConfigEntry, get_dict_from_home_connect_error
15 from .const import (
16  APPLIANCES_WITH_PROGRAMS,
17  ATTR_ALLOWED_VALUES,
18  ATTR_CONSTRAINTS,
19  ATTR_VALUE,
20  BSH_ACTIVE_PROGRAM,
21  BSH_CHILD_LOCK_STATE,
22  BSH_OPERATION_STATE,
23  BSH_POWER_OFF,
24  BSH_POWER_ON,
25  BSH_POWER_STANDBY,
26  BSH_POWER_STATE,
27  DOMAIN,
28  REFRIGERATION_DISPENSER,
29  REFRIGERATION_SUPERMODEFREEZER,
30  REFRIGERATION_SUPERMODEREFRIGERATOR,
31  SVE_TRANSLATION_PLACEHOLDER_APPLIANCE_NAME,
32  SVE_TRANSLATION_PLACEHOLDER_ENTITY_ID,
33  SVE_TRANSLATION_PLACEHOLDER_SETTING_KEY,
34  SVE_TRANSLATION_PLACEHOLDER_VALUE,
35 )
36 from .entity import HomeConnectDevice, HomeConnectEntity
37 
38 _LOGGER = logging.getLogger(__name__)
39 
40 
41 SWITCHES = (
43  key=BSH_CHILD_LOCK_STATE,
44  translation_key="child_lock",
45  ),
47  key="ConsumerProducts.CoffeeMaker.Setting.CupWarmer",
48  translation_key="cup_warmer",
49  ),
51  key=REFRIGERATION_SUPERMODEFREEZER,
52  translation_key="freezer_super_mode",
53  ),
55  key=REFRIGERATION_SUPERMODEREFRIGERATOR,
56  translation_key="refrigerator_super_mode",
57  ),
59  key="Refrigeration.Common.Setting.EcoMode",
60  translation_key="eco_mode",
61  ),
63  key="Cooking.Oven.Setting.SabbathMode",
64  translation_key="sabbath_mode",
65  ),
67  key="Refrigeration.Common.Setting.SabbathMode",
68  translation_key="sabbath_mode",
69  ),
71  key="Refrigeration.Common.Setting.VacationMode",
72  translation_key="vacation_mode",
73  ),
75  key="Refrigeration.Common.Setting.FreshMode",
76  translation_key="fresh_mode",
77  ),
79  key=REFRIGERATION_DISPENSER,
80  translation_key="dispenser_enabled",
81  ),
83  key="Refrigeration.Common.Setting.Door.AssistantFridge",
84  translation_key="door_assistant_fridge",
85  ),
87  key="Refrigeration.Common.Setting.Door.AssistantFreezer",
88  translation_key="door_assistant_freezer",
89  ),
90 )
91 
92 
94  hass: HomeAssistant,
95  entry: HomeConnectConfigEntry,
96  async_add_entities: AddEntitiesCallback,
97 ) -> None:
98  """Set up the Home Connect switch."""
99 
100  def get_entities() -> list[SwitchEntity]:
101  """Get a list of entities."""
102  entities: list[SwitchEntity] = []
103  for device in entry.runtime_data.devices:
104  if device.appliance.type in APPLIANCES_WITH_PROGRAMS:
105  with contextlib.suppress(HomeConnectError):
106  programs = device.appliance.get_programs_available()
107  if programs:
108  entities.extend(
109  HomeConnectProgramSwitch(device, program)
110  for program in programs
111  )
112  entities.append(HomeConnectPowerSwitch(device))
113  entities.extend(
114  HomeConnectSwitch(device, description)
115  for description in SWITCHES
116  if description.key in device.appliance.status
117  )
118 
119  return entities
120 
121  async_add_entities(await hass.async_add_executor_job(get_entities), True)
122 
123 
125  """Generic switch class for Home Connect Binary Settings."""
126 
127  async def async_turn_on(self, **kwargs: Any) -> None:
128  """Turn on setting."""
129 
130  _LOGGER.debug("Turning on %s", self.entity_descriptionentity_description.key)
131  try:
132  await self.hasshass.async_add_executor_job(
133  self.devicedevice.appliance.set_setting, self.entity_descriptionentity_description.key, True
134  )
135  except HomeConnectError as err:
136  self._attr_available_attr_available = False
138  translation_domain=DOMAIN,
139  translation_key="turn_on",
140  translation_placeholders={
142  SVE_TRANSLATION_PLACEHOLDER_ENTITY_ID: self.entity_identity_id,
143  SVE_TRANSLATION_PLACEHOLDER_SETTING_KEY: self.bsh_keybsh_key,
144  },
145  ) from err
146 
147  self._attr_available_attr_available = True
148  self.async_entity_updateasync_entity_update()
149 
150  async def async_turn_off(self, **kwargs: Any) -> None:
151  """Turn off setting."""
152 
153  _LOGGER.debug("Turning off %s", self.entity_descriptionentity_description.key)
154  try:
155  await self.hasshass.async_add_executor_job(
156  self.devicedevice.appliance.set_setting, self.entity_descriptionentity_description.key, False
157  )
158  except HomeConnectError as err:
159  _LOGGER.error("Error while trying to turn off: %s", err)
160  self._attr_available_attr_available = False
162  translation_domain=DOMAIN,
163  translation_key="turn_off",
164  translation_placeholders={
166  SVE_TRANSLATION_PLACEHOLDER_ENTITY_ID: self.entity_identity_id,
167  SVE_TRANSLATION_PLACEHOLDER_SETTING_KEY: self.bsh_keybsh_key,
168  },
169  ) from err
170 
171  self._attr_available_attr_available = True
172  self.async_entity_updateasync_entity_update()
173 
174  async def async_update(self) -> None:
175  """Update the switch's status."""
176 
177  self._attr_is_on_attr_is_on = self.devicedevice.appliance.status.get(
178  self.entity_descriptionentity_description.key, {}
179  ).get(ATTR_VALUE)
180  self._attr_available_attr_available = True
181  _LOGGER.debug(
182  "Updated %s, new state: %s",
183  self.entity_descriptionentity_description.key,
184  self._attr_is_on_attr_is_on,
185  )
186 
187 
189  """Switch class for Home Connect."""
190 
191  def __init__(self, device: HomeConnectDevice, program_name: str) -> None:
192  """Initialize the entity."""
193  desc = " ".join(["Program", program_name.split(".")[-1]])
194  if device.appliance.type == "WasherDryer":
195  desc = " ".join(
196  ["Program", program_name.split(".")[-3], program_name.split(".")[-1]]
197  )
198  super().__init__(device, SwitchEntityDescription(key=program_name))
199  self._attr_name_attr_name = f"{device.appliance.name} {desc}"
200  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{device.appliance.haId}-{desc}"
201  self._attr_has_entity_name_attr_has_entity_name_attr_has_entity_name = False
202  self.program_nameprogram_name = program_name
203 
204  async def async_turn_on(self, **kwargs: Any) -> None:
205  """Start the program."""
206  _LOGGER.debug("Tried to turn on program %s", self.program_nameprogram_name)
207  try:
208  await self.hasshass.async_add_executor_job(
209  self.devicedevice.appliance.start_program, self.program_nameprogram_name
210  )
211  except HomeConnectError as err:
213  translation_domain=DOMAIN,
214  translation_key="start_program",
215  translation_placeholders={
217  "program": self.program_nameprogram_name,
218  },
219  ) from err
220  self.async_entity_updateasync_entity_update()
221 
222  async def async_turn_off(self, **kwargs: Any) -> None:
223  """Stop the program."""
224  _LOGGER.debug("Tried to stop program %s", self.program_nameprogram_name)
225  try:
226  await self.hasshass.async_add_executor_job(self.devicedevice.appliance.stop_program)
227  except HomeConnectError as err:
229  translation_domain=DOMAIN,
230  translation_key="stop_program",
231  translation_placeholders={
233  "program": self.program_nameprogram_name,
234  },
235  ) from err
236  self.async_entity_updateasync_entity_update()
237 
238  async def async_update(self) -> None:
239  """Update the switch's status."""
240  state = self.devicedevice.appliance.status.get(BSH_ACTIVE_PROGRAM, {})
241  if state.get(ATTR_VALUE) == self.program_nameprogram_name:
242  self._attr_is_on_attr_is_on = True
243  else:
244  self._attr_is_on_attr_is_on = False
245  _LOGGER.debug("Updated, new state: %s", self._attr_is_on_attr_is_on)
246 
247 
249  """Power switch class for Home Connect."""
250 
251  power_off_state: str | None
252 
253  def __init__(self, device: HomeConnectDevice) -> None:
254  """Initialize the entity."""
255  super().__init__(
256  device,
257  SwitchEntityDescription(key=BSH_POWER_STATE, translation_key="power"),
258  )
259  if (
260  power_state := device.appliance.status.get(BSH_POWER_STATE, {}).get(
261  ATTR_VALUE
262  )
263  ) and power_state in [BSH_POWER_OFF, BSH_POWER_STANDBY]:
264  self.power_off_statepower_off_state = power_state
265 
266  async def async_added_to_hass(self) -> None:
267  """Add the entity to the hass instance."""
268  await super().async_added_to_hass()
269  if not hasattr(self, "power_off_state"):
270  await self.async_fetch_power_off_stateasync_fetch_power_off_state()
271 
272  async def async_turn_on(self, **kwargs: Any) -> None:
273  """Switch the device on."""
274  _LOGGER.debug("Tried to switch on %s", self.namename)
275  try:
276  await self.hasshass.async_add_executor_job(
277  self.devicedevice.appliance.set_setting, BSH_POWER_STATE, BSH_POWER_ON
278  )
279  except HomeConnectError as err:
280  self._attr_is_on_attr_is_on = False
282  translation_domain=DOMAIN,
283  translation_key="power_on",
284  translation_placeholders={
286  SVE_TRANSLATION_PLACEHOLDER_APPLIANCE_NAME: self.devicedevice.appliance.name,
287  },
288  ) from err
289  self.async_entity_updateasync_entity_update()
290 
291  async def async_turn_off(self, **kwargs: Any) -> None:
292  """Switch the device off."""
293  if not hasattr(self, "power_off_state"):
295  translation_domain=DOMAIN,
296  translation_key="unable_to_retrieve_turn_off",
297  translation_placeholders={
298  SVE_TRANSLATION_PLACEHOLDER_APPLIANCE_NAME: self.devicedevice.appliance.name
299  },
300  )
301 
302  if self.power_off_statepower_off_state is None:
304  translation_domain=DOMAIN,
305  translation_key="turn_off_not_supported",
306  translation_placeholders={
307  SVE_TRANSLATION_PLACEHOLDER_APPLIANCE_NAME: self.devicedevice.appliance.name
308  },
309  )
310  _LOGGER.debug("tried to switch off %s", self.namename)
311  try:
312  await self.hasshass.async_add_executor_job(
313  self.devicedevice.appliance.set_setting,
314  BSH_POWER_STATE,
315  self.power_off_statepower_off_state,
316  )
317  except HomeConnectError as err:
318  self._attr_is_on_attr_is_on = True
320  translation_domain=DOMAIN,
321  translation_key="power_off",
322  translation_placeholders={
324  SVE_TRANSLATION_PLACEHOLDER_APPLIANCE_NAME: self.devicedevice.appliance.name,
325  SVE_TRANSLATION_PLACEHOLDER_VALUE: self.power_off_statepower_off_state,
326  },
327  ) from err
328  self.async_entity_updateasync_entity_update()
329 
330  async def async_update(self) -> None:
331  """Update the switch's status."""
332  if (
333  self.devicedevice.appliance.status.get(BSH_POWER_STATE, {}).get(ATTR_VALUE)
334  == BSH_POWER_ON
335  ):
336  self._attr_is_on_attr_is_on = True
337  elif (
338  hasattr(self, "power_off_state")
339  and self.devicedevice.appliance.status.get(BSH_POWER_STATE, {}).get(ATTR_VALUE)
340  == self.power_off_statepower_off_state
341  ):
342  self._attr_is_on_attr_is_on = False
343  elif self.devicedevice.appliance.status.get(BSH_OPERATION_STATE, {}).get(
344  ATTR_VALUE, None
345  ) in [
346  "BSH.Common.EnumType.OperationState.Ready",
347  "BSH.Common.EnumType.OperationState.DelayedStart",
348  "BSH.Common.EnumType.OperationState.Run",
349  "BSH.Common.EnumType.OperationState.Pause",
350  "BSH.Common.EnumType.OperationState.ActionRequired",
351  "BSH.Common.EnumType.OperationState.Aborting",
352  "BSH.Common.EnumType.OperationState.Finished",
353  ]:
354  self._attr_is_on_attr_is_on = True
355  elif (
356  self.devicedevice.appliance.status.get(BSH_OPERATION_STATE, {}).get(ATTR_VALUE)
357  == "BSH.Common.EnumType.OperationState.Inactive"
358  ):
359  self._attr_is_on_attr_is_on = False
360  else:
361  self._attr_is_on_attr_is_on = None
362  _LOGGER.debug("Updated, new state: %s", self._attr_is_on_attr_is_on)
363 
364  async def async_fetch_power_off_state(self) -> None:
365  """Fetch the power off state."""
366  try:
367  data = await self.hasshass.async_add_executor_job(
368  self.devicedevice.appliance.get, f"/settings/{self.bsh_key}"
369  )
370  except HomeConnectError as err:
371  _LOGGER.error("An error occurred: %s", err)
372  return
373  if not data or not (
374  allowed_values := data.get(ATTR_CONSTRAINTS, {}).get(ATTR_ALLOWED_VALUES)
375  ):
376  return
377 
378  if BSH_POWER_OFF in allowed_values:
379  self.power_off_statepower_off_state = BSH_POWER_OFF
380  elif BSH_POWER_STANDBY in allowed_values:
381  self.power_off_statepower_off_state = BSH_POWER_STANDBY
382  else:
383  self.power_off_statepower_off_state = None
None __init__(self, HomeConnectDevice device, str program_name)
Definition: switch.py:191
str|UndefinedType|None name(self)
Definition: entity.py:738
None async_turn_off(self, **Any kwargs)
Definition: entity.py:1709
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, HomeConnectConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: switch.py:97
dict[str, Any] get_dict_from_home_connect_error(api.HomeConnectError err)
Definition: __init__.py:334
list[OneWireSwitch] get_entities(OneWireHub onewire_hub)
Definition: switch.py:162