Add new number component for setting the wallbox ICP current (#125209)

* Add new number component for setting the wallbox ICP current

* feat: Add number component for wallbox ICP current control
pull/124925/head
Hessel 2024-09-04 14:00:38 +02:00 committed by GitHub
parent 4b111008df
commit b5d7eba4f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 171 additions and 1 deletions

View File

@ -22,11 +22,15 @@ CHARGER_CURRENCY_KEY = "currency"
CHARGER_DATA_KEY = "config_data"
CHARGER_DEPOT_PRICE_KEY = "depot_price"
CHARGER_ENERGY_PRICE_KEY = "energy_price"
CHARGER_FEATURES_KEY = "features"
CHARGER_SERIAL_NUMBER_KEY = "serial_number"
CHARGER_PART_NUMBER_KEY = "part_number"
CHARGER_PLAN_KEY = "plan"
CHARGER_POWER_BOOST_KEY = "POWER_BOOST"
CHARGER_SOFTWARE_KEY = "software"
CHARGER_MAX_AVAILABLE_POWER_KEY = "max_available_power"
CHARGER_MAX_CHARGING_CURRENT_KEY = "max_charging_current"
CHARGER_MAX_ICP_CURRENT_KEY = "icp_max_current"
CHARGER_PAUSE_RESUME_KEY = "paused"
CHARGER_LOCKED_UNLOCKED_KEY = "locked"
CHARGER_NAME_KEY = "name"

View File

@ -19,8 +19,12 @@ from .const import (
CHARGER_CURRENCY_KEY,
CHARGER_DATA_KEY,
CHARGER_ENERGY_PRICE_KEY,
CHARGER_FEATURES_KEY,
CHARGER_LOCKED_UNLOCKED_KEY,
CHARGER_MAX_CHARGING_CURRENT_KEY,
CHARGER_MAX_ICP_CURRENT_KEY,
CHARGER_PLAN_KEY,
CHARGER_POWER_BOOST_KEY,
CHARGER_STATUS_DESCRIPTION_KEY,
CHARGER_STATUS_ID_KEY,
CODE_KEY,
@ -130,6 +134,16 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][
CHARGER_ENERGY_PRICE_KEY
]
# Only show max_icp_current if power_boost is available in the wallbox unit:
if (
data[CHARGER_DATA_KEY].get(CHARGER_MAX_ICP_CURRENT_KEY, 0) > 0
and CHARGER_POWER_BOOST_KEY
in data[CHARGER_DATA_KEY][CHARGER_PLAN_KEY][CHARGER_FEATURES_KEY]
):
data[CHARGER_MAX_ICP_CURRENT_KEY] = data[CHARGER_DATA_KEY][
CHARGER_MAX_ICP_CURRENT_KEY
]
data[CHARGER_CURRENCY_KEY] = (
f"{data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][CODE_KEY]}/kWh"
)
@ -160,6 +174,21 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]):
)
await self.async_request_refresh()
@_require_authentication
def _set_icp_current(self, icp_current: float) -> None:
"""Set maximum icp current for Wallbox."""
try:
self._wallbox.setIcpMaxCurrent(self._station, icp_current)
except requests.exceptions.HTTPError as wallbox_connection_error:
if wallbox_connection_error.response.status_code == 403:
raise InvalidAuth from wallbox_connection_error
raise
async def async_set_icp_current(self, icp_current: float) -> None:
"""Set maximum icp current for Wallbox."""
await self.hass.async_add_executor_job(self._set_icp_current, icp_current)
await self.async_request_refresh()
@_require_authentication
def _set_energy_cost(self, energy_cost: float) -> None:
"""Set energy cost for Wallbox."""

View File

