Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """DataUpdateCoordinator for the wallbox integration."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from datetime import timedelta
7 from http import HTTPStatus
8 import logging
9 from typing import Any, Concatenate
10 
11 import requests
12 from wallbox import Wallbox
13 
14 from homeassistant.core import HomeAssistant
15 from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError
16 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
17 
18 from .const import (
19  CHARGER_CURRENCY_KEY,
20  CHARGER_DATA_KEY,
21  CHARGER_ENERGY_PRICE_KEY,
22  CHARGER_FEATURES_KEY,
23  CHARGER_LOCKED_UNLOCKED_KEY,
24  CHARGER_MAX_CHARGING_CURRENT_KEY,
25  CHARGER_MAX_ICP_CURRENT_KEY,
26  CHARGER_PLAN_KEY,
27  CHARGER_POWER_BOOST_KEY,
28  CHARGER_STATUS_DESCRIPTION_KEY,
29  CHARGER_STATUS_ID_KEY,
30  CODE_KEY,
31  DOMAIN,
32  UPDATE_INTERVAL,
33  ChargerStatus,
34 )
35 
36 _LOGGER = logging.getLogger(__name__)
37 
38 # Translation of StatusId based on Wallbox portal code:
39 # https://my.wallbox.com/src/utilities/charger/chargerStatuses.js
40 CHARGER_STATUS: dict[int, ChargerStatus] = {
41  0: ChargerStatus.DISCONNECTED,
42  14: ChargerStatus.ERROR,
43  15: ChargerStatus.ERROR,
44  161: ChargerStatus.READY,
45  162: ChargerStatus.READY,
46  163: ChargerStatus.DISCONNECTED,
47  164: ChargerStatus.WAITING,
48  165: ChargerStatus.LOCKED,
49  166: ChargerStatus.UPDATING,
50  177: ChargerStatus.SCHEDULED,
51  178: ChargerStatus.PAUSED,
52  179: ChargerStatus.SCHEDULED,
53  180: ChargerStatus.WAITING_FOR_CAR,
54  181: ChargerStatus.WAITING_FOR_CAR,
55  182: ChargerStatus.PAUSED,
56  183: ChargerStatus.WAITING_IN_QUEUE_POWER_SHARING,
57  184: ChargerStatus.WAITING_IN_QUEUE_POWER_SHARING,
58  185: ChargerStatus.WAITING_IN_QUEUE_POWER_BOOST,
59  186: ChargerStatus.WAITING_IN_QUEUE_POWER_BOOST,
60  187: ChargerStatus.WAITING_MID_FAILED,
61  188: ChargerStatus.WAITING_MID_SAFETY,
62  189: ChargerStatus.WAITING_IN_QUEUE_ECO_SMART,
63  193: ChargerStatus.CHARGING,
64  194: ChargerStatus.CHARGING,
65  195: ChargerStatus.CHARGING,
66  196: ChargerStatus.DISCHARGING,
67  209: ChargerStatus.LOCKED,
68  210: ChargerStatus.LOCKED_CAR_CONNECTED,
69 }
70 
71 
72 def _require_authentication[_WallboxCoordinatorT: WallboxCoordinator, **_P](
73  func: Callable[Concatenate[_WallboxCoordinatorT, _P], Any],
74 ) -> Callable[Concatenate[_WallboxCoordinatorT, _P], Any]:
75  """Authenticate with decorator using Wallbox API."""
76 
77  def require_authentication(
78  self: _WallboxCoordinatorT, *args: _P.args, **kwargs: _P.kwargs
79  ) -> Any:
80  """Authenticate using Wallbox API."""
81  try:
82  self.authenticate()
83  return func(self, *args, **kwargs)
84  except requests.exceptions.HTTPError as wallbox_connection_error:
85  if wallbox_connection_error.response.status_code == HTTPStatus.FORBIDDEN:
86  raise ConfigEntryAuthFailed from wallbox_connection_error
87  raise ConnectionError from wallbox_connection_error
88 
89  return require_authentication
90 
91 
92 def _validate(wallbox: Wallbox) -> None:
93  """Authenticate using Wallbox API."""
94  try:
95  wallbox.authenticate()
96  except requests.exceptions.HTTPError as wallbox_connection_error:
97  if wallbox_connection_error.response.status_code == 403:
98  raise InvalidAuth from wallbox_connection_error
99  raise ConnectionError from wallbox_connection_error
100 
101 
102 async def async_validate_input(hass: HomeAssistant, wallbox: Wallbox) -> None:
103  """Get new sensor data for Wallbox component."""
104  await hass.async_add_executor_job(_validate, wallbox)
105 
106 
108  """Wallbox Coordinator class."""
109 
110  def __init__(self, station: str, wallbox: Wallbox, hass: HomeAssistant) -> None:
111  """Initialize."""
112  self._station_station = station
113  self._wallbox_wallbox = wallbox
114 
115  super().__init__(
116  hass,
117  _LOGGER,
118  name=DOMAIN,
119  update_interval=timedelta(seconds=UPDATE_INTERVAL),
120  )
121 
122  def authenticate(self) -> None:
123  """Authenticate using Wallbox API."""
124  self._wallbox_wallbox.authenticate()
125 
126  @_require_authentication
127  def _get_data(self) -> dict[str, Any]:
128  """Get new sensor data for Wallbox component."""
129  data: dict[str, Any] = self._wallbox_wallbox.getChargerStatus(self._station_station)
130  data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][
131  CHARGER_MAX_CHARGING_CURRENT_KEY
132  ]
133  data[CHARGER_LOCKED_UNLOCKED_KEY] = data[CHARGER_DATA_KEY][
134  CHARGER_LOCKED_UNLOCKED_KEY
135  ]
136  data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][
137  CHARGER_ENERGY_PRICE_KEY
138  ]
139  # Only show max_icp_current if power_boost is available in the wallbox unit:
140  if (
141  data[CHARGER_DATA_KEY].get(CHARGER_MAX_ICP_CURRENT_KEY, 0) > 0
142  and CHARGER_POWER_BOOST_KEY
143  in data[CHARGER_DATA_KEY][CHARGER_PLAN_KEY][CHARGER_FEATURES_KEY]
144  ):
145  data[CHARGER_MAX_ICP_CURRENT_KEY] = data[CHARGER_DATA_KEY][
146  CHARGER_MAX_ICP_CURRENT_KEY
147  ]
148 
149  data[CHARGER_CURRENCY_KEY] = (
150  f"{data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][CODE_KEY]}/kWh"
151  )
152 
153  data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get(
154  data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN
155  )
156  return data
157 
158  async def _async_update_data(self) -> dict[str, Any]:
159  """Get new sensor data for Wallbox component."""
160  return await self.hasshass.async_add_executor_job(self._get_data_get_data)
161 
162  @_require_authentication
163  def _set_charging_current(self, charging_current: float) -> None:
164  """Set maximum charging current for Wallbox."""
165  try:
166  self._wallbox_wallbox.setMaxChargingCurrent(self._station_station, charging_current)
167  except requests.exceptions.HTTPError as wallbox_connection_error:
168  if wallbox_connection_error.response.status_code == 403:
169  raise InvalidAuth from wallbox_connection_error
170  raise
171 
172  async def async_set_charging_current(self, charging_current: float) -> None:
173  """Set maximum charging current for Wallbox."""
174  await self.hasshass.async_add_executor_job(
175  self._set_charging_current_set_charging_current, charging_current
176  )
177  await self.async_request_refreshasync_request_refresh()
178 
179  @_require_authentication
180  def _set_icp_current(self, icp_current: float) -> None:
181  """Set maximum icp current for Wallbox."""
182  try:
183  self._wallbox_wallbox.setIcpMaxCurrent(self._station_station, icp_current)
184  except requests.exceptions.HTTPError as wallbox_connection_error:
185  if wallbox_connection_error.response.status_code == 403:
186  raise InvalidAuth from wallbox_connection_error
187  raise
188 
189  async def async_set_icp_current(self, icp_current: float) -> None:
190  """Set maximum icp current for Wallbox."""
191  await self.hasshass.async_add_executor_job(self._set_icp_current_set_icp_current, icp_current)
192  await self.async_request_refreshasync_request_refresh()
193 
194  @_require_authentication
195  def _set_energy_cost(self, energy_cost: float) -> None:
196  """Set energy cost for Wallbox."""
197 
198  self._wallbox_wallbox.setEnergyCost(self._station_station, energy_cost)
199 
200  async def async_set_energy_cost(self, energy_cost: float) -> None:
201  """Set energy cost for Wallbox."""
202  await self.hasshass.async_add_executor_job(self._set_energy_cost_set_energy_cost, energy_cost)
203  await self.async_request_refreshasync_request_refresh()
204 
205  @_require_authentication
206  def _set_lock_unlock(self, lock: bool) -> None:
207  """Set wallbox to locked or unlocked."""
208  try:
209  if lock:
210  self._wallbox_wallbox.lockCharger(self._station_station)
211  else:
212  self._wallbox_wallbox.unlockCharger(self._station_station)
213  except requests.exceptions.HTTPError as wallbox_connection_error:
214  if wallbox_connection_error.response.status_code == 403:
215  raise InvalidAuth from wallbox_connection_error
216  raise
217 
218  async def async_set_lock_unlock(self, lock: bool) -> None:
219  """Set wallbox to locked or unlocked."""
220  await self.hasshass.async_add_executor_job(self._set_lock_unlock_set_lock_unlock, lock)
221  await self.async_request_refreshasync_request_refresh()
222 
223  @_require_authentication
224  def _pause_charger(self, pause: bool) -> None:
225  """Set wallbox to pause or resume."""
226 
227  if pause:
228  self._wallbox_wallbox.pauseChargingSession(self._station_station)
229  else:
230  self._wallbox_wallbox.resumeChargingSession(self._station_station)
231 
232  async def async_pause_charger(self, pause: bool) -> None:
233  """Set wallbox to pause or resume."""
234  await self.hasshass.async_add_executor_job(self._pause_charger_pause_charger, pause)
235  await self.async_request_refreshasync_request_refresh()
236 
237 
239  """Error to indicate there is invalid auth."""
None async_set_charging_current(self, float charging_current)
Definition: coordinator.py:172
None __init__(self, str station, Wallbox wallbox, HomeAssistant hass)
Definition: coordinator.py:110
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_validate_input(HomeAssistant hass, Wallbox wallbox)
Definition: coordinator.py:102