Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for Vallox ventilation units."""
2 
3 from __future__ import annotations
4 
5 import ipaddress
6 import logging
7 from typing import NamedTuple
8 
9 from vallox_websocket_api import Profile, Vallox, ValloxApiException
10 import voluptuous as vol
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import CONF_HOST, CONF_NAME, Platform
14 from homeassistant.core import HomeAssistant, ServiceCall
15 from homeassistant.helpers import config_validation as cv
16 
17 from .const import (
18  DEFAULT_FAN_SPEED_AWAY,
19  DEFAULT_FAN_SPEED_BOOST,
20  DEFAULT_FAN_SPEED_HOME,
21  DEFAULT_NAME,
22  DOMAIN,
23  I18N_KEY_TO_VALLOX_PROFILE,
24 )
25 from .coordinator import ValloxDataUpdateCoordinator
26 
27 _LOGGER = logging.getLogger(__name__)
28 
29 CONFIG_SCHEMA = vol.Schema(
30  vol.All(
31  cv.deprecated(DOMAIN),
32  {
33  DOMAIN: vol.Schema(
34  {
35  vol.Required(CONF_HOST): vol.All(ipaddress.ip_address, cv.string),
36  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
37  }
38  )
39  },
40  ),
41  extra=vol.ALLOW_EXTRA,
42 )
43 
44 PLATFORMS: list[str] = [
45  Platform.BINARY_SENSOR,
46  Platform.DATE,
47  Platform.FAN,
48  Platform.NUMBER,
49  Platform.SENSOR,
50  Platform.SWITCH,
51 ]
52 
53 ATTR_PROFILE_FAN_SPEED = "fan_speed"
54 
55 SERVICE_SCHEMA_SET_PROFILE_FAN_SPEED = vol.Schema(
56  {
57  vol.Required(ATTR_PROFILE_FAN_SPEED): vol.All(
58  vol.Coerce(int), vol.Clamp(min=0, max=100)
59  )
60  }
61 )
62 
63 ATTR_PROFILE = "profile"
64 ATTR_DURATION = "duration"
65 
66 SERVICE_SCHEMA_SET_PROFILE = vol.Schema(
67  {
68  vol.Required(ATTR_PROFILE): vol.In(I18N_KEY_TO_VALLOX_PROFILE),
69  vol.Optional(ATTR_DURATION): vol.All(
70  vol.Coerce(int), vol.Clamp(min=1, max=65535)
71  ),
72  }
73 )
74 
75 
76 class ServiceMethodDetails(NamedTuple):
77  """Details for SERVICE_TO_METHOD mapping."""
78 
79  method: str
80  schema: vol.Schema
81 
82 
83 SERVICE_SET_PROFILE_FAN_SPEED_HOME = "set_profile_fan_speed_home"
84 SERVICE_SET_PROFILE_FAN_SPEED_AWAY = "set_profile_fan_speed_away"
85 SERVICE_SET_PROFILE_FAN_SPEED_BOOST = "set_profile_fan_speed_boost"
86 SERVICE_SET_PROFILE = "set_profile"
87 
88 SERVICE_TO_METHOD = {
89  SERVICE_SET_PROFILE_FAN_SPEED_HOME: ServiceMethodDetails(
90  method="async_set_profile_fan_speed_home",
91  schema=SERVICE_SCHEMA_SET_PROFILE_FAN_SPEED,
92  ),
93  SERVICE_SET_PROFILE_FAN_SPEED_AWAY: ServiceMethodDetails(
94  method="async_set_profile_fan_speed_away",
95  schema=SERVICE_SCHEMA_SET_PROFILE_FAN_SPEED,
96  ),
97  SERVICE_SET_PROFILE_FAN_SPEED_BOOST: ServiceMethodDetails(
98  method="async_set_profile_fan_speed_boost",
99  schema=SERVICE_SCHEMA_SET_PROFILE_FAN_SPEED,
100  ),
101  SERVICE_SET_PROFILE: ServiceMethodDetails(
102  method="async_set_profile", schema=SERVICE_SCHEMA_SET_PROFILE
103  ),
104 }
105 
106 
107 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
108  """Set up the client and boot the platforms."""
109  host = entry.data[CONF_HOST]
110  name = entry.data[CONF_NAME]
111 
112  client = Vallox(host)
113 
114  coordinator = ValloxDataUpdateCoordinator(hass, name, client)
115 
116  await coordinator.async_config_entry_first_refresh()
117 
118  service_handler = ValloxServiceHandler(client, coordinator)
119  for vallox_service, service_details in SERVICE_TO_METHOD.items():
120  hass.services.async_register(
121  DOMAIN,
122  vallox_service,
123  service_handler.async_handle,
124  schema=service_details.schema,
125  )
126 
127  hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
128  "client": client,
129  "coordinator": coordinator,
130  "name": name,
131  }
132 
133  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
134 
135  return True
136 
137 
138 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
139  """Unload a config entry."""
140  if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
141  hass.data[DOMAIN].pop(entry.entry_id)
142 
143  if hass.data[DOMAIN]:
144  return unload_ok
145 
146  for service in SERVICE_TO_METHOD:
147  hass.services.async_remove(DOMAIN, service)
148 
149  return unload_ok
150 
151 
153  """Services implementation."""
154 
155  def __init__(
156  self, client: Vallox, coordinator: ValloxDataUpdateCoordinator
157  ) -> None:
158  """Initialize the proxy."""
159  self._client_client = client
160  self._coordinator_coordinator = coordinator
161 
163  self, fan_speed: int = DEFAULT_FAN_SPEED_HOME
164  ) -> bool:
165  """Set the fan speed in percent for the Home profile."""
166  _LOGGER.debug("Setting Home fan speed to: %d%%", fan_speed)
167 
168  try:
169  await self._client_client.set_fan_speed(Profile.HOME, fan_speed)
170  except ValloxApiException as err:
171  _LOGGER.error("Error setting fan speed for Home profile: %s", err)
172  return False
173  return True
174 
176  self, fan_speed: int = DEFAULT_FAN_SPEED_AWAY
177  ) -> bool:
178  """Set the fan speed in percent for the Away profile."""
179  _LOGGER.debug("Setting Away fan speed to: %d%%", fan_speed)
180 
181  try:
182  await self._client_client.set_fan_speed(Profile.AWAY, fan_speed)
183  except ValloxApiException as err:
184  _LOGGER.error("Error setting fan speed for Away profile: %s", err)
185  return False
186  return True
187 
189  self, fan_speed: int = DEFAULT_FAN_SPEED_BOOST
190  ) -> bool:
191  """Set the fan speed in percent for the Boost profile."""
192  _LOGGER.debug("Setting Boost fan speed to: %d%%", fan_speed)
193 
194  try:
195  await self._client_client.set_fan_speed(Profile.BOOST, fan_speed)
196  except ValloxApiException as err:
197  _LOGGER.error("Error setting fan speed for Boost profile: %s", err)
198  return False
199  return True
200 
201  async def async_set_profile(
202  self, profile: str, duration: int | None = None
203  ) -> bool:
204  """Activate profile for given duration."""
205  _LOGGER.debug("Activating profile %s for %s min", profile, duration)
206  try:
207  await self._client_client.set_profile(
208  I18N_KEY_TO_VALLOX_PROFILE[profile], duration
209  )
210  except ValloxApiException as err:
211  _LOGGER.error(
212  "Error setting profile %d for duration %s: %s", profile, duration, err
213  )
214  return False
215  return True
216 
217  async def async_handle(self, call: ServiceCall) -> None:
218  """Dispatch a service call."""
219  service_details = SERVICE_TO_METHOD.get(call.service)
220  params = call.data.copy()
221 
222  if service_details is None:
223  return
224 
225  if not hasattr(self, service_details.method):
226  _LOGGER.error("Service not implemented: %s", service_details.method)
227  return
228 
229  result = await getattr(self, service_details.method)(**params)
230 
231  # This state change affects other entities like sensors. Force an immediate update that can
232  # be observed by all parties involved.
233  if result:
234  await self._coordinator_coordinator.async_request_refresh()
bool async_set_profile_fan_speed_boost(self, int fan_speed=DEFAULT_FAN_SPEED_BOOST)
Definition: __init__.py:190
bool async_set_profile_fan_speed_home(self, int fan_speed=DEFAULT_FAN_SPEED_HOME)
Definition: __init__.py:164
bool async_set_profile(self, str profile, int|None duration=None)
Definition: __init__.py:203
None __init__(self, Vallox client, ValloxDataUpdateCoordinator coordinator)
Definition: __init__.py:157
bool async_set_profile_fan_speed_away(self, int fan_speed=DEFAULT_FAN_SPEED_AWAY)
Definition: __init__.py:177
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:107
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:138