@ -21,6 +21,7 @@ from .const import (
CHARGER_ENERGY_PRICE_KEY,
CHARGER_MAX_AVAILABLE_POWER_KEY,
CHARGER_MAX_CHARGING_CURRENT_KEY,
CHARGER_MAX_ICP_CURRENT_KEY,
CHARGER_PART_NUMBER_KEY,
CHARGER_SERIAL_NUMBER_KEY,
DOMAIN,
@ -67,6 +68,16 @@ NUMBER_TYPES: dict[str, WallboxNumberEntityDescription] = {
set_value_fn=lambda coordinator: coordinator.async_set_energy_cost,
native_step=0.01,
),
CHARGER_MAX_ICP_CURRENT_KEY: WallboxNumberEntityDescription(
key=CHARGER_MAX_ICP_CURRENT_KEY,
translation_key="maximum_icp_current",
max_value_fn=lambda coordinator: cast(
float, coordinator.data[CHARGER_MAX_AVAILABLE_POWER_KEY]
),
min_value_fn=lambda _: 6,
set_value_fn=lambda coordinator: coordinator.async_set_icp_current,
native_step=1,
),
}

View File

@ -38,6 +38,7 @@ from .const import (
CHARGER_ENERGY_PRICE_KEY,
CHARGER_MAX_AVAILABLE_POWER_KEY,
CHARGER_MAX_CHARGING_CURRENT_KEY,
CHARGER_MAX_ICP_CURRENT_KEY,
CHARGER_SERIAL_NUMBER_KEY,
CHARGER_STATE_OF_CHARGE_KEY,
CHARGER_STATUS_DESCRIPTION_KEY,
@ -145,6 +146,13 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = {
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
CHARGER_MAX_ICP_CURRENT_KEY: WallboxSensorEntityDescription(
key=CHARGER_MAX_ICP_CURRENT_KEY,
translation_key=CHARGER_MAX_ICP_CURRENT_KEY,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
}

View File

@ -38,6 +38,9 @@
},
"energy_price": {
"name": "Energy price"
},
"maximum_icp_current": {
"name": "Maximum ICP current"
}
},
"sensor": {
@ -79,6 +82,9 @@
},
"max_charging_current": {
"name": "Max charging current"
},
"icp_max_current": {
"name": "Max ICP current"
}
},
"switch": {

View File

@ -14,11 +14,15 @@ from homeassistant.components.wallbox.const import (
CHARGER_CURRENT_VERSION_KEY,
CHARGER_DATA_KEY,
CHARGER_ENERGY_PRICE_KEY,
CHARGER_FEATURES_KEY,
CHARGER_LOCKED_UNLOCKED_KEY,
CHARGER_MAX_AVAILABLE_POWER_KEY,
CHARGER_MAX_CHARGING_CURRENT_KEY,
CHARGER_MAX_ICP_CURRENT_KEY,
CHARGER_NAME_KEY,
CHARGER_PART_NUMBER_KEY,
CHARGER_PLAN_KEY,
CHARGER_POWER_BOOST_KEY,
CHARGER_SERIAL_NUMBER_KEY,
CHARGER_SOFTWARE_KEY,
CHARGER_STATUS_ID_KEY,
@ -45,6 +49,8 @@ test_response = {
CHARGER_PART_NUMBER_KEY: "PLP1-0-2-4-9-002-E",
CHARGER_SOFTWARE_KEY: {CHARGER_CURRENT_VERSION_KEY: "5.5.10"},
CHARGER_CURRENCY_KEY: {"code": "EUR/kWh"},
CHARGER_MAX_ICP_CURRENT_KEY: 20,
CHARGER_PLAN_KEY: {CHARGER_FEATURES_KEY: [CHARGER_POWER_BOOST_KEY]},
},
}
@ -64,6 +70,8 @@ test_response_bidir = {
CHARGER_PART_NUMBER_KEY: "QSP1-0-2-4-9-002-E",
CHARGER_SOFTWARE_KEY: {CHARGER_CURRENT_VERSION_KEY: "5.5.10"},
CHARGER_CURRENCY_KEY: {"code": "EUR/kWh"},
CHARGER_MAX_ICP_CURRENT_KEY: 20,
CHARGER_PLAN_KEY: {CHARGER_FEATURES_KEY: [CHARGER_POWER_BOOST_KEY]},
},
}

