Home Assistant Unofficial Reference 2024.12.1
services.py
Go to the documentation of this file.
1 """Support for Renault services."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Mapping
6 from datetime import datetime
7 import logging
8 from typing import TYPE_CHECKING, Any
9 
10 import voluptuous as vol
11 
12 from homeassistant.config_entries import ConfigEntryState
13 from homeassistant.core import HomeAssistant, ServiceCall
14 from homeassistant.exceptions import ServiceValidationError
15 from homeassistant.helpers import config_validation as cv, device_registry as dr
16 
17 from .const import DOMAIN
18 from .renault_vehicle import RenaultVehicleProxy
19 
20 if TYPE_CHECKING:
21  from . import RenaultConfigEntry
22 
23 LOGGER = logging.getLogger(__name__)
24 
25 ATTR_SCHEDULES = "schedules"
26 ATTR_TEMPERATURE = "temperature"
27 ATTR_VEHICLE = "vehicle"
28 ATTR_WHEN = "when"
29 
30 SERVICE_VEHICLE_SCHEMA = vol.Schema(
31  {
32  vol.Required(ATTR_VEHICLE): cv.string,
33  }
34 )
35 SERVICE_AC_START_SCHEMA = SERVICE_VEHICLE_SCHEMA.extend(
36  {
37  vol.Required(ATTR_TEMPERATURE): cv.positive_float,
38  vol.Optional(ATTR_WHEN): cv.datetime,
39  }
40 )
41 SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA = vol.Schema(
42  {
43  vol.Required("startTime"): cv.string,
44  vol.Required("duration"): cv.positive_int,
45  }
46 )
47 SERVICE_CHARGE_SET_SCHEDULE_SCHEMA = vol.Schema(
48  {
49  vol.Required("id"): cv.positive_int,
50  vol.Optional("activated"): cv.boolean,
51  vol.Optional("monday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
52  vol.Optional("tuesday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
53  vol.Optional("wednesday"): vol.Any(
54  None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA
55  ),
56  vol.Optional("thursday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
57  vol.Optional("friday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
58  vol.Optional("saturday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
59  vol.Optional("sunday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
60  }
61 )
62 SERVICE_CHARGE_SET_SCHEDULES_SCHEMA = SERVICE_VEHICLE_SCHEMA.extend(
63  {
64  vol.Required(ATTR_SCHEDULES): vol.All(
65  cv.ensure_list, [SERVICE_CHARGE_SET_SCHEDULE_SCHEMA]
66  ),
67  }
68 )
69 
70 SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA = vol.Schema(
71  {
72  vol.Required("readyAtTime"): cv.string,
73  }
74 )
75 
76 SERVICE_AC_SET_SCHEDULE_SCHEMA = vol.Schema(
77  {
78  vol.Required("id"): cv.positive_int,
79  vol.Optional("activated"): cv.boolean,
80  vol.Optional("monday"): vol.Any(None, SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA),
81  vol.Optional("tuesday"): vol.Any(None, SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA),
82  vol.Optional("wednesday"): vol.Any(None, SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA),
83  vol.Optional("thursday"): vol.Any(None, SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA),
84  vol.Optional("friday"): vol.Any(None, SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA),
85  vol.Optional("saturday"): vol.Any(None, SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA),
86  vol.Optional("sunday"): vol.Any(None, SERVICE_AC_SET_SCHEDULE_DAY_SCHEMA),
87  }
88 )
89 SERVICE_AC_SET_SCHEDULES_SCHEMA = SERVICE_VEHICLE_SCHEMA.extend(
90  {
91  vol.Required(ATTR_SCHEDULES): vol.All(
92  cv.ensure_list, [SERVICE_AC_SET_SCHEDULE_SCHEMA]
93  ),
94  }
95 )
96 
97 SERVICE_AC_CANCEL = "ac_cancel"
98 SERVICE_AC_START = "ac_start"
99 SERVICE_CHARGE_SET_SCHEDULES = "charge_set_schedules"
100 SERVICE_AC_SET_SCHEDULES = "ac_set_schedules"
101 SERVICES = [
102  SERVICE_AC_CANCEL,
103  SERVICE_AC_START,
104  SERVICE_CHARGE_SET_SCHEDULES,
105  SERVICE_AC_SET_SCHEDULES,
106 ]
107 
108 
109 def setup_services(hass: HomeAssistant) -> None:
110  """Register the Renault services."""
111 
112  async def ac_cancel(service_call: ServiceCall) -> None:
113  """Cancel A/C."""
114  proxy = get_vehicle_proxy(service_call.data)
115 
116  LOGGER.debug("A/C cancel attempt")
117  result = await proxy.set_ac_stop()
118  LOGGER.debug("A/C cancel result: %s", result)
119 
120  async def ac_start(service_call: ServiceCall) -> None:
121  """Start A/C."""
122  temperature: float = service_call.data[ATTR_TEMPERATURE]
123  when: datetime | None = service_call.data.get(ATTR_WHEN)
124  proxy = get_vehicle_proxy(service_call.data)
125 
126  LOGGER.debug("A/C start attempt: %s / %s", temperature, when)
127  result = await proxy.set_ac_start(temperature, when)
128  LOGGER.debug("A/C start result: %s", result.raw_data)
129 
130  async def charge_set_schedules(service_call: ServiceCall) -> None:
131  """Set charge schedules."""
132  schedules: list[dict[str, Any]] = service_call.data[ATTR_SCHEDULES]
133  proxy = get_vehicle_proxy(service_call.data)
134  charge_schedules = await proxy.get_charging_settings()
135  for schedule in schedules:
136  charge_schedules.update(schedule)
137 
138  if TYPE_CHECKING:
139  assert charge_schedules.schedules is not None
140  LOGGER.debug("Charge set schedules attempt: %s", schedules)
141  result = await proxy.set_charge_schedules(charge_schedules.schedules)
142 
143  LOGGER.debug("Charge set schedules result: %s", result)
144  LOGGER.debug(
145  "It may take some time before these changes are reflected in your vehicle"
146  )
147 
148  async def ac_set_schedules(service_call: ServiceCall) -> None:
149  """Set A/C schedules."""
150  schedules: list[dict[str, Any]] = service_call.data[ATTR_SCHEDULES]
151  proxy = get_vehicle_proxy(service_call.data)
152  hvac_schedules = await proxy.get_hvac_settings()
153 
154  for schedule in schedules:
155  hvac_schedules.update(schedule)
156 
157  if TYPE_CHECKING:
158  assert hvac_schedules.schedules is not None
159  LOGGER.debug("HVAC set schedules attempt: %s", schedules)
160  result = await proxy.set_hvac_schedules(hvac_schedules.schedules)
161 
162  LOGGER.debug("HVAC set schedules result: %s", result)
163  LOGGER.debug(
164  "It may take some time before these changes are reflected in your vehicle"
165  )
166 
167  def get_vehicle_proxy(service_call_data: Mapping) -> RenaultVehicleProxy:
168  """Get vehicle from service_call data."""
169  device_registry = dr.async_get(hass)
170  device_id = service_call_data[ATTR_VEHICLE]
171  device_entry = device_registry.async_get(device_id)
172  if device_entry is None:
174  translation_domain=DOMAIN,
175  translation_key="invalid_device_id",
176  translation_placeholders={"device_id": device_id},
177  )
178 
179  loaded_entries: list[RenaultConfigEntry] = [
180  entry
181  for entry in hass.config_entries.async_entries(DOMAIN)
182  if entry.state == ConfigEntryState.LOADED
183  and entry.entry_id in device_entry.config_entries
184  ]
185  for entry in loaded_entries:
186  for vin, vehicle in entry.runtime_data.vehicles.items():
187  if (DOMAIN, vin) in device_entry.identifiers:
188  return vehicle
190  translation_domain=DOMAIN,
191  translation_key="no_config_entry_for_device",
192  translation_placeholders={"device_id": device_entry.name or device_id},
193  )
194 
195  hass.services.async_register(
196  DOMAIN,
197  SERVICE_AC_CANCEL,
198  ac_cancel,
199  schema=SERVICE_VEHICLE_SCHEMA,
200  )
201  hass.services.async_register(
202  DOMAIN,
203  SERVICE_AC_START,
204  ac_start,
205  schema=SERVICE_AC_START_SCHEMA,
206  )
207  hass.services.async_register(
208  DOMAIN,
209  SERVICE_CHARGE_SET_SCHEDULES,
210  charge_set_schedules,
211  schema=SERVICE_CHARGE_SET_SCHEDULES_SCHEMA,
212  )
213  hass.services.async_register(
214  DOMAIN,
215  SERVICE_AC_SET_SCHEDULES,
216  ac_set_schedules,
217  schema=SERVICE_AC_SET_SCHEDULES_SCHEMA,
218  )
None setup_services(HomeAssistant hass)
Definition: services.py:109