Skip to content

Commit 90cbe27

Browse files
authored
Wallbox Integration, Reduce API impact by limiting the amount of API calls made (home-assistant#147618)
1 parent 511b739 commit 90cbe27

File tree

8 files changed

+168
-86
lines changed

8 files changed

+168
-86
lines changed

homeassistant/components/wallbox/const.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
CHARGER_CURRENT_VERSION_KEY = "currentVersion"
2323
CHARGER_CURRENCY_KEY = "currency"
2424
CHARGER_DATA_KEY = "config_data"
25+
CHARGER_DATA_POST_L1_KEY = "data"
26+
CHARGER_DATA_POST_L2_KEY = "chargerData"
2527
CHARGER_DEPOT_PRICE_KEY = "depot_price"
2628
CHARGER_ENERGY_PRICE_KEY = "energy_price"
2729
CHARGER_FEATURES_KEY = "features"
@@ -32,7 +34,9 @@
3234
CHARGER_SOFTWARE_KEY = "software"
3335
CHARGER_MAX_AVAILABLE_POWER_KEY = "max_available_power"
3436
CHARGER_MAX_CHARGING_CURRENT_KEY = "max_charging_current"
37+
CHARGER_MAX_CHARGING_CURRENT_POST_KEY = "maxChargingCurrent"
3538
CHARGER_MAX_ICP_CURRENT_KEY = "icp_max_current"
39+
CHARGER_MAX_ICP_CURRENT_POST_KEY = "maxAvailableCurrent"
3640
CHARGER_PAUSE_RESUME_KEY = "paused"
3741
CHARGER_LOCKED_UNLOCKED_KEY = "locked"
3842
CHARGER_NAME_KEY = "name"

homeassistant/components/wallbox/coordinator.py

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,21 @@
1414
from homeassistant.config_entries import ConfigEntry
1515
from homeassistant.core import HomeAssistant
1616
from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError
17-
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
17+
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
1818

1919
from .const import (
2020
CHARGER_CURRENCY_KEY,
2121
CHARGER_DATA_KEY,
22+
CHARGER_DATA_POST_L1_KEY,
23+
CHARGER_DATA_POST_L2_KEY,
2224
CHARGER_ECO_SMART_KEY,
2325
CHARGER_ECO_SMART_MODE_KEY,
2426
CHARGER_ECO_SMART_STATUS_KEY,
2527
CHARGER_ENERGY_PRICE_KEY,
2628
CHARGER_FEATURES_KEY,
2729
CHARGER_LOCKED_UNLOCKED_KEY,
2830
CHARGER_MAX_CHARGING_CURRENT_KEY,
31+
CHARGER_MAX_CHARGING_CURRENT_POST_KEY,
2932
CHARGER_MAX_ICP_CURRENT_KEY,
3033
CHARGER_PLAN_KEY,
3134
CHARGER_POWER_BOOST_KEY,
@@ -192,10 +195,10 @@ def _get_data(self) -> dict[str, Any]:
192195
return data # noqa: TRY300
193196
except requests.exceptions.HTTPError as wallbox_connection_error:
194197
if wallbox_connection_error.response.status_code == 429:
195-
raise HomeAssistantError(
198+
raise UpdateFailed(
196199
translation_domain=DOMAIN, translation_key="too_many_requests"
197200
) from wallbox_connection_error
198-
raise HomeAssistantError(
201+
raise UpdateFailed(
199202
translation_domain=DOMAIN, translation_key="api_failed"
200203
) from wallbox_connection_error
201204

@@ -204,10 +207,19 @@ async def _async_update_data(self) -> dict[str, Any]:
204207
return await self.hass.async_add_executor_job(self._get_data)
205208

206209
@_require_authentication
207-
def _set_charging_current(self, charging_current: float) -> None:
210+
def _set_charging_current(
211+
self, charging_current: float
212+
) -> dict[str, dict[str, dict[str, Any]]]:
208213
"""Set maximum charging current for Wallbox."""
209214
try:
210-
self._wallbox.setMaxChargingCurrent(self._station, charging_current)
215+
result = self._wallbox.setMaxChargingCurrent(
216+
self._station, charging_current
217+
)
218+
data = self.data
219+
data[CHARGER_MAX_CHARGING_CURRENT_KEY] = result[CHARGER_DATA_POST_L1_KEY][
220+
CHARGER_DATA_POST_L2_KEY
221+
][CHARGER_MAX_CHARGING_CURRENT_POST_KEY]
222+
return data # noqa: TRY300
211223
except requests.exceptions.HTTPError as wallbox_connection_error:
212224
if wallbox_connection_error.response.status_code == 403:
213225
raise InvalidAuth from wallbox_connection_error
@@ -221,16 +233,19 @@ def _set_charging_current(self, charging_current: float) -> None:
221233

222234
async def async_set_charging_current(self, charging_current: float) -> None:
223235
"""Set maximum charging current for Wallbox."""
224-
await self.hass.async_add_executor_job(
236+
data = await self.hass.async_add_executor_job(
225237
self._set_charging_current, charging_current
226238
)
227-
await self.async_request_refresh()
239+
self.async_set_updated_data(data)
228240

229241
@_require_authentication
230-
def _set_icp_current(self, icp_current: float) -> None:
242+
def _set_icp_current(self, icp_current: float) -> dict[str, Any]:
231243
"""Set maximum icp current for Wallbox."""
232244
try:
233-
self._wallbox.setIcpMaxCurrent(self._station, icp_current)
245+
result = self._wallbox.setIcpMaxCurrent(self._station, icp_current)
246+
data = self.data
247+
data[CHARGER_MAX_ICP_CURRENT_KEY] = result[CHARGER_MAX_ICP_CURRENT_KEY]
248+
return data # noqa: TRY300
234249
except requests.exceptions.HTTPError as wallbox_connection_error:
235250
if wallbox_connection_error.response.status_code == 403:
236251
raise InvalidAuth from wallbox_connection_error
@@ -244,14 +259,19 @@ def _set_icp_current(self, icp_current: float) -> None:
244259

245260
async def async_set_icp_current(self, icp_current: float) -> None:
246261
"""Set maximum icp current for Wallbox."""
247-
await self.hass.async_add_executor_job(self._set_icp_current, icp_current)
248-
await self.async_request_refresh()
262+
data = await self.hass.async_add_executor_job(
263+
self._set_icp_current, icp_current
264+
)
265+
self.async_set_updated_data(data)
249266

250267
@_require_authentication
251-
def _set_energy_cost(self, energy_cost: float) -> None:
268+
def _set_energy_cost(self, energy_cost: float) -> dict[str, Any]:
252269
"""Set energy cost for Wallbox."""
253270
try:
254-
self._wallbox.setEnergyCost(self._station, energy_cost)
271+
result = self._wallbox.setEnergyCost(self._station, energy_cost)
272+
data = self.data
273+
data[CHARGER_ENERGY_PRICE_KEY] = result[CHARGER_ENERGY_PRICE_KEY]
274+
return data # noqa: TRY300
255275
except requests.exceptions.HTTPError as wallbox_connection_error:
256276
if wallbox_connection_error.response.status_code == 429:
257277
raise HomeAssistantError(
@@ -263,17 +283,24 @@ def _set_energy_cost(self, energy_cost: float) -> None:
263283

264284
async def async_set_energy_cost(self, energy_cost: float) -> None:
265285
"""Set energy cost for Wallbox."""
266-
await self.hass.async_add_executor_job(self._set_energy_cost, energy_cost)
267-
await self.async_request_refresh()
286+
data = await self.hass.async_add_executor_job(
287+
self._set_energy_cost, energy_cost
288+
)
289+
self.async_set_updated_data(data)
268290

269291
@_require_authentication
270-
def _set_lock_unlock(self, lock: bool) -> None:
292+
def _set_lock_unlock(self, lock: bool) -> dict[str, dict[str, dict[str, Any]]]:
271293
"""Set wallbox to locked or unlocked."""
272294
try:
273295
if lock:
274-
self._wallbox.lockCharger(self._station)
296+
result = self._wallbox.lockCharger(self._station)
275297
else:
276-
self._wallbox.unlockCharger(self._station)
298+
result = self._wallbox.unlockCharger(self._station)
299+
data = self.data
300+
data[CHARGER_LOCKED_UNLOCKED_KEY] = result[CHARGER_DATA_POST_L1_KEY][
301+
CHARGER_DATA_POST_L2_KEY
302+
][CHARGER_LOCKED_UNLOCKED_KEY]
303+
return data # noqa: TRY300
277304
except requests.exceptions.HTTPError as wallbox_connection_error:
278305
if wallbox_connection_error.response.status_code == 403:
279306
raise InvalidAuth from wallbox_connection_error
@@ -287,8 +314,8 @@ def _set_lock_unlock(self, lock: bool) -> None:
287314

288315
async def async_set_lock_unlock(self, lock: bool) -> None:
289316
"""Set wallbox to locked or unlocked."""
290-
await self.hass.async_add_executor_job(self._set_lock_unlock, lock)
291-
await self.async_request_refresh()
317+
data = await self.hass.async_add_executor_job(self._set_lock_unlock, lock)
318+
self.async_set_updated_data(data)
292319

293320
@_require_authentication
294321
def _pause_charger(self, pause: bool) -> None:

homeassistant/components/wallbox/lock.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
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 HomeAssistantError, PlatformNotReady
1110
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1211

1312
from .const import (
@@ -16,7 +15,7 @@
1615
CHARGER_SERIAL_NUMBER_KEY,
1716
DOMAIN,
1817
)
19-
from .coordinator import InvalidAuth, WallboxCoordinator
18+
from .coordinator import WallboxCoordinator
2019
from .entity import WallboxEntity
2120

2221
LOCK_TYPES: dict[str, LockEntityDescription] = {
@@ -34,16 +33,6 @@ async def async_setup_entry(
3433
) -> None:
3534
"""Create wallbox lock entities in HASS."""
3635
coordinator: WallboxCoordinator = hass.data[DOMAIN][entry.entry_id]
37-
# Check if the user is authorized to lock, if so, add lock component
38-
try:
39-
await coordinator.async_set_lock_unlock(
40-
coordinator.data[CHARGER_LOCKED_UNLOCKED_KEY]
41-
)
42-
except InvalidAuth:
43-
return
44-
except HomeAssistantError as exc:
45-
raise PlatformNotReady from exc
46-
4736
async_add_entities(
4837
WallboxLock(coordinator, description)
4938
for ent in coordinator.data

homeassistant/components/wallbox/number.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
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 HomeAssistantError, PlatformNotReady
1615
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1716

1817
from .const import (
@@ -26,7 +25,7 @@
2625
CHARGER_SERIAL_NUMBER_KEY,
2726
DOMAIN,
2827
)
29-
from .coordinator import InvalidAuth, WallboxCoordinator
28+
from .coordinator import WallboxCoordinator
3029
from .entity import WallboxEntity
3130

3231

@@ -86,16 +85,6 @@ async def async_setup_entry(
8685
) -> None:
8786
"""Create wallbox number entities in HASS."""
8887
coordinator: WallboxCoordinator = hass.data[DOMAIN][entry.entry_id]
89-
# Check if the user has sufficient rights to change values, if so, add number component:
90-
try:
91-
await coordinator.async_set_charging_current(
92-
coordinator.data[CHARGER_MAX_CHARGING_CURRENT_KEY]
93-
)
94-
except InvalidAuth:
95-
return
96-
except HomeAssistantError as exc:
97-
raise PlatformNotReady from exc
98-
9988
async_add_entities(
10089
WallboxNumber(coordinator, entry, description)
10190
for ent in coordinator.data

tests/components/wallbox/test_lock.py

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
from . import (
1414
authorisation_response,
15+
http_403_error,
16+
http_404_error,
1517
http_429_error,
1618
setup_integration,
17-
setup_integration_platform_not_ready,
18-
setup_integration_read_only,
1919
)
2020
from .const import MOCK_LOCK_ENTITY_ID
2121

@@ -38,11 +38,15 @@ async def test_wallbox_lock_class(hass: HomeAssistant, entry: MockConfigEntry) -
3838
),
3939
patch(
4040
"homeassistant.components.wallbox.Wallbox.lockCharger",
41-
new=Mock(return_value={CHARGER_LOCKED_UNLOCKED_KEY: False}),
41+
new=Mock(
42+
return_value={"data": {"chargerData": {CHARGER_LOCKED_UNLOCKED_KEY: 1}}}
43+
),
4244
),
4345
patch(
4446
"homeassistant.components.wallbox.Wallbox.unlockCharger",
45-
new=Mock(return_value={CHARGER_LOCKED_UNLOCKED_KEY: False}),
47+
new=Mock(
48+
return_value={"data": {"chargerData": {CHARGER_LOCKED_UNLOCKED_KEY: 0}}}
49+
),
4650
),
4751
):
4852
await hass.services.async_call(
@@ -129,6 +133,52 @@ async def test_wallbox_lock_class_connection_error(
129133
new=Mock(side_effect=http_429_error),
130134
),
131135
pytest.raises(HomeAssistantError),
136+
):
137+
await hass.services.async_call(
138+
"lock",
139+
SERVICE_LOCK,
140+
{
141+
ATTR_ENTITY_ID: MOCK_LOCK_ENTITY_ID,
142+
},
143+
blocking=True,
144+
)
145+
with (
146+
patch(
147+
"homeassistant.components.wallbox.Wallbox.authenticate",
148+
new=Mock(return_value=authorisation_response),
149+
),
150+
patch(
151+
"homeassistant.components.wallbox.Wallbox.lockCharger",
152+
new=Mock(side_effect=http_403_error),
153+
),
154+
patch(
155+
"homeassistant.components.wallbox.Wallbox.unlockCharger",
156+
new=Mock(side_effect=http_403_error),
157+
),
158+
pytest.raises(HomeAssistantError),
159+
):
160+
await hass.services.async_call(
161+
"lock",
162+
SERVICE_UNLOCK,
163+
{
164+
ATTR_ENTITY_ID: MOCK_LOCK_ENTITY_ID,
165+
},
166+
blocking=True,
167+
)
168+
with (
169+
patch(
170+
"homeassistant.components.wallbox.Wallbox.authenticate",
171+
new=Mock(return_value=authorisation_response),
172+
),
173+
patch(
174+
"homeassistant.components.wallbox.Wallbox.lockCharger",
175+
new=Mock(side_effect=http_404_error),
176+
),
177+
patch(
178+
"homeassistant.components.wallbox.Wallbox.unlockCharger",
179+
new=Mock(side_effect=http_404_error),
180+
),
181+
pytest.raises(HomeAssistantError),
132182
):
133183
await hass.services.async_call(
134184
"lock",
@@ -138,27 +188,3 @@ async def test_wallbox_lock_class_connection_error(
138188
},
139189
blocking=True,
140190
)
141-
142-
143-
async def test_wallbox_lock_class_authentication_error(
144-
hass: HomeAssistant, entry: MockConfigEntry
145-
) -> None:
146-
"""Test wallbox lock not loaded on authentication error."""
147-
148-
await setup_integration_read_only(hass, entry)
149-
150-
state = hass.states.get(MOCK_LOCK_ENTITY_ID)
151-
152-
assert state is None
153-
154-
155-
async def test_wallbox_lock_class_platform_not_ready(
156-
hass: HomeAssistant, entry: MockConfigEntry
157-
) -> None:
158-
"""Test wallbox lock not loaded on authentication error."""
159-
160-
await setup_integration_platform_not_ready(hass, entry)
161-
162-
state = hass.states.get(MOCK_LOCK_ENTITY_ID)
163-
164-
assert state is None

0 commit comments

Comments
 (0)