View File

@ -9,6 +9,7 @@ STATUS = "status"
MOCK_NUMBER_ENTITY_ID = "number.wallbox_wallboxname_maximum_charging_current"
MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID = "number.wallbox_wallboxname_energy_price"
MOCK_NUMBER_ENTITY_ICP_CURRENT_ID = "number.wallbox_wallboxname_maximum_icp_current"
MOCK_LOCK_ENTITY_ID = "lock.wallbox_wallboxname_lock"
MOCK_SENSOR_CHARGING_SPEED_ID = "sensor.wallbox_wallboxname_charging_speed"
MOCK_SENSOR_CHARGING_POWER_ID = "sensor.wallbox_wallboxname_charging_power"

View File

@ -6,9 +6,12 @@ import pytest
import requests_mock
from homeassistant.components.input_number import ATTR_VALUE, SERVICE_SET_VALUE
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
from homeassistant.components.wallbox import InvalidAuth
from homeassistant.components.wallbox.const import (
CHARGER_ENERGY_PRICE_KEY,
CHARGER_MAX_CHARGING_CURRENT_KEY,
CHARGER_MAX_ICP_CURRENT_KEY,
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
@ -20,7 +23,11 @@ from . import (
setup_integration_bidir,
setup_integration_platform_not_ready,
)
from .const import MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID, MOCK_NUMBER_ENTITY_ID
from .const import (
MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID,
MOCK_NUMBER_ENTITY_ICP_CURRENT_ID,
MOCK_NUMBER_ENTITY_ID,
)
from tests.common import MockConfigEntry
@ -212,3 +219,99 @@ async def test_wallbox_number_class_platform_not_ready(
assert state is None
await hass.config_entries.async_unload(entry.entry_id)
async def test_wallbox_number_class_icp_energy(
hass: HomeAssistant, entry: MockConfigEntry
) -> None:
"""Test wallbox sensor class."""
await setup_integration(hass, entry)
with requests_mock.Mocker() as mock_request:
mock_request.get(
"https://user-api.wall-box.com/users/signin",
json=authorisation_response,
status_code=200,
)
mock_request.post(
"https://api.wall-box.com/chargers/config/12345",
json={CHARGER_MAX_ICP_CURRENT_KEY: 10},
status_code=200,
)
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID,
ATTR_VALUE: 10,
},
blocking=True,
)
await hass.config_entries.async_unload(entry.entry_id)
async def test_wallbox_number_class_icp_energy_auth_error(
hass: HomeAssistant, entry: MockConfigEntry
) -> None:
"""Test wallbox sensor class."""
await setup_integration(hass, entry)
with requests_mock.Mocker() as mock_request:
mock_request.get(
"https://user-api.wall-box.com/users/signin",
json=authorisation_response,
status_code=200,
)
mock_request.post(
"https://api.wall-box.com/chargers/config/12345",
json={CHARGER_MAX_ICP_CURRENT_KEY: 10},
status_code=403,
)
with pytest.raises(InvalidAuth):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID,
ATTR_VALUE: 10,
},
blocking=True,
)
await hass.config_entries.async_unload(entry.entry_id)
async def test_wallbox_number_class_icp_energy_connection_error(
hass: HomeAssistant, entry: MockConfigEntry
) -> None:
"""Test wallbox sensor class."""
await setup_integration(hass, entry)
with requests_mock.Mocker() as mock_request:
mock_request.get(
"https://user-api.wall-box.com/users/signin",
json=authorisation_response,
status_code=200,
)
mock_request.post(
"https://api.wall-box.com/chargers/config/12345",
json={CHARGER_MAX_ICP_CURRENT_KEY: 10},
status_code=404,
)
with pytest.raises(ConnectionError):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID,
ATTR_VALUE: 10,
},
blocking=True,
)
await hass.config_entries.async_unload(entry.entry_id)