Home Assistant Unofficial Reference 2024.12.1
switch.py
Go to the documentation of this file.
1 """YoLink Switch."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import Any
8 
9 from yolink.client_request import ClientRequest
10 from yolink.const import (
11  ATTR_DEVICE_MANIPULATOR,
12  ATTR_DEVICE_MULTI_OUTLET,
13  ATTR_DEVICE_OUTLET,
14  ATTR_DEVICE_SWITCH,
15 )
16 from yolink.device import YoLinkDevice
17 from yolink.outlet_request_builder import OutletRequestBuilder
18 
20  SwitchDeviceClass,
21  SwitchEntity,
22  SwitchEntityDescription,
23 )
24 from homeassistant.config_entries import ConfigEntry
25 from homeassistant.core import HomeAssistant, callback
26 from homeassistant.helpers.entity_platform import AddEntitiesCallback
27 
28 from .const import DEV_MODEL_MULTI_OUTLET_YS6801, DOMAIN
29 from .coordinator import YoLinkCoordinator
30 from .entity import YoLinkEntity
31 
32 
33 @dataclass(frozen=True)
35  """YoLink SwitchEntityDescription."""
36 
37  exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True
38  plug_index_fn: Callable[[YoLinkDevice], int | None] = lambda _: None
39 
40 
41 DEVICE_TYPES: tuple[YoLinkSwitchEntityDescription, ...] = (
43  key="outlet_state",
44  device_class=SwitchDeviceClass.OUTLET,
45  name=None,
46  exists_fn=lambda device: device.device_type == ATTR_DEVICE_OUTLET,
47  ),
49  key="manipulator_state",
50  translation_key="manipulator_state",
51  name=None,
52  exists_fn=lambda device: device.device_type == ATTR_DEVICE_MANIPULATOR,
53  ),
55  key="switch_state",
56  name=None,
57  device_class=SwitchDeviceClass.SWITCH,
58  exists_fn=lambda device: device.device_type == ATTR_DEVICE_SWITCH,
59  ),
61  key="multi_outlet_usb_ports",
62  translation_key="usb_ports",
63  device_class=SwitchDeviceClass.OUTLET,
64  exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET
65  and device.device_model_name.startswith(DEV_MODEL_MULTI_OUTLET_YS6801),
66  plug_index_fn=lambda _: 0,
67  ),
69  key="multi_outlet_plug_1",
70  translation_key="plug_1",
71  device_class=SwitchDeviceClass.OUTLET,
72  exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET,
73  plug_index_fn=lambda device: (
74  1
75  if device.device_model_name.startswith(DEV_MODEL_MULTI_OUTLET_YS6801)
76  else 0
77  ),
78  ),
80  key="multi_outlet_plug_2",
81  translation_key="plug_2",
82  device_class=SwitchDeviceClass.OUTLET,
83  exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET,
84  plug_index_fn=lambda device: (
85  2
86  if device.device_model_name.startswith(DEV_MODEL_MULTI_OUTLET_YS6801)
87  else 1
88  ),
89  ),
91  key="multi_outlet_plug_3",
92  translation_key="plug_3",
93  device_class=SwitchDeviceClass.OUTLET,
94  exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET
95  and device.device_model_name.startswith(DEV_MODEL_MULTI_OUTLET_YS6801),
96  plug_index_fn=lambda _: 3,
97  ),
99  key="multi_outlet_plug_4",
100  translation_key="plug_4",
101  device_class=SwitchDeviceClass.OUTLET,
102  exists_fn=lambda device: device.device_type == ATTR_DEVICE_MULTI_OUTLET
103  and device.device_model_name.startswith(DEV_MODEL_MULTI_OUTLET_YS6801),
104  plug_index_fn=lambda _: 4,
105  ),
106 )
107 
108 DEVICE_TYPE = [
109  ATTR_DEVICE_MANIPULATOR,
110  ATTR_DEVICE_MULTI_OUTLET,
111  ATTR_DEVICE_OUTLET,
112  ATTR_DEVICE_SWITCH,
113 ]
114 
115 
117  hass: HomeAssistant,
118  config_entry: ConfigEntry,
119  async_add_entities: AddEntitiesCallback,
120 ) -> None:
121  """Set up YoLink switch from a config entry."""
122  device_coordinators = hass.data[DOMAIN][config_entry.entry_id].device_coordinators
123  switch_device_coordinators = [
124  device_coordinator
125  for device_coordinator in device_coordinators.values()
126  if device_coordinator.device.device_type in DEVICE_TYPE
127  ]
129  YoLinkSwitchEntity(config_entry, switch_device_coordinator, description)
130  for switch_device_coordinator in switch_device_coordinators
131  for description in DEVICE_TYPES
132  if description.exists_fn(switch_device_coordinator.device)
133  )
134 
135 
137  """YoLink Switch Entity."""
138 
139  entity_description: YoLinkSwitchEntityDescription
140 
141  def __init__(
142  self,
143  config_entry: ConfigEntry,
144  coordinator: YoLinkCoordinator,
145  description: YoLinkSwitchEntityDescription,
146  ) -> None:
147  """Init YoLink switch."""
148  super().__init__(config_entry, coordinator)
149  self.entity_descriptionentity_description = description
150  self._attr_unique_id_attr_unique_id = (
151  f"{coordinator.device.device_id} {self.entity_description.key}"
152  )
153 
155  self, state_value: str | list[str] | None, plug_index: int | None
156  ) -> bool | None:
157  """Parse state value."""
158  if isinstance(state_value, list) and plug_index is not None:
159  return state_value[plug_index] == "open"
160  return state_value == "open" if state_value is not None else None
161 
162  @callback
163  def update_entity_state(self, state: dict[str, str | list[str]]) -> None:
164  """Update HA Entity State."""
165  self._attr_is_on_attr_is_on = self._get_state_get_state(
166  state.get("state"),
167  self.entity_descriptionentity_description.plug_index_fn(self.coordinator.device),
168  )
169  self.async_write_ha_stateasync_write_ha_state()
170 
171  async def call_state_change(self, state: str) -> None:
172  """Call setState api to change switch state."""
173  client_request: ClientRequest = None
174  if self.coordinator.device.device_type in [
175  ATTR_DEVICE_OUTLET,
176  ATTR_DEVICE_MULTI_OUTLET,
177  ]:
178  client_request = OutletRequestBuilder.set_state_request(
179  state, self.entity_descriptionentity_description.plug_index_fn(self.coordinator.device)
180  )
181  else:
182  client_request = ClientRequest("setState", {"state": state})
183  await self.call_devicecall_device(client_request)
184  self._attr_is_on_attr_is_on = self._get_state_get_state(
185  state, self.entity_descriptionentity_description.plug_index_fn(self.coordinator.device)
186  )
187  self.async_write_ha_stateasync_write_ha_state()
188 
189  async def async_turn_on(self, **kwargs: Any) -> None:
190  """Turn the entity on."""
191  await self.call_state_changecall_state_change("open")
192 
193  async def async_turn_off(self, **kwargs: Any) -> None:
194  """Turn the entity off."""
195  await self.call_state_changecall_state_change("close")