Skip to content

Commit 7cc6e28

Browse files
hesselonlinefrenck
authored andcommitted
Wallbox fix too many requests by API (home-assistant#147197)
1 parent 802fcab commit 7cc6e28

File tree

13 files changed

+696
-458
lines changed

13 files changed

+696
-458
lines changed

homeassistant/components/wallbox/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from enum import StrEnum
44

55
DOMAIN = "wallbox"
6-
UPDATE_INTERVAL = 30
6+
UPDATE_INTERVAL = 60
77

88
BIDIRECTIONAL_MODEL_PREFIXES = ["QS"]
99

homeassistant/components/wallbox/coordinator.py

Lines changed: 120 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ def require_authentication(
9090
except requests.exceptions.HTTPError as wallbox_connection_error:
9191
if wallbox_connection_error.response.status_code == HTTPStatus.FORBIDDEN:
9292
raise ConfigEntryAuthFailed from wallbox_connection_error
93-
raise ConnectionError from wallbox_connection_error
93+
raise HomeAssistantError(
94+
translation_domain=DOMAIN, translation_key="api_failed"
95+
) from wallbox_connection_error
9496

9597
return require_authentication
9698

@@ -137,56 +139,65 @@ def authenticate(self) -> None:
137139
@_require_authentication
138140
def _get_data(self) -> dict[str, Any]:
139141
"""Get new sensor data for Wallbox component."""
140-
data: dict[str, Any] = self._wallbox.getChargerStatus(self._station)
141-
data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][
142-
CHARGER_MAX_CHARGING_CURRENT_KEY
143-
]
144-
data[CHARGER_LOCKED_UNLOCKED_KEY] = data[CHARGER_DATA_KEY][
145-
CHARGER_LOCKED_UNLOCKED_KEY
146-
]
147-
data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][
148-
CHARGER_ENERGY_PRICE_KEY
149-
]
150-
# Only show max_icp_current if power_boost is available in the wallbox unit:
151-
if (
152-
data[CHARGER_DATA_KEY].get(CHARGER_MAX_ICP_CURRENT_KEY, 0) > 0
153-
and CHARGER_POWER_BOOST_KEY
154-
in data[CHARGER_DATA_KEY][CHARGER_PLAN_KEY][CHARGER_FEATURES_KEY]
155-
):
156-
data[CHARGER_MAX_ICP_CURRENT_KEY] = data[CHARGER_DATA_KEY][
157-
CHARGER_MAX_ICP_CURRENT_KEY
142+
try:
143+
data: dict[str, Any] = self._wallbox.getChargerStatus(self._station)
144+
data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][
145+
CHARGER_MAX_CHARGING_CURRENT_KEY
158146
]
159-
160-
data[CHARGER_CURRENCY_KEY] = (
161-
f"{data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][CODE_KEY]}/kWh"
162-
)
163-
164-
data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get(
165-
data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN
166-
)
167-
168-
# Set current solar charging mode
169-
eco_smart_enabled = (
170-
data[CHARGER_DATA_KEY]
171-
.get(CHARGER_ECO_SMART_KEY, {})
172-
.get(CHARGER_ECO_SMART_STATUS_KEY)
173-
)
174-
175-
eco_smart_mode = (
176-
data[CHARGER_DATA_KEY]
177-
.get(CHARGER_ECO_SMART_KEY, {})
178-
.get(CHARGER_ECO_SMART_MODE_KEY)
179-
)
180-
if eco_smart_mode is None:
181-
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.DISABLED
182-
elif eco_smart_enabled is False:
183-
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.OFF
184-
elif eco_smart_mode == 0:
185-
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.ECO_MODE
186-
elif eco_smart_mode == 1:
187-
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.FULL_SOLAR
188-
189-
return data
147+
data[CHARGER_LOCKED_UNLOCKED_KEY] = data[CHARGER_DATA_KEY][
148+
CHARGER_LOCKED_UNLOCKED_KEY
149+
]
150+
data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][
151+
CHARGER_ENERGY_PRICE_KEY
152+
]
153+
# Only show max_icp_current if power_boost is available in the wallbox unit:
154+
if (
155+
data[CHARGER_DATA_KEY].get(CHARGER_MAX_ICP_CURRENT_KEY, 0) > 0
156+
and CHARGER_POWER_BOOST_KEY
157+
in data[CHARGER_DATA_KEY][CHARGER_PLAN_KEY][CHARGER_FEATURES_KEY]
158+
):
159+
data[CHARGER_MAX_ICP_CURRENT_KEY] = data[CHARGER_DATA_KEY][
160+
CHARGER_MAX_ICP_CURRENT_KEY
161+
]
162+
163+
data[CHARGER_CURRENCY_KEY] = (
164+
f"{data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][CODE_KEY]}/kWh"
165+
)
166+
167+
data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get(
168+
data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN
169+
)
170+
171+
# Set current solar charging mode
172+
eco_smart_enabled = (
173+
data[CHARGER_DATA_KEY]
174+
.get(CHARGER_ECO_SMART_KEY, {})
175+
.get(CHARGER_ECO_SMART_STATUS_KEY)
176+
)
177+
178+
eco_smart_mode = (
179+
data[CHARGER_DATA_KEY]
180+
.get(CHARGER_ECO_SMART_KEY, {})
181+
.get(CHARGER_ECO_SMART_MODE_KEY)
182+
)
183+
if eco_smart_mode is None:
184+
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.DISABLED
185+
elif eco_smart_enabled is False:
186+
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.OFF
187+
elif eco_smart_mode == 0:
188+
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.ECO_MODE
189+
elif eco_smart_mode == 1:
190+
data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.FULL_SOLAR
191+
192+
return data # noqa: TRY300
193+
except requests.exceptions.HTTPError as wallbox_connection_error:
194+
if wallbox_connection_error.response.status_code == 429:
195+
raise HomeAssistantError(
196+
translation_domain=DOMAIN, translation_key="too_many_requests"
197+
) from wallbox_connection_error
198+
raise HomeAssistantError(
199+
translation_domain=DOMAIN, translation_key="api_failed"
200+
) from wallbox_connection_error
190201

