Skip to content

Commit

Permalink
Added multi-wallbox capability to service
Browse files Browse the repository at this point in the history
  • Loading branch information
bullitt186 committed Jun 30, 2024
1 parent 186136a commit bb132a9
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 30 deletions.
25 changes: 15 additions & 10 deletions custom_components/e3dc_rscp/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand All @@ -514,14 +514,19 @@ 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

_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)
Expand Down
34 changes: 17 additions & 17 deletions custom_components/e3dc_rscp/e3dc_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
16 changes: 13 additions & 3 deletions custom_components/e3dc_rscp/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)),
}
)
Expand Down Expand Up @@ -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:
Expand Down
9 changes: 9 additions & 0 deletions custom_components/e3dc_rscp/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions custom_components/e3dc_rscp/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
4 changes: 4 additions & 0 deletions custom_components/e3dc_rscp/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down

0 comments on commit bb132a9

Please sign in to comment.