Skip to content

Commit

Permalink
Upgrade base version dependency to HA 2024.9 (#179)
Browse files Browse the repository at this point in the history
- Upgrade HA base version dependency to 2024.9
- Move to ServiceValidationError
- Freeze custom dataclasses
- Rename services to actions in user-facing places
  • Loading branch information
torbennehmer authored Sep 10, 2024
2 parents 9e4397c + 99f21f7 commit 98e8843
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 135 deletions.
2 changes: 1 addition & 1 deletion .devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "torbennehmer/hacs-e3dc",
"image": "mcr.microsoft.com/vscode/devcontainers/python:0-3.11",
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"postCreateCommand": "scripts/setup",
"forwardPorts": [
8124
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: "Set up Python"
uses: actions/setup-python@v5.2.0
with:
python-version: "3.11"
python-version: "3.12"
cache: "pip"

- name: "Install requirements"
Expand Down
49 changes: 28 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ code.
- [Password limitations](#password-limitations)
- [Network restriction](#network-restriction)
- [Unsupported features configuration schemes](#unsupported-features-configuration-schemes)
- [Services](#services)
- [Actions](#actions)
- [Set power limits](#set-power-limits)
- [Clear current power limits](#clear-current-power-limits)
- [Initate manual battery charging](#initate-manual-battery-charging)
Expand Down Expand Up @@ -163,14 +163,14 @@ Currently, the following features of pye3dc are not supported:
- Web Connections
- Local connections when offline, using the backup user.
## Services
## Actions
The integration currently provides these services to initiate more complex
The integration currently provides these actions to initiate more complex
commands to the E3DC unit:
### Set power limits
Use the service `set_power_limits` to limit the maximum charging or discharging
Use the action `set_power_limits` to limit the maximum charging or discharging
rates of the battery system. Both values can be controlled individually, each
call replaces the settings made by the last. It will not allow you to change the
system defined minimum discharge rate at the moment, as I am not sure if this is
Expand All @@ -184,7 +184,7 @@ values to the system defined maximum.

### Initate manual battery charging

The service `manual_charge` will start charging the specified amount of energy
The action `manual_charge` will start charging the specified amount of energy
into the battery, taking it from the grid if neccessary. The idea behind this is
to take advantage of dynamic electricity providers like Tibber. Charge your
battery when electricity is cheap even if you have no solar power available, for
Expand All @@ -193,36 +193,43 @@ example in windy winter nights/days.
**Read the following before using this functionality on your own risk:**

- Calls to this operation are rate-limited, your E3DC probaby will not accept
more than one call every few hours. One unit reported to me had a wait time
of two hours, apparently. The website mentions that this operation can only
be called once a day and limits the charge amount to 3 kWh. This, again,
is unconfirmed, so your milage may vary.
more than one call every few hours. One unit reported to me had a wait time of
two hours, apparently. The website mentions that this operation can only be
called once a day and limits the charge amount to 3 kWh. This, again, is
unconfirmed, so your milage may vary.
- Important from a monetary point of view: You will have losses from two AC/DC
conversions (load and unload), as opposed to one when charging from the PV.
A single conversion will probably cost you 10-15% in stored power. So,
charging 10 kWh from the Grid will approximate only 7-8 kWh when using it.
Also, the wear on the battery should be considered. Following that, you'll
want significant savings, not just a few cents.
- Check if your local laws and regulations do allow you to charge your
battery from the grid for consuming power later in the first place.
conversions (load and unload), as opposed to one when charging from the PV. A
single conversion will probably cost you 10-15% in stored power. So, charging
10 kWh from the Grid will approximate only 7-8 kWh when using it. Also, the
wear on the battery should be considered. Following that, you'll want
significant savings, not just a few cents.
- Check if your local laws and regulations do allow you to charge your battery
from the grid for consuming power later in the first place.
- Check the impact on any warranty from E3DC you may have.

To stress this once more: Use this feature at your own risk.

### Set maximum wallbox charging current

The Service `set_wallbox_charging_current` will set the maximum charging current of the given Wallbox in Amps. 16A is typical for a 11kW Wallbox, 32A is typical for a 22kW Wallbox.
The action `set_wallbox_charging_current` will set the maximum charging current
of the given Wallbox in Amps. 16A is typical for a 11kW Wallbox, 32A is typical
for a 22kW Wallbox.

**Read the following before using this functionality on your own risk:**

- If values cannot be set, this may be due to the hard limits for your fuses configured during the installation of the Wallbox. Only a E3DC service technican can and should change these limits.
- In case your fuse settings are wrong (too low) and you set the charging current too high, you may blow your fuse or even damage your Wallbox.
- Check your local laws and regulations whether setting the Wallbox to a higher charging current requires approvals from your grid operator.
- If values cannot be set, this may be due to the hard limits for your fuses
configured during the installation of the Wallbox. Only a E3DC service
technican can and should change these limits.
- In case your fuse settings are wrong (too low) and you set the charging
current too high, you may blow your fuse or even damage your Wallbox.
- Check your local laws and regulations whether setting the Wallbox to a higher
charging current requires approvals from your grid operator.

## Upstream source

The extension is based on [Python E3DC
library](https://github.com/fsantini/python-e3dc) from @fsantini. The general considerations mentioned in his project do apply to this integration.
library](https://github.com/fsantini/python-e3dc) from @fsantini. The general
considerations mentioned in his project do apply to this integration.

***

Expand Down
75 changes: 64 additions & 11 deletions custom_components/e3dc_rscp/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
_LOGGER = logging.getLogger(__name__)


@dataclass
@dataclass(slots=True, frozen=True)
class E3DCBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Derived helper for more advanced entity configs."""

Expand Down Expand Up @@ -81,7 +81,14 @@ async def async_setup_entry(
off_icon="mdi:weather-sunny-off",
device_class=None,
)
entities.append(E3DCBinarySensor(coordinator, wallbox_sun_mode_description, unique_id, wallbox["deviceInfo"]))
entities.append(
E3DCBinarySensor(
coordinator,
wallbox_sun_mode_description,
unique_id,
wallbox["deviceInfo"],
)
)

wallbox_plug_lock_description = E3DCBinarySensorEntityDescription(
key=f"{wallbox_key}-plug-lock",
Expand All @@ -91,7 +98,14 @@ async def async_setup_entry(
device_class=BinarySensorDeviceClass.LOCK,
entity_registry_enabled_default=False, # Disabled per default as only Wallbox easy connect provides this state
)
entities.append(E3DCBinarySensor(coordinator, wallbox_plug_lock_description, unique_id, wallbox["deviceInfo"]))
entities.append(
E3DCBinarySensor(
coordinator,
wallbox_plug_lock_description,
unique_id,
wallbox["deviceInfo"],
)
)

wallbox_plug_description = E3DCBinarySensorEntityDescription(
key=f"{wallbox_key}-plug",
Expand All @@ -100,17 +114,28 @@ async def async_setup_entry(
off_icon="mdi:power-plug-off",
device_class=BinarySensorDeviceClass.PLUG,
)
entities.append(E3DCBinarySensor(coordinator, wallbox_plug_description, unique_id, wallbox["deviceInfo"]))
entities.append(
E3DCBinarySensor(
coordinator, wallbox_plug_description, unique_id, wallbox["deviceInfo"]
)
)

wallbox_schuko_description = E3DCBinarySensorEntityDescription(
key=f"{wallbox_key}-schuko",
translation_key="wallbox-schuko",
on_icon="mdi:power-plug-outline",
off_icon="mdi:power-plug-off-outline",
device_class=BinarySensorDeviceClass.POWER,
entity_registry_enabled_default=False, # Disabled per default as only Wallbox multi connect I provides this feature
entity_registry_enabled_default=False, # Disabled per default as only Wallbox multi connect I provides this feature
)
entities.append(
E3DCBinarySensor(
coordinator,
wallbox_schuko_description,
unique_id,
wallbox["deviceInfo"],
)
)
entities.append(E3DCBinarySensor(coordinator, wallbox_schuko_description, unique_id, wallbox["deviceInfo"]))

wallbox_charging_description = E3DCBinarySensorEntityDescription(
key=f"{wallbox_key}-charging",
Expand All @@ -119,7 +144,14 @@ async def async_setup_entry(
off_icon="mdi:car-electric-outline",
device_class=BinarySensorDeviceClass.BATTERY_CHARGING,
)
entities.append(E3DCBinarySensor(coordinator, wallbox_charging_description, unique_id, wallbox["deviceInfo"]))
entities.append(
E3DCBinarySensor(
coordinator,
wallbox_charging_description,
unique_id,
wallbox["deviceInfo"],
)
)

wallbox_charging_canceled_description = E3DCBinarySensorEntityDescription(
key=f"{wallbox_key}-charging-canceled",
Expand All @@ -128,7 +160,14 @@ async def async_setup_entry(
off_icon="mdi:check-circle-outline",
device_class=None,
)
entities.append(E3DCBinarySensor(coordinator, wallbox_charging_canceled_description, unique_id, wallbox["deviceInfo"]))
entities.append(
E3DCBinarySensor(
coordinator,
wallbox_charging_canceled_description,
unique_id,
wallbox["deviceInfo"],
)
)

wallbox_battery_to_car_description = E3DCBinarySensorEntityDescription(
key=f"{wallbox_key}-battery-to-car",
Expand All @@ -138,7 +177,14 @@ async def async_setup_entry(
device_class=None,
entity_registry_enabled_default=False,
)
entities.append(E3DCBinarySensor(coordinator, wallbox_battery_to_car_description, unique_id, wallbox["deviceInfo"]))
entities.append(
E3DCBinarySensor(
coordinator,
wallbox_battery_to_car_description,
unique_id,
wallbox["deviceInfo"],
)
)

wallbox_key_state_description = E3DCBinarySensorEntityDescription(
key=f"{wallbox_key}-key-state",
Expand All @@ -148,7 +194,14 @@ async def async_setup_entry(
device_class=BinarySensorDeviceClass.LOCK,
entity_registry_enabled_default=False,
)
entities.append(E3DCBinarySensor(coordinator, wallbox_key_state_description, unique_id, wallbox["deviceInfo"]))
entities.append(
E3DCBinarySensor(
coordinator,
wallbox_key_state_description,
unique_id,
wallbox["deviceInfo"],
)
)

async_add_entities(entities)

Expand All @@ -163,7 +216,7 @@ def __init__(
coordinator: E3DCCoordinator,
description: E3DCBinarySensorEntityDescription,
uid: str,
device_info: DeviceInfo | None = None
device_info: DeviceInfo | None = None,
) -> None:
"""Initialize the Sensor."""
super().__init__(coordinator)
Expand Down
36 changes: 26 additions & 10 deletions custom_components/e3dc_rscp/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
_LOGGER = logging.getLogger(__name__)


@dataclass
@dataclass(slots=True, frozen=True)
class E3DCButtonEntityDescription(ButtonEntityDescription):
"""Derived helper for advanced configs."""

Expand All @@ -31,7 +31,7 @@ class E3DCButtonEntityDescription(ButtonEntityDescription):
) = None


BUTTONS: Final[tuple[E3DCButtonEntityDescription, ...]] = () # None yet
BUTTONS: Final[tuple[E3DCButtonEntityDescription, ...]] = () # None yet


async def async_setup_entry(
Expand All @@ -42,8 +42,7 @@ async def async_setup_entry(
coordinator: E3DCCoordinator = hass.data[DOMAIN][entry.unique_id]

entities: list[E3DCButton] = [
E3DCButton(coordinator, description, entry.unique_id)
for description in BUTTONS
E3DCButton(coordinator, description, entry.unique_id) for description in BUTTONS
]

for wallbox in coordinator.wallboxes:
Expand All @@ -55,18 +54,35 @@ async def async_setup_entry(
key=f"{wallbox_key}-toggle-wallbox-phases",
translation_key="wallbox-toggle-wallbox-phases",
icon="mdi:sine-wave",
async_press_action=lambda coordinator, index=wallbox["index"]: coordinator.async_toggle_wallbox_phases(index),
async_press_action=lambda coordinator, index=wallbox[
"index"
]: coordinator.async_toggle_wallbox_phases(index),
)
entities.append(
E3DCButton(
coordinator,
wallbox_toggle_wallbox_phases_description,
unique_id,
wallbox["deviceInfo"],
)
)
entities.append(E3DCButton(coordinator, wallbox_toggle_wallbox_phases_description, unique_id, wallbox["deviceInfo"]))

wallbox_toggle_wallbox_charging_description = E3DCButtonEntityDescription(
key=f"{wallbox_key}-toggle-wallbox-charging",
translation_key="wallbox-toggle-wallbox-charging",
icon="mdi:car-electric",
async_press_action=lambda coordinator, index=wallbox["index"]: coordinator.async_toggle_wallbox_charging(index),
async_press_action=lambda coordinator, index=wallbox[
"index"
]: coordinator.async_toggle_wallbox_charging(index),
)
entities.append(
E3DCButton(
coordinator,
wallbox_toggle_wallbox_charging_description,
unique_id,
wallbox["deviceInfo"],
)
)
entities.append(E3DCButton(coordinator, wallbox_toggle_wallbox_charging_description, unique_id, wallbox["deviceInfo"]))


async_add_entities(entities)

Expand All @@ -81,7 +97,7 @@ def __init__(
coordinator: E3DCCoordinator,
description: E3DCButtonEntityDescription,
uid: str,
device_info: DeviceInfo | None = None
device_info: DeviceInfo | None = None,
) -> None:
"""Initialize the Button."""
super().__init__(coordinator)
Expand Down
Loading

0 comments on commit 98e8843

Please sign in to comment.