From bb132a9539c9f2333eeeb65cc155ef6693a6e47e Mon Sep 17 00:00:00 2001 From: bullitt168 Date: Sun, 30 Jun 2024 21:21:32 +0000 Subject: [PATCH] Added multi-wallbox capability to service --- custom_components/e3dc_rscp/coordinator.py | 25 ++++++++------ custom_components/e3dc_rscp/e3dc_proxy.py | 34 +++++++++---------- custom_components/e3dc_rscp/services.py | 16 +++++++-- custom_components/e3dc_rscp/services.yaml | 9 +++++ custom_components/e3dc_rscp/strings.json | 4 +++ .../e3dc_rscp/translations/en.json | 4 +++ 6 files changed, 62 insertions(+), 30 deletions(-) diff --git a/custom_components/e3dc_rscp/coordinator.py b/custom_components/e3dc_rscp/coordinator.py index 587a9d3..51fb52b 100644 --- a/custom_components/e3dc_rscp/coordinator.py +++ b/custom_components/e3dc_rscp/coordinator.py @@ -89,25 +89,25 @@ async def async_identify_wallboxes(self): _LOGGER.debug("async_identify_wallboxes") # TODO: Find a more robust way to identify if a Wallbox is installed - for wbIndex in range(0, MAX_WALLBOXES_POSSIBLE-1): + for wallbox_index in range(0, MAX_WALLBOXES_POSSIBLE-1): try: request_data: dict[str, Any] = await self.hass.async_add_executor_job( - self.proxy.get_wallbox_data, wbIndex + self.proxy.get_wallbox_data, wallbox_index ) except HomeAssistantError as ex: - _LOGGER.warning("Failed to load wallbox with index %s, not updating data: %s", wbIndex, ex) + _LOGGER.warning("Failed to load wallbox with index %s, not updating data: %s", wallbox_index, ex) return if request_data["appSoftware"] is not None: - _LOGGER.debug("Wallbox with index %s has been found", wbIndex) + _LOGGER.debug("Wallbox with index %s has been found", wallbox_index) wallbox = { - "index": wbIndex, - "key": f"wallbox-{wbIndex + 1}", - "name": f"Wallbox {wbIndex + 1}" + "index": wallbox_index, + "key": f"wallbox-{wallbox_index + 1}", + "name": f"Wallbox {wallbox_index + 1}" } self.wallboxes.append(wallbox) else: - _LOGGER.debug("No Wallbox with index %s has been found", wbIndex) + _LOGGER.debug("No Wallbox with index %s has been found", wallbox_index) # Fix Naming if there's only one wallbox if len(self.wallboxes) == 1: @@ -503,7 +503,7 @@ async def async_clear_power_limits(self) -> None: _LOGGER.debug("Successfully cleared the power limits") - async def async_set_wallbox_max_charge_current(self, current: int | None) -> None: + async def async_set_wallbox_max_charge_current(self, current: int | None, wallbox_index: int | None) -> None: """Set the wallbox max charge current.""" # TODO: Add more refined way to deal with maximum charge current, right now it's hard coded to 32A. The max current is dependant on the local installations, many WBs are throttled at 16A, not 32A due to power grid restrictions. @@ -514,6 +514,11 @@ async def async_set_wallbox_max_charge_current(self, current: int | None) -> Non "async_set_wallbox_max_charge_current must be called with a positive current value." ) + if wallbox_index < 0 or wallbox_index >= MAX_WALLBOXES_POSSIBLE: + raise ValueError( + "async_set_wallbox_max_charge_current must be called with a valid wallbox id." + ) + if current > MAX_CHARGE_CURRENT: _LOGGER.warning("Limiting current to %s", MAX_CHARGE_CURRENT) current = MAX_CHARGE_CURRENT @@ -521,7 +526,7 @@ async def async_set_wallbox_max_charge_current(self, current: int | None) -> Non _LOGGER.debug("Setting wallbox max charge current to %s", current) await self.hass.async_add_executor_job( - self.proxy.set_wallbox_max_charge_current, current + self.proxy.set_wallbox_max_charge_current, current, wallbox_index ) _LOGGER.debug("Successfully set the wallbox max charge current to %s", current) diff --git a/custom_components/e3dc_rscp/e3dc_proxy.py b/custom_components/e3dc_rscp/e3dc_proxy.py index 984a35d..0a1bf4d 100644 --- a/custom_components/e3dc_rscp/e3dc_proxy.py +++ b/custom_components/e3dc_rscp/e3dc_proxy.py @@ -156,9 +156,9 @@ def get_powermeters(self) -> dict[str, Any]: return self.e3dc.get_powermeters(keepAlive=True) @e3dc_call - def get_wallbox_data(self, wbIndex: int = 0) -> dict[str, Any]: + def get_wallbox_data(self, wallbox_index: int = 0) -> dict[str, Any]: """Poll current wallbox readings.""" - return self.e3dc.get_wallbox_data(wbIndex=wbIndex, keepAlive=True) + return self.e3dc.get_wallbox_data(wbIndex=wallbox_index, keepAlive=True) @e3dc_call def get_powermeters_data(self) -> dict[str, Any]: @@ -232,78 +232,78 @@ def start_manual_charge(self, charge_amount_wh: int) -> None: _LOGGER.warning("Manual charging could not be activated") @e3dc_call - def set_wallbox_sun_mode(self, enabled: bool, wbIndex: int = 0): + def set_wallbox_sun_mode(self, enabled: bool, wallbox_index: int = 0): """Set wallbox charging mode to sun mode on/off. Args: enabled(bool): the desired state True = sun mode enabled, False = sun mode disabled - wbIndex (Optional[int]): index of the requested wallbox, + wallbox_index (Optional[int]): index of the requested wallbox, Returns: nothing """ - result: bool = self.e3dc.set_wallbox_sunmode(enabled, wbIndex, True) + result: bool = self.e3dc.set_wallbox_sunmode(enable=enabled, wbIndex=wallbox_index, keepAlive=True) if not result: raise HomeAssistantError("Failed to set wallbox to sun mode %s", enabled) @e3dc_call - def set_wallbox_schuko(self, enabled: bool, wbIndex: int = 0): + def set_wallbox_schuko(self, enabled: bool, wallbox_index: int = 0): """Set wallbox power outlet (schuko) to on/off. Args: enabled(bool): the desired state True = on, False = off - wbIndex (Optional[int]): index of the requested wallbox, + wallbox_index (Optional[int]): index of the requested wallbox, Returns: nothing """ - result: bool = self.e3dc.set_wallbox_schuko(enabled, wbIndex, True) + result: bool = self.e3dc.set_wallbox_schuko(enable=enabled, wbIndex=wallbox_index, keepAlive=True) if not result: raise HomeAssistantError("Failed to set wallbox schuko to %s", enabled) @e3dc_call - def toggle_wallbox_charging(self, wbIndex: int = 0): + def toggle_wallbox_charging(self, wallbox_index: int = 0): """Toggle charging of the wallbox. Args: - wbIndex (Optional[int]): index of the requested wallbox, + wallbox_index (Optional[int]): index of the requested wallbox, Returns: nothing """ - result: bool = self.e3dc.toggle_wallbox_charging(wbIndex, True) + result: bool = self.e3dc.toggle_wallbox_charging(wbIndex=wallbox_index, keepAlive=True) if not result: raise HomeAssistantError("Failed to toggle wallbox charging") @e3dc_call - def toggle_wallbox_phases(self, wbIndex: int = 0): + def toggle_wallbox_phases(self, wallbox_index: int = 0): """Toggle the phases of wallbox charging between 1 and 3 phases. Only works if "Phasen" in the portal/device is not set to Auto. Args: - wbIndex (Optional[int]): index of the requested wallbox, + wallbox_index (Optional[int]): index of the requested wallbox, Returns: nothing """ - result: bool = self.e3dc.toggle_wallbox_phases(wbIndex, True) + result: bool = self.e3dc.toggle_wallbox_phases(wbIndex=wallbox_index, keepAlive=True) if not result: raise HomeAssistantError("Failed to toggle wallbox phases") @e3dc_call def set_wallbox_max_charge_current( - self, max_charge_current: int, wbIndex: int = 0 + self, max_charge_current: int, wallbox_index: int = 0 ) -> bool: """Set the maximum charge current of the wallbox via RSCP protocol locally. Args: max_charge_current (int): maximum allowed charge current in A - wbIndex (Optional[int]): index of the requested wallbox + wallbox_index (Optional[int]): index of the requested wallbox Returns: True if success (wallbox has understood the request, but might have clipped the value) @@ -316,7 +316,7 @@ def set_wallbox_max_charge_current( max_charge_current = MAX_CHARGE_CURRENT return self.e3dc.set_wallbox_max_charge_current( - max_charge_current, wbIndex, keepAlive=True + max_charge_current=max_charge_current, wbIndex=wallbox_index, keepAlive=True ) @e3dc_call diff --git a/custom_components/e3dc_rscp/services.py b/custom_components/e3dc_rscp/services.py index 13cf36a..eb028b9 100644 --- a/custom_components/e3dc_rscp/services.py +++ b/custom_components/e3dc_rscp/services.py @@ -24,6 +24,7 @@ _device_map: dict[str, E3DCCoordinator] = {} ATTR_DEVICEID = "device_id" +ATTR_WALLBOX_INDEX = "wallbox_index" ATTR_MAX_CHARGE = "max_charge" ATTR_MAX_DISCHARGE = "max_discharge" ATTR_CHARGE_AMOUNT = "charge_amount" @@ -46,6 +47,7 @@ SCHEMA_SET_WALLBOX_MAX_CHARGE_CURRENT = vol.Schema( { vol.Required(ATTR_DEVICEID): str, + vol.Required(ATTR_WALLBOX_INDEX): vol.All(int, vol.Range(min=0)), vol.Optional(ATTR_MAX_CHARGE_CURRENT): vol.All(int, vol.Range(min=0)), } ) @@ -137,16 +139,24 @@ async def _async_set_wallbox_max_charge_current( hass: HomeAssistant, call: ServiceCall ) -> None: """Extract service information and relay to coordinator.""" - # TODO: Add option to select Wallbox + + _LOGGER.debug("begin of _async_set_wallbox_max_charge_current") + coordinator: E3DCCoordinator = _resolve_device_id( hass, call.data.get(ATTR_DEVICEID) ) + wallbox_index: int | None = call.data.get(ATTR_WALLBOX_INDEX) + if wallbox_index is None: + raise HomeAssistantError( + f"{SERVICE_SET_WALLBOX_MAX_CHARGE_CURRENT}: Need to set {ATTR_WALLBOX_INDEX}" + ) max_charge_current: int | None = call.data.get(ATTR_MAX_CHARGE_CURRENT) if max_charge_current is None: raise HomeAssistantError( - f"{SERVICE_SET_POWER_LIMITS}: Need to set {ATTR_MAX_CHARGE_CURRENT}" + f"{SERVICE_SET_WALLBOX_MAX_CHARGE_CURRENT}: Need to set {ATTR_MAX_CHARGE_CURRENT}" ) - await coordinator.async_set_wallbox_max_charge_current(current=max_charge_current) + _LOGGER.debug("calling coordinator.async_set_wallbox_max_charge_current") + await coordinator.async_set_wallbox_max_charge_current(current=max_charge_current, wallbox_index=wallbox_index) async def _async_set_power_limits(hass: HomeAssistant, call: ServiceCall) -> None: diff --git a/custom_components/e3dc_rscp/services.yaml b/custom_components/e3dc_rscp/services.yaml index 9f2198a..7cd5048 100644 --- a/custom_components/e3dc_rscp/services.yaml +++ b/custom_components/e3dc_rscp/services.yaml @@ -45,6 +45,15 @@ set_wallbox_max_charge_current: device: filter: integration: e3dc_rscp + wallbox_index: + required: true + example: "0" + selector: + number: + min: 0 + max: 7 + mode: box + step: 1 max_charge_current: required: false example: "16" diff --git a/custom_components/e3dc_rscp/strings.json b/custom_components/e3dc_rscp/strings.json index beb163b..2639e16 100644 --- a/custom_components/e3dc_rscp/strings.json +++ b/custom_components/e3dc_rscp/strings.json @@ -309,6 +309,10 @@ "name": "E3DC Device ID", "description": "E3DC Device ID, take it either from the YAML-Mode on the website of out of the URL of the device configuration page." }, + "wallbox_index": { + "name": "Wallbox Index", + "description": "Index of the Wallbox, You find it in the diagnostic entity of the wallbox \"Wallbox Index\" (deactivated by default)." + }, "max_charge_current": { "name": "Maximum Charging Current (A)", "description": "Maximum allowed Charging via Wallbox in Ampere." diff --git a/custom_components/e3dc_rscp/translations/en.json b/custom_components/e3dc_rscp/translations/en.json index 46d9104..af0acca 100644 --- a/custom_components/e3dc_rscp/translations/en.json +++ b/custom_components/e3dc_rscp/translations/en.json @@ -309,6 +309,10 @@ "name": "E3DC Device ID", "description": "E3DC Device ID, take it either from the YAML-Mode on the website of out of the URL of the device configuration page." }, + "wallbox_index": { + "name": "Wallbox Index", + "description": "Index of the Wallbox, You find it in the diagnostic entity of the wallbox \"Wallbox Index\" (deactivated by default)." + }, "max_charge_current": { "name": "Maximum Charging Current (A)", "description": "Maximum allowed Charging via Wallbox in Ampere."