191202
async def _async_update_data(self) -> dict[str, Any]:
192203
"""Get new sensor data for Wallbox component."""
@@ -200,7 +211,13 @@ def _set_charging_current(self, charging_current: float) -> None:
200211
except requests.exceptions.HTTPError as wallbox_connection_error:
201212
if wallbox_connection_error.response.status_code == 403:
202213
raise InvalidAuth from wallbox_connection_error
203-
raise
214+
if wallbox_connection_error.response.status_code == 429:
215+
raise HomeAssistantError(
216+
translation_domain=DOMAIN, translation_key="too_many_requests"
217+
) from wallbox_connection_error
218+
raise HomeAssistantError(
219+
translation_domain=DOMAIN, translation_key="api_failed"
220+
) from wallbox_connection_error
204221

205222
async def async_set_charging_current(self, charging_current: float) -> None:
206223
"""Set maximum charging current for Wallbox."""
@@ -217,7 +234,13 @@ def _set_icp_current(self, icp_current: float) -> None:
217234
except requests.exceptions.HTTPError as wallbox_connection_error:
218235
if wallbox_connection_error.response.status_code == 403:
219236
raise InvalidAuth from wallbox_connection_error
220-
raise
237+
if wallbox_connection_error.response.status_code == 429:
238+
raise HomeAssistantError(
239+
translation_domain=DOMAIN, translation_key="too_many_requests"
240+
) from wallbox_connection_error
241+
raise HomeAssistantError(
242+
translation_domain=DOMAIN, translation_key="api_failed"
243+
) from wallbox_connection_error
221244

222245
async def async_set_icp_current(self, icp_current: float) -> None:
223246
"""Set maximum icp current for Wallbox."""
@@ -227,8 +250,16 @@ async def async_set_icp_current(self, icp_current: float) -> None:
227250
@_require_authentication
228251
def _set_energy_cost(self, energy_cost: float) -> None:
229252
"""Set energy cost for Wallbox."""
230-
231-
self._wallbox.setEnergyCost(self._station, energy_cost)
253+
try:
254+
self._wallbox.setEnergyCost(self._station, energy_cost)
255+
except requests.exceptions.HTTPError as wallbox_connection_error:
256+
if wallbox_connection_error.response.status_code == 429:
257+
raise HomeAssistantError(
258+
translation_domain=DOMAIN, translation_key="too_many_requests"
259+
) from wallbox_connection_error
260+
raise HomeAssistantError(
261+
translation_domain=DOMAIN, translation_key="api_failed"
262+
) from wallbox_connection_error
232263

233264
async def async_set_energy_cost(self, energy_cost: float) -> None:
234265
"""Set energy cost for Wallbox."""
@@ -246,7 +277,13 @@ def _set_lock_unlock(self, lock: bool) -> None:
246277
except requests.exceptions.HTTPError as wallbox_connection_error:
247278
if wallbox_connection_error.response.status_code == 403:
248279
raise InvalidAuth from wallbox_connection_error
249-
raise
280+
if wallbox_connection_error.response.status_code == 429:
281+
raise HomeAssistantError(
282+
translation_domain=DOMAIN, translation_key="too_many_requests"
283+
) from wallbox_connection_error
284+
raise HomeAssistantError(
285+
translation_domain=DOMAIN, translation_key="api_failed"
286+
) from wallbox_connection_error
250287

251288
async def async_set_lock_unlock(self, lock: bool) -> None:
252289
"""Set wallbox to locked or unlocked."""
@@ -256,11 +293,19 @@ async def async_set_lock_unlock(self, lock: bool) -> None:
256293
@_require_authentication
257294
def _pause_charger(self, pause: bool) -> None:
258295
"""Set wallbox to pause or resume."""
259-
260-
if pause:
261-
self._wallbox.pauseChargingSession(self._station)
262-
else:
263-
self._wallbox.resumeChargingSession(self._station)
296+
try:
297+
if pause:
298+
self._wallbox.pauseChargingSession(self._station)
299+
else:
300+
self._wallbox.resumeChargingSession(self._station)
301+
except requests.exceptions.HTTPError as wallbox_connection_error:
302+
if wallbox_connection_error.response.status_code == 429:
303+
raise HomeAssistantError(
304+
translation_domain=DOMAIN, translation_key="too_many_requests"
305+
) from wallbox_connection_error
306+
raise HomeAssistantError(
307+
translation_domain=DOMAIN, translation_key="api_failed"
308+
) from wallbox_connection_error
264309

265310
async def async_pause_charger(self, pause: bool) -> None:
266311
"""Set wallbox to pause or resume."""
@@ -270,13 +315,21 @@ async def async_pause_charger(self, pause: bool) -> None:
270315
@_require_authentication
271316
def _set_eco_smart(self, option: str) -> None:
272317
"""Set wallbox solar charging mode."""
273-
274-
if option == EcoSmartMode.ECO_MODE:
275-
self._wallbox.enableEcoSmart(self._station, 0)
276-
elif option == EcoSmartMode.FULL_SOLAR:
277-
self._wallbox.enableEcoSmart(self._station, 1)
278-
else:
279-
self._wallbox.disableEcoSmart(self._station)
318+
try:
319+
if option == EcoSmartMode.ECO_MODE:
320+
self._wallbox.enableEcoSmart(self._station, 0)
321+
elif option == EcoSmartMode.FULL_SOLAR:
322+
self._wallbox.enableEcoSmart(self._station, 1)
323+
else:
324+
self._wallbox.disableEcoSmart(self._station)
325+
except requests.exceptions.HTTPError as wallbox_connection_error:
326+
if wallbox_connection_error.response.status_code == 429:
327+
raise HomeAssistantError(
328+
translation_domain=DOMAIN, translation_key="too_many_requests"
329+
) from wallbox_connection_error
330+
raise HomeAssistantError(
331+
translation_domain=DOMAIN, translation_key="api_failed"
332+
) from wallbox_connection_error
280333

281334
async def async_set_eco_smart(self, option: str) -> None:
282335
"""Set wallbox solar charging mode."""

homeassistant/components/wallbox/lock.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from homeassistant.components.lock import LockEntity, LockEntityDescription
88
from homeassistant.config_entries import ConfigEntry
99
from homeassistant.core import HomeAssistant
10-
from homeassistant.exceptions import PlatformNotReady
10+
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
1111
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1212

1313
from .const import (
@@ -41,7 +41,7 @@ async def async_setup_entry(
4141
)
4242
except InvalidAuth:
4343
return
44-
except ConnectionError as exc:
44+
except HomeAssistantError as exc:
4545
raise PlatformNotReady from exc
4646

4747
async_add_entities(

homeassistant/components/wallbox/number.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from homeassistant.components.number import NumberEntity, NumberEntityDescription
1313
from homeassistant.config_entries import ConfigEntry
1414
from homeassistant.core import HomeAssistant
15-
from homeassistant.exceptions import PlatformNotReady
15+
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
1616
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1717

1818
from .const import (
@@ -93,7 +93,7 @@ async def async_setup_entry(
9393
)
9494
except InvalidAuth:
9595
return
96-
except ConnectionError as exc:
96+
except HomeAssistantError as exc:
9797
raise PlatformNotReady from exc
9898

9999
async_add_entities(

homeassistant/components/wallbox/sensor.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44

55
from dataclasses import dataclass
6-
import logging
76
from typing import cast
87

98
from homeassistant.components.sensor import (
@@ -49,11 +48,6 @@
4948
from .coordinator import WallboxCoordinator
5049
from .entity import WallboxEntity
5150

52-
CHARGER_STATION = "station"
53-
UPDATE_INTERVAL = 30
54-
55-
_LOGGER = logging.getLogger(__name__)
56-
5751

5852
@dataclass(frozen=True)
5953
class WallboxSensorEntityDescription(SensorEntityDescription):

homeassistant/components/wallbox/strings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@
112112
"exceptions": {
113113
"api_failed": {
114114
"message": "Error communicating with Wallbox API"
115+
},
116+
"too_many_requests": {
117+
"message": "Error communicating with Wallbox API, too many requests"
115118
}
116119
}
117120
}

tests/components/wallbox/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@
162162
http_404_error = requests.exceptions.HTTPError()
163163
http_404_error.response = requests.Response()
164164
http_404_error.response.status_code = HTTPStatus.NOT_FOUND
165+
http_429_error = requests.exceptions.HTTPError()
166+
http_429_error.response = requests.Response()
167+
http_429_error.response.status_code = HTTPStatus.TOO_MANY_REQUESTS
165168

166169
authorisation_response = {
167170
"data": {
@@ -192,6 +195,24 @@
192195
}
193196
}
194197

198+
invalid_reauth_response = {
199+
"jwt": "fakekeyhere",
200+
"refresh_token": "refresh_fakekeyhere",
201+
"user_id": 12345,
202+
"ttl": 145656758,
203+
"refresh_token_ttl": 145756758,
204+
"error": False,
205+
"status": 200,
206+
}
207+
208+
http_403_error = requests.exceptions.HTTPError()
209+
http_403_error.response = requests.Response()
210+
http_403_error.response.status_code = HTTPStatus.FORBIDDEN
211+
212+
http_404_error = requests.exceptions.HTTPError()
213+
http_404_error.response = requests.Response()
214+
http_404_error.response.status_code = HTTPStatus.NOT_FOUND
215+
195216

196217
async def setup_integration(hass: HomeAssistant, entry: MockConfigEntry) -> None:
197218
"""Test wallbox sensor class setup."""

0 commit comments

Comments
 (0)