From 0bff3c2d5381b0bce46978bdd9fadba6d087c02e Mon Sep 17 00:00:00 2001 From: Tuen Lee Date: Sat, 5 Aug 2023 14:07:37 +0200 Subject: [PATCH 1/3] fix #34 add network selection, fix #33 change boost from select to switch --- custom_components/alfen_wallbox/select.py | 42 +++++++++++++++-------- custom_components/alfen_wallbox/switch.py | 5 +++ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/custom_components/alfen_wallbox/select.py b/custom_components/alfen_wallbox/select.py index 0be5871..470d4d2 100644 --- a/custom_components/alfen_wallbox/select.py +++ b/custom_components/alfen_wallbox/select.py @@ -38,8 +38,6 @@ class AlfenSelectDescription(SelectEntityDescription, AlfenSelectDescriptionMixi CHARGING_MODE_DICT: Final[dict[str, int]] = { "Disable": 0, "Comfort": 1, "Green": 2} -ON_OFF_DICT: Final[dict[str, int]] = {"Off": 0, "On": 1} - PHASE_ROTATION_DICT: Final[dict[str, str]] = { "L1": "L1", "L2": "L2", @@ -118,6 +116,17 @@ class AlfenSelectDescription(SelectEntityDescription, AlfenSelectDescriptionMixi "In-operative": 2, } +GPRS_NETWORK_MODE_DICT: Final[dict[str, int]] = { + "Automatic": 0, + "Manual": 1 +} + +GPRS_TECHNOLOGY_DICT: Final[dict[str, int]] = { + "2G": 0, + "4G": 2, +} + + ALFEN_SELECT_TYPES: Final[tuple[AlfenSelectDescription, ...]] = ( AlfenSelectDescription( key="lb_solar_charging_mode", @@ -127,14 +136,7 @@ class AlfenSelectDescription(SelectEntityDescription, AlfenSelectDescriptionMixi options_dict=CHARGING_MODE_DICT, api_param="3280_1", ), - AlfenSelectDescription( - key="lb_solar_charging_boost", - name="Solar Charging Boost", - icon="mdi:ev-station", - options=list(ON_OFF_DICT), - options_dict=ON_OFF_DICT, - api_param="3280_4", - ), + AlfenSelectDescription( key="alb_phase_connection", name="Active Load Balancing Phase Connection", @@ -209,10 +211,22 @@ class AlfenSelectDescription(SelectEntityDescription, AlfenSelectDescriptionMixi options_dict=OPERATIVE_MODE_DICT, api_param="205F_0", ), - - - - + AlfenSelectDescription( + key="gprs_network_mode", + name="GPRS Network Mode", + icon="mdi:antenna", + options=list(GPRS_NETWORK_MODE_DICT), + options_dict=GPRS_NETWORK_MODE_DICT, + api_param="2113_0", + ), + AlfenSelectDescription( + key="gprs_technology", + name="GPRS Technology", + icon="mdi:antenna", + options=list(GPRS_TECHNOLOGY_DICT), + options_dict=GPRS_TECHNOLOGY_DICT, + api_param="2114_0", + ), ) diff --git a/custom_components/alfen_wallbox/switch.py b/custom_components/alfen_wallbox/switch.py index a09f8b6..46248d9 100644 --- a/custom_components/alfen_wallbox/switch.py +++ b/custom_components/alfen_wallbox/switch.py @@ -40,6 +40,11 @@ class AlfenSwitchDescription(SwitchEntityDescription, AlfenSwitchDescriptionMixi name="Display Light Auto Dim", api_param="2061_1", ), + AlfenSwitchDescription( + key="lb_solar_charging_boost", + name="Solar Charging Boost", + api_param="3280_4", + ), ) From e4da75b224397b766fcba454299609ba287861b0 Mon Sep 17 00:00:00 2001 From: Tuen Lee Date: Mon, 7 Aug 2023 23:54:10 +0200 Subject: [PATCH 2/3] move the service call to the correct entity --- custom_components/alfen_wallbox/alfen.py | 15 +-- .../alfen_wallbox/binary_sensor.py | 2 - custom_components/alfen_wallbox/number.py | 51 +++++++++- custom_components/alfen_wallbox/select.py | 45 ++++++++- custom_components/alfen_wallbox/sensor.py | 94 ------------------- custom_components/alfen_wallbox/services.yaml | 24 +++-- custom_components/alfen_wallbox/switch.py | 28 +++++- 7 files changed, 142 insertions(+), 117 deletions(-) diff --git a/custom_components/alfen_wallbox/alfen.py b/custom_components/alfen_wallbox/alfen.py index 0e646d7..55d2273 100644 --- a/custom_components/alfen_wallbox/alfen.py +++ b/custom_components/alfen_wallbox/alfen.py @@ -210,7 +210,8 @@ async def set_value(self, api_param, value) -> bool: if prop[ID] == api_param: _LOGGER.debug(f"Set {api_param} value {value}") prop[VALUE] = value - return True + break + return False async def get_value(self, api_param): @@ -222,7 +223,7 @@ async def set_current_limit(self, limit): _LOGGER.debug(f"Set current limit {limit}A") if limit > 32 | limit < 1: return None - self.set_value("2129_0", limit) + await self.set_value("2129_0", limit) async def set_rfid_auth_mode(self, enabled): _LOGGER.debug(f"Set RFID Auth Mode {enabled}") @@ -231,13 +232,13 @@ async def set_rfid_auth_mode(self, enabled): if enabled: value = 2 - self.set_value("2126_0", value) + await self.set_value("2126_0", value) async def set_current_phase(self, phase): _LOGGER.debug(f"Set current phase {phase}") if phase not in ('L1', 'L2', 'L3'): return None - self.set_value("2069_0", phase) + await self.set_value("2069_0", phase) async def set_phase_switching(self, enabled): _LOGGER.debug(f"Set Phase Switching {enabled}") @@ -246,19 +247,19 @@ async def set_phase_switching(self, enabled): if enabled: value = 1 - self.set_value("2185_0", value) + await self.set_value("2185_0", value) async def set_green_share(self, value): _LOGGER.debug(f"Set green share value {value}%") if value < 0 | value > 100: return None - self.set_value("3280_2", value) + await self.set_value("3280_2", value) async def set_comfort_power(self, value): _LOGGER.debug(f"Set Comfort Level {value}W") if value < 1400 | value > 5000: return None - self.set_value("3280_3", value) + await self.set_value("3280_3", value) def __get_url(self, action) -> str: return "https://{}/api/{}".format(self.host, action) diff --git a/custom_components/alfen_wallbox/binary_sensor.py b/custom_components/alfen_wallbox/binary_sensor.py index e512049..9fe4c3e 100644 --- a/custom_components/alfen_wallbox/binary_sensor.py +++ b/custom_components/alfen_wallbox/binary_sensor.py @@ -55,8 +55,6 @@ async def async_setup_entry( class AlfenBinarySensor(AlfenEntity, BinarySensorEntity): """Define an Alfen binary sensor.""" - # entity_description: AlfenBinaryDescriptionMixin - def __init__(self, device: AlfenDevice, description: AlfenBinaryDescription diff --git a/custom_components/alfen_wallbox/number.py b/custom_components/alfen_wallbox/number.py index fec3fc7..aca8645 100644 --- a/custom_components/alfen_wallbox/number.py +++ b/custom_components/alfen_wallbox/number.py @@ -1,4 +1,5 @@ -from .const import ID, VALUE +from homeassistant.helpers import entity_platform +from .const import ID, SERVICE_SET_COMFORT_POWER, SERVICE_SET_CURRENT_LIMIT, SERVICE_SET_GREEN_SHARE, VALUE from homeassistant.components.number import NumberDeviceClass, NumberEntity, NumberEntityDescription, NumberMode from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -15,6 +16,10 @@ UnitOfPower, ) +import voluptuous as vol + +from homeassistant.helpers import config_validation as cv + _LOGGER = logging.getLogger(__name__) @@ -159,6 +164,32 @@ async def async_setup_entry( async_add_entities(numbers) + platform = entity_platform.current_platform.get() + + platform.async_register_entity_service( + SERVICE_SET_CURRENT_LIMIT, + { + vol.Required("limit"): cv.positive_int, + }, + "async_set_current_limit", + ) + + platform.async_register_entity_service( + SERVICE_SET_GREEN_SHARE, + { + vol.Required(VALUE): cv.positive_int, + }, + "async_set_green_share", + ) + + platform.async_register_entity_service( + SERVICE_SET_COMFORT_POWER, + { + vol.Required(VALUE): cv.positive_int, + }, + "async_set_comfort_power", + ) + class AlfenNumber(AlfenEntity, NumberEntity): """Define an Alfen select entity.""" @@ -172,7 +203,7 @@ def __init__( device: AlfenDevice, description: AlfenNumberDescription, ) -> None: - """Initialize the Demo Number entity.""" + """Initialize the Alfen Number entity.""" super().__init__(device) self._device = device self._attr_name = f"{description.name}" @@ -217,3 +248,19 @@ def _get_current_option(self) -> str | None: if prop[ID] == self.entity_description.api_param: return prop[VALUE] return None + + async def async_set_current_limit(self, limit): + """Set the current limit.""" + await self._device.set_current_limit(limit) + await self.async_set_native_value(limit) + + async def async_set_green_share(self, value): + """Set the green share.""" + await self._device.set_green_share(value) + await self.async_set_native_value(value) + + + async def async_set_comfort_power(self, value): + """Set the comfort power.""" + await self._device.set_comfort_power(value) + await self.async_set_native_value(value) \ No newline at end of file diff --git a/custom_components/alfen_wallbox/select.py b/custom_components/alfen_wallbox/select.py index 0be5871..00e52ec 100644 --- a/custom_components/alfen_wallbox/select.py +++ b/custom_components/alfen_wallbox/select.py @@ -3,7 +3,9 @@ from dataclasses import dataclass -from .const import ID, VALUE +from homeassistant.helpers import entity_platform + +from .const import ID, SERVICE_DISABLE_RFID_AUTHORIZATION_MODE, SERVICE_ENABLE_RFID_AUTHORIZATION_MODE, SERVICE_SET_CURRENT_PHASE, VALUE from .entity import AlfenEntity from homeassistant.config_entries import ConfigEntry @@ -19,6 +21,9 @@ from homeassistant.core import HomeAssistant, callback from . import DOMAIN as ALFEN_DOMAIN +import voluptuous as vol + + _LOGGER = logging.getLogger(__name__) @@ -228,6 +233,27 @@ async def async_setup_entry( async_add_entities(selects) + platform = entity_platform.current_platform.get() + + platform.async_register_entity_service( + SERVICE_SET_CURRENT_PHASE, + { + vol.Required("phase"): str, + }, + "async_set_current_phase", + ) + + platform.async_register_entity_service( + SERVICE_ENABLE_RFID_AUTHORIZATION_MODE, + {}, + "async_enable_rfid_auth_mode", + ) + + platform.async_register_entity_service( + SERVICE_DISABLE_RFID_AUTHORIZATION_MODE, + {}, + "async_disable_rfid_auth_mode", + ) class AlfenSelect(AlfenEntity, SelectEntity): """Define Alfen select.""" @@ -275,3 +301,20 @@ async def async_update(self): def _async_update_attrs(self) -> None: """Update select attributes.""" self._attr_current_option = self._get_current_option() + + async def async_set_current_phase(self, phase): + """Set the current phase.""" + await self._device.set_current_phase(phase) + await self.async_select_option(phase) + + async def async_enable_rfid_auth_mode(self): + """Enable RFID authorization mode.""" + await self._device.set_rfid_auth_mode(True) + await self.update_state(self.entity_description.api_param, 2) + self.async_write_ha_state() + + async def async_disable_rfid_auth_mode(self): + """Disable RFID authorization mode.""" + await self._device.set_rfid_auth_mode(False) + await self.update_state(self.entity_description.api_param, 0) + self.async_write_ha_state() \ No newline at end of file diff --git a/custom_components/alfen_wallbox/sensor.py b/custom_components/alfen_wallbox/sensor.py index df5c8f0..f76533e 100644 --- a/custom_components/alfen_wallbox/sensor.py +++ b/custom_components/alfen_wallbox/sensor.py @@ -33,14 +33,6 @@ from .const import ( ID, SERVICE_REBOOT_WALLBOX, - SERVICE_SET_CURRENT_LIMIT, - SERVICE_ENABLE_RFID_AUTHORIZATION_MODE, - SERVICE_DISABLE_RFID_AUTHORIZATION_MODE, - SERVICE_SET_CURRENT_PHASE, - SERVICE_ENABLE_PHASE_SWITCHING, - SERVICE_DISABLE_PHASE_SWITCHING, - SERVICE_SET_GREEN_SHARE, - SERVICE_SET_COMFORT_POWER, VALUE, ) @@ -610,62 +602,6 @@ async def async_setup_entry( "async_reboot_wallbox", ) - platform.async_register_entity_service( - SERVICE_SET_CURRENT_LIMIT, - { - vol.Required("limit"): cv.positive_int, - }, - "async_set_current_limit", - ) - - platform.async_register_entity_service( - SERVICE_SET_CURRENT_PHASE, - { - vol.Required("phase"): str, - }, - "async_set_current_phase", - ) - - platform.async_register_entity_service( - SERVICE_ENABLE_RFID_AUTHORIZATION_MODE, - {}, - "async_enable_rfid_auth_mode", - ) - - platform.async_register_entity_service( - SERVICE_DISABLE_RFID_AUTHORIZATION_MODE, - {}, - "async_disable_rfid_auth_mode", - ) - - platform.async_register_entity_service( - SERVICE_ENABLE_PHASE_SWITCHING, - {}, - "async_enable_phase_switching", - ) - - platform.async_register_entity_service( - SERVICE_DISABLE_PHASE_SWITCHING, - {}, - "async_disable_phase_switching", - ) - - platform.async_register_entity_service( - SERVICE_SET_GREEN_SHARE, - { - vol.Required(VALUE): cv.positive_int, - }, - "async_set_green_share", - ) - - platform.async_register_entity_service( - SERVICE_SET_COMFORT_POWER, - { - vol.Required(VALUE): cv.positive_int, - }, - "async_set_comfort_power", - ) - class AlfenMainSensor(AlfenEntity): def __init__(self, device: AlfenDevice, description: AlfenSensorDescription) -> None: @@ -707,41 +643,11 @@ async def async_reboot_wallbox(self): """Reboot the wallbox.""" await self._device.reboot_wallbox() - async def async_set_current_limit(self, limit): - """Set the current limit.""" - await self._device.set_current_limit(limit) - - async def async_enable_rfid_auth_mode(self): - """Enable RFID authorization mode.""" - await self._device.set_rfid_auth_mode(True) - - async def async_disable_rfid_auth_mode(self): - """Disable RFID authorization mode.""" - await self._device.set_rfid_auth_mode(False) - async def async_update(self): """Update the sensor.""" await self._device.async_update() - async def async_set_current_phase(self, phase): - """Set the current phase.""" - await self._device.set_current_phase(phase) - - async def async_enable_phase_switching(self): - """Enable phase switching.""" - await self._device.set_phase_switching(True) - - async def async_disable_phase_switching(self): - """Disable phase switching.""" - await self._device.set_phase_switching(False) - - async def async_set_green_share(self, value): - """Set the green share.""" - await self._device.set_green_share(value) - async def async_set_comfort_power(self, value): - """Set the comfort power.""" - await self._device.set_comfort_power(value) @property def device_info(self): diff --git a/custom_components/alfen_wallbox/services.yaml b/custom_components/alfen_wallbox/services.yaml index 7607be8..d439e83 100644 --- a/custom_components/alfen_wallbox/services.yaml +++ b/custom_components/alfen_wallbox/services.yaml @@ -10,61 +10,65 @@ set_current_limit: fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "number.wallbox_current_limit" limit: description: New limit. + example: 16 enable_rfid_authorization_mode: description: Enables RFID auth mode fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "select.wallbox_authorization_mode" disable_rfid_authorization_mode: description: Disables RFID auth mode fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "select.wallbox_authorization_mode" set_current_phase: - description: Set current phase mapping to L1 + description: Set current phase mapping fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "select.wallbox_active_load_balancing_phase_connection" phase: description: phase. + example: "L1" enable_phase_switching: description: Enable phase switching fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "switch.wallbox_enable_phase_switching" disable_phase_switching: description: Disable phase switching fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "switch.wallbox_enable_phase_switching" set_green_share: description: Set Green Share Percentage fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "number.wallbox_solar_green_share" value: description: New value. + example: 80 set_comfort_power: description: Set Solar Comfort Power fields: entity_id: description: Name(s) of entities to change. - example: "alfen_wallbox.garage" + example: "number.wallbox_solar_comfort_level" value: - description: New value. \ No newline at end of file + description: New value. + example: 1400 \ No newline at end of file diff --git a/custom_components/alfen_wallbox/switch.py b/custom_components/alfen_wallbox/switch.py index a09f8b6..1907870 100644 --- a/custom_components/alfen_wallbox/switch.py +++ b/custom_components/alfen_wallbox/switch.py @@ -3,7 +3,9 @@ from dataclasses import dataclass from typing import Any, Final -from .const import ID, VALUE +from homeassistant.helpers import entity_platform + +from .const import ID, SERVICE_DISABLE_PHASE_SWITCHING, SERVICE_ENABLE_PHASE_SWITCHING, VALUE from .alfen import AlfenDevice from .entity import AlfenEntity @@ -55,6 +57,20 @@ async def async_setup_entry( async_add_entities(switches) + platform = entity_platform.current_platform.get() + + platform.async_register_entity_service( + SERVICE_ENABLE_PHASE_SWITCHING, + {}, + "async_enable_phase_switching", + ) + + platform.async_register_entity_service( + SERVICE_DISABLE_PHASE_SWITCHING, + {}, + "async_disable_phase_switching", + ) + class AlfenSwitchSensor(AlfenEntity, SwitchEntity): """Define an Alfen binary sensor.""" @@ -95,3 +111,13 @@ async def async_turn_off(self, **kwargs: Any) -> None: """Turn the entity off.""" await self.update_state(self.entity_description.api_param, 0) await self._device.async_update() + + async def async_enable_phase_switching(self): + """Enable phase switching.""" + await self._device.set_phase_switching(True) + await self.async_turn_on() + + async def async_disable_phase_switching(self): + """Disable phase switching.""" + await self._device.set_phase_switching(False) + await self.async_turn_off() From c1c412dc77ff082accbf8cb49ea330a0d670d378 Mon Sep 17 00:00:00 2001 From: Tuen Lee Date: Wed, 9 Aug 2023 19:48:18 +0200 Subject: [PATCH 3/3] add extra language Czech, Hungarian, Icelandic --- custom_components/alfen_wallbox/select.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/custom_components/alfen_wallbox/select.py b/custom_components/alfen_wallbox/select.py index 0be5871..2e70341 100644 --- a/custom_components/alfen_wallbox/select.py +++ b/custom_components/alfen_wallbox/select.py @@ -86,18 +86,22 @@ class AlfenSelectDescription(SelectEntityDescription, AlfenSelectDescriptionMixi } DISPLAY_LANGUAGE_DICT: Final[dict[str, str]] = { - "English": "en_GB", + "Czech": "cz_CZ", + "Danish": "da_DK", "Dutch": "nl_NL", - "German": "de_DE", + "English": "en_GB", + "Finnish": "fi_FI", "French": "fr_FR", + "German": "de_DE", + "Hungarian": "hu_HU", + "Icelandic": "is_IS", "Italian": "it_IT", "Norwegian": "no_NO", - "Finnish": "fi_FI", + "Polish": "pl_PL", "Portuguese": "pt_PT", + "Romanian": "ro_RO", "Spanish": "es_ES", "Swedish": "sv_SE", - "Polish": "pl_PL", - "Danish": "da_DK", } ALLOWED_PHASE_DICT: Final[dict[str, int]] = {