Home Assistant Unofficial Reference 2024.12.1
services.py
Go to the documentation of this file.
1 """Support for HomematicIP Cloud devices."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from pathlib import Path
7 
8 from homematicip.aio.device import AsyncSwitchMeasuring
9 from homematicip.aio.group import AsyncHeatingGroup
10 from homematicip.aio.home import AsyncHome
11 from homematicip.base.helpers import handle_config
12 import voluptuous as vol
13 
14 from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
15 from homeassistant.core import HomeAssistant, ServiceCall
16 from homeassistant.exceptions import ServiceValidationError
18 from homeassistant.helpers.config_validation import comp_entity_ids
20  async_register_admin_service,
21  verify_domain_control,
22 )
23 
24 from .const import DOMAIN
25 
26 _LOGGER = logging.getLogger(__name__)
27 
28 ATTR_ACCESSPOINT_ID = "accesspoint_id"
29 ATTR_ANONYMIZE = "anonymize"
30 ATTR_CLIMATE_PROFILE_INDEX = "climate_profile_index"
31 ATTR_CONFIG_OUTPUT_FILE_PREFIX = "config_output_file_prefix"
32 ATTR_CONFIG_OUTPUT_PATH = "config_output_path"
33 ATTR_DURATION = "duration"
34 ATTR_ENDTIME = "endtime"
35 ATTR_COOLING = "cooling"
36 
37 DEFAULT_CONFIG_FILE_PREFIX = "hmip-config"
38 
39 SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION = "activate_eco_mode_with_duration"
40 SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD = "activate_eco_mode_with_period"
41 SERVICE_ACTIVATE_VACATION = "activate_vacation"
42 SERVICE_DEACTIVATE_ECO_MODE = "deactivate_eco_mode"
43 SERVICE_DEACTIVATE_VACATION = "deactivate_vacation"
44 SERVICE_DUMP_HAP_CONFIG = "dump_hap_config"
45 SERVICE_RESET_ENERGY_COUNTER = "reset_energy_counter"
46 SERVICE_SET_ACTIVE_CLIMATE_PROFILE = "set_active_climate_profile"
47 SERVICE_SET_HOME_COOLING_MODE = "set_home_cooling_mode"
48 
49 HMIPC_SERVICES = [
50  SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION,
51  SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD,
52  SERVICE_ACTIVATE_VACATION,
53  SERVICE_DEACTIVATE_ECO_MODE,
54  SERVICE_DEACTIVATE_VACATION,
55  SERVICE_DUMP_HAP_CONFIG,
56  SERVICE_RESET_ENERGY_COUNTER,
57  SERVICE_SET_ACTIVE_CLIMATE_PROFILE,
58  SERVICE_SET_HOME_COOLING_MODE,
59 ]
60 
61 SCHEMA_ACTIVATE_ECO_MODE_WITH_DURATION = vol.Schema(
62  {
63  vol.Required(ATTR_DURATION): cv.positive_int,
64  vol.Optional(ATTR_ACCESSPOINT_ID): vol.All(str, vol.Length(min=24, max=24)),
65  }
66 )
67 
68 SCHEMA_ACTIVATE_ECO_MODE_WITH_PERIOD = vol.Schema(
69  {
70  vol.Required(ATTR_ENDTIME): cv.datetime,
71  vol.Optional(ATTR_ACCESSPOINT_ID): vol.All(str, vol.Length(min=24, max=24)),
72  }
73 )
74 
75 SCHEMA_ACTIVATE_VACATION = vol.Schema(
76  {
77  vol.Required(ATTR_ENDTIME): cv.datetime,
78  vol.Required(ATTR_TEMPERATURE, default=18.0): vol.All(
79  vol.Coerce(float), vol.Range(min=0, max=55)
80  ),
81  vol.Optional(ATTR_ACCESSPOINT_ID): vol.All(str, vol.Length(min=24, max=24)),
82  }
83 )
84 
85 SCHEMA_DEACTIVATE_ECO_MODE = vol.Schema(
86  {vol.Optional(ATTR_ACCESSPOINT_ID): vol.All(str, vol.Length(min=24, max=24))}
87 )
88 
89 SCHEMA_DEACTIVATE_VACATION = vol.Schema(
90  {vol.Optional(ATTR_ACCESSPOINT_ID): vol.All(str, vol.Length(min=24, max=24))}
91 )
92 
93 SCHEMA_SET_ACTIVE_CLIMATE_PROFILE = vol.Schema(
94  {
95  vol.Required(ATTR_ENTITY_ID): comp_entity_ids,
96  vol.Required(ATTR_CLIMATE_PROFILE_INDEX): cv.positive_int,
97  }
98 )
99 
100 SCHEMA_DUMP_HAP_CONFIG = vol.Schema(
101  {
102  vol.Optional(ATTR_CONFIG_OUTPUT_PATH): cv.string,
103  vol.Optional(
104  ATTR_CONFIG_OUTPUT_FILE_PREFIX, default=DEFAULT_CONFIG_FILE_PREFIX
105  ): cv.string,
106  vol.Optional(ATTR_ANONYMIZE, default=True): cv.boolean,
107  }
108 )
109 
110 SCHEMA_RESET_ENERGY_COUNTER = vol.Schema(
111  {vol.Required(ATTR_ENTITY_ID): comp_entity_ids}
112 )
113 
114 SCHEMA_SET_HOME_COOLING_MODE = vol.Schema(
115  {
116  vol.Optional(ATTR_COOLING, default=True): cv.boolean,
117  vol.Optional(ATTR_ACCESSPOINT_ID): vol.All(str, vol.Length(min=24, max=24)),
118  }
119 )
120 
121 
122 async def async_setup_services(hass: HomeAssistant) -> None:
123  """Set up the HomematicIP Cloud services."""
124 
125  if hass.services.async_services_for_domain(DOMAIN):
126  return
127 
128  @verify_domain_control(hass, DOMAIN)
129  async def async_call_hmipc_service(service: ServiceCall) -> None:
130  """Call correct HomematicIP Cloud service."""
131  service_name = service.service
132 
133  if service_name == SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION:
134  await _async_activate_eco_mode_with_duration(hass, service)
135  elif service_name == SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD:
136  await _async_activate_eco_mode_with_period(hass, service)
137  elif service_name == SERVICE_ACTIVATE_VACATION:
138  await _async_activate_vacation(hass, service)
139  elif service_name == SERVICE_DEACTIVATE_ECO_MODE:
140  await _async_deactivate_eco_mode(hass, service)
141  elif service_name == SERVICE_DEACTIVATE_VACATION:
142  await _async_deactivate_vacation(hass, service)
143  elif service_name == SERVICE_DUMP_HAP_CONFIG:
144  await _async_dump_hap_config(hass, service)
145  elif service_name == SERVICE_RESET_ENERGY_COUNTER:
146  await _async_reset_energy_counter(hass, service)
147  elif service_name == SERVICE_SET_ACTIVE_CLIMATE_PROFILE:
148  await _set_active_climate_profile(hass, service)
149  elif service_name == SERVICE_SET_HOME_COOLING_MODE:
150  await _async_set_home_cooling_mode(hass, service)
151 
152  hass.services.async_register(
153  domain=DOMAIN,
154  service=SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION,
155  service_func=async_call_hmipc_service,
156  schema=SCHEMA_ACTIVATE_ECO_MODE_WITH_DURATION,
157  )
158 
159  hass.services.async_register(
160  domain=DOMAIN,
161  service=SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD,
162  service_func=async_call_hmipc_service,
163  schema=SCHEMA_ACTIVATE_ECO_MODE_WITH_PERIOD,
164  )
165 
166  hass.services.async_register(
167  domain=DOMAIN,
168  service=SERVICE_ACTIVATE_VACATION,
169  service_func=async_call_hmipc_service,
170  schema=SCHEMA_ACTIVATE_VACATION,
171  )
172 
173  hass.services.async_register(
174  domain=DOMAIN,
175  service=SERVICE_DEACTIVATE_ECO_MODE,
176  service_func=async_call_hmipc_service,
177  schema=SCHEMA_DEACTIVATE_ECO_MODE,
178  )
179 
180  hass.services.async_register(
181  domain=DOMAIN,
182  service=SERVICE_DEACTIVATE_VACATION,
183  service_func=async_call_hmipc_service,
184  schema=SCHEMA_DEACTIVATE_VACATION,
185  )
186 
187  hass.services.async_register(
188  domain=DOMAIN,
189  service=SERVICE_SET_ACTIVE_CLIMATE_PROFILE,
190  service_func=async_call_hmipc_service,
191  schema=SCHEMA_SET_ACTIVE_CLIMATE_PROFILE,
192  )
193 
195  hass=hass,
196  domain=DOMAIN,
197  service=SERVICE_DUMP_HAP_CONFIG,
198  service_func=async_call_hmipc_service,
199  schema=SCHEMA_DUMP_HAP_CONFIG,
200  )
201 
203  hass=hass,
204  domain=DOMAIN,
205  service=SERVICE_RESET_ENERGY_COUNTER,
206  service_func=async_call_hmipc_service,
207  schema=SCHEMA_RESET_ENERGY_COUNTER,
208  )
209 
211  hass=hass,
212  domain=DOMAIN,
213  service=SERVICE_SET_HOME_COOLING_MODE,
214  service_func=async_call_hmipc_service,
215  schema=SCHEMA_SET_HOME_COOLING_MODE,
216  )
217 
218 
219 async def async_unload_services(hass: HomeAssistant):
220  """Unload HomematicIP Cloud services."""
221  if hass.data[DOMAIN]:
222  return
223 
224  for hmipc_service in HMIPC_SERVICES:
225  hass.services.async_remove(domain=DOMAIN, service=hmipc_service)
226 
227 
229  hass: HomeAssistant, service: ServiceCall
230 ) -> None:
231  """Service to activate eco mode with duration."""
232  duration = service.data[ATTR_DURATION]
233 
234  if hapid := service.data.get(ATTR_ACCESSPOINT_ID):
235  if home := _get_home(hass, hapid):
236  await home.activate_absence_with_duration(duration)
237  else:
238  for hap in hass.data[DOMAIN].values():
239  await hap.home.activate_absence_with_duration(duration)
240 
241 
243  hass: HomeAssistant, service: ServiceCall
244 ) -> None:
245  """Service to activate eco mode with period."""
246  endtime = service.data[ATTR_ENDTIME]
247 
248  if hapid := service.data.get(ATTR_ACCESSPOINT_ID):
249  if home := _get_home(hass, hapid):
250  await home.activate_absence_with_period(endtime)
251  else:
252  for hap in hass.data[DOMAIN].values():
253  await hap.home.activate_absence_with_period(endtime)
254 
255 
256 async def _async_activate_vacation(hass: HomeAssistant, service: ServiceCall) -> None:
257  """Service to activate vacation."""
258  endtime = service.data[ATTR_ENDTIME]
259  temperature = service.data[ATTR_TEMPERATURE]
260 
261  if hapid := service.data.get(ATTR_ACCESSPOINT_ID):
262  if home := _get_home(hass, hapid):
263  await home.activate_vacation(endtime, temperature)
264  else:
265  for hap in hass.data[DOMAIN].values():
266  await hap.home.activate_vacation(endtime, temperature)
267 
268 
269 async def _async_deactivate_eco_mode(hass: HomeAssistant, service: ServiceCall) -> None:
270  """Service to deactivate eco mode."""
271  if hapid := service.data.get(ATTR_ACCESSPOINT_ID):
272  if home := _get_home(hass, hapid):
273  await home.deactivate_absence()
274  else:
275  for hap in hass.data[DOMAIN].values():
276  await hap.home.deactivate_absence()
277 
278 
279 async def _async_deactivate_vacation(hass: HomeAssistant, service: ServiceCall) -> None:
280  """Service to deactivate vacation."""
281  if hapid := service.data.get(ATTR_ACCESSPOINT_ID):
282  if home := _get_home(hass, hapid):
283  await home.deactivate_vacation()
284  else:
285  for hap in hass.data[DOMAIN].values():
286  await hap.home.deactivate_vacation()
287 
288 
290  hass: HomeAssistant, service: ServiceCall
291 ) -> None:
292  """Service to set the active climate profile."""
293  entity_id_list = service.data[ATTR_ENTITY_ID]
294  climate_profile_index = service.data[ATTR_CLIMATE_PROFILE_INDEX] - 1
295 
296  for hap in hass.data[DOMAIN].values():
297  if entity_id_list != "all":
298  for entity_id in entity_id_list:
299  group = hap.hmip_device_by_entity_id.get(entity_id)
300  if group and isinstance(group, AsyncHeatingGroup):
301  await group.set_active_profile(climate_profile_index)
302  else:
303  for group in hap.home.groups:
304  if isinstance(group, AsyncHeatingGroup):
305  await group.set_active_profile(climate_profile_index)
306 
307 
308 async def _async_dump_hap_config(hass: HomeAssistant, service: ServiceCall) -> None:
309  """Service to dump the configuration of a Homematic IP Access Point."""
310  config_path: str = (
311  service.data.get(ATTR_CONFIG_OUTPUT_PATH) or hass.config.config_dir
312  )
313  config_file_prefix = service.data[ATTR_CONFIG_OUTPUT_FILE_PREFIX]
314  anonymize = service.data[ATTR_ANONYMIZE]
315 
316  for hap in hass.data[DOMAIN].values():
317  hap_sgtin = hap.config_entry.unique_id
318 
319  if anonymize:
320  hap_sgtin = hap_sgtin[-4:]
321 
322  file_name = f"{config_file_prefix}_{hap_sgtin}.json"
323  path = Path(config_path)
324  config_file = path / file_name
325 
326  json_state = await hap.home.download_configuration()
327  json_state = handle_config(json_state, anonymize)
328 
329  config_file.write_text(json_state, encoding="utf8")
330 
331 
332 async def _async_reset_energy_counter(hass: HomeAssistant, service: ServiceCall):
333  """Service to reset the energy counter."""
334  entity_id_list = service.data[ATTR_ENTITY_ID]
335 
336  for hap in hass.data[DOMAIN].values():
337  if entity_id_list != "all":
338  for entity_id in entity_id_list:
339  device = hap.hmip_device_by_entity_id.get(entity_id)
340  if device and isinstance(device, AsyncSwitchMeasuring):
341  await device.reset_energy_counter()
342  else:
343  for device in hap.home.devices:
344  if isinstance(device, AsyncSwitchMeasuring):
345  await device.reset_energy_counter()
346 
347 
348 async def _async_set_home_cooling_mode(hass: HomeAssistant, service: ServiceCall):
349  """Service to set the cooling mode."""
350  cooling = service.data[ATTR_COOLING]
351 
352  if hapid := service.data.get(ATTR_ACCESSPOINT_ID):
353  if home := _get_home(hass, hapid):
354  await home.set_cooling(cooling)
355  else:
356  for hap in hass.data[DOMAIN].values():
357  await hap.home.set_cooling(cooling)
358 
359 
360 def _get_home(hass: HomeAssistant, hapid: str) -> AsyncHome | None:
361  """Return a HmIP home."""
362  if hap := hass.data[DOMAIN].get(hapid):
363  return hap.home
364 
366  translation_domain=DOMAIN,
367  translation_key="access_point_not_found",
368  translation_placeholders={"id": hapid},
369  )
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None _async_dump_hap_config(HomeAssistant hass, ServiceCall service)
Definition: services.py:308
None _async_activate_eco_mode_with_period(HomeAssistant hass, ServiceCall service)
Definition: services.py:244
def _async_reset_energy_counter(HomeAssistant hass, ServiceCall service)
Definition: services.py:332
None _async_deactivate_eco_mode(HomeAssistant hass, ServiceCall service)
Definition: services.py:269
None _set_active_climate_profile(HomeAssistant hass, ServiceCall service)
Definition: services.py:291
None _async_deactivate_vacation(HomeAssistant hass, ServiceCall service)
Definition: services.py:279
None _async_activate_vacation(HomeAssistant hass, ServiceCall service)
Definition: services.py:256
AsyncHome|None _get_home(HomeAssistant hass, str hapid)
Definition: services.py:360
None _async_activate_eco_mode_with_duration(HomeAssistant hass, ServiceCall service)
Definition: services.py:230
def _async_set_home_cooling_mode(HomeAssistant hass, ServiceCall service)
Definition: services.py:348
None async_register_admin_service(HomeAssistant hass, str domain, str service, Callable[[ServiceCall], Awaitable[None]|None] service_func, VolSchemaType schema=vol.Schema({}, extra=vol.PREVENT_EXTRA))
Definition: service.py:1121