From f7696f12246a13e3dcc6992fa96c0f0097853a39 Mon Sep 17 00:00:00 2001 From: Chris Peka <24318424+cdpuk@users.noreply.github.com> Date: Tue, 30 Jan 2024 18:21:52 +0000 Subject: [PATCH] WIP --- custom_components/givenergy_local/const.py | 5 +- .../givenergy_modbus/client/commands.py | 16 ++++ .../givenergy_modbus/model/inverter.py | 14 ++++ custom_components/givenergy_local/select.py | 80 +++++++++++++++++++ 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 custom_components/givenergy_local/select.py diff --git a/custom_components/givenergy_local/const.py b/custom_components/givenergy_local/const.py index cdf7d02..3734c14 100755 --- a/custom_components/givenergy_local/const.py +++ b/custom_components/givenergy_local/const.py @@ -1,6 +1,6 @@ """Constants for the GivEnergy integration.""" -from enum import Enum +from enum import StrEnum from logging import Logger, getLogger DOMAIN = "givenergy_local" @@ -14,7 +14,7 @@ BATTERY_NOMINAL_VOLTAGE = 51.2 -class Icon(str, Enum): +class Icon(StrEnum): """Icon styles.""" PV = "mdi:solar-power" @@ -24,6 +24,7 @@ class Icon(str, Enum): BATTERY_TEMPERATURE = "mdi:thermometer" BATTERY_MINUS = "mdi:battery-minus" BATTERY_PLUS = "mdi:battery-plus" + BATTERY_PAUSE = "mdi:battery-clock" INVERTER = "mdi:flash" GRID_IMPORT = "mdi:transmission-tower-export" GRID_EXPORT = "mdi:transmission-tower-import" diff --git a/custom_components/givenergy_local/givenergy_modbus/client/commands.py b/custom_components/givenergy_local/givenergy_modbus/client/commands.py index 47c3f6d..203a030 100644 --- a/custom_components/givenergy_local/givenergy_modbus/client/commands.py +++ b/custom_components/givenergy_local/givenergy_modbus/client/commands.py @@ -6,6 +6,9 @@ from typing_extensions import deprecated # type: ignore[attr-defined] from custom_components.givenergy_local.givenergy_modbus.model import TimeSlot +from custom_components.givenergy_local.givenergy_modbus.model.inverter import ( + BatteryPauseMode, +) from custom_components.givenergy_local.givenergy_modbus.pdu import ( ReadHoldingRegistersRequest, ReadInputRegistersRequest, @@ -43,6 +46,7 @@ class RegisterMap: BATTERY_DISCHARGE_MIN_POWER_RESERVE = 114 CHARGE_TARGET_SOC = 116 REBOOT = 163 + BATTERY_PAUSE_MODE = 318 def refresh_plant_data( @@ -73,6 +77,11 @@ def refresh_plant_data( base_register=120, register_count=60, slave_address=0x32 ) ) + requests.append( + ReadHoldingRegistersRequest( + base_register=300, register_count=60, slave_address=0x32 + ) + ) requests.append( ReadInputRegistersRequest( base_register=120, register_count=60, slave_address=0x32 @@ -209,6 +218,13 @@ def set_battery_power_reserve(val: int) -> list[TransparentRequest]: ] +def set_battery_pause_mode(val: BatteryPauseMode) -> list[TransparentRequest]: + """Set the battery pause mode.""" + if not 0 <= val <= 3: + raise ValueError(f"Battery pause mode ({val}) must be in [0-3]") + return [WriteHoldingRegisterRequest(RegisterMap.BATTERY_PAUSE_MODE, val)] + + def _set_charge_slot( discharge: bool, idx: int, slot: Optional[TimeSlot] ) -> list[TransparentRequest]: diff --git a/custom_components/givenergy_local/givenergy_modbus/model/inverter.py b/custom_components/givenergy_local/givenergy_modbus/model/inverter.py index b05d9bc..e6201d1 100644 --- a/custom_components/givenergy_local/givenergy_modbus/model/inverter.py +++ b/custom_components/givenergy_local/givenergy_modbus/model/inverter.py @@ -96,6 +96,15 @@ class BatteryType(IntEnum): LITHIUM = 1 +class BatteryPauseMode(IntEnum): + """Battery pause mode.""" + + DISABLED = 0 + PAUSE_CHARGE = 1 + PAUSE_DISCHARGE = 2 + PAUSE_BOTH = 3 + + class PowerFactorFunctionModel(IntEnum): """Power Factor function model.""" @@ -233,6 +242,11 @@ class InverterRegisterGetter(RegisterGetter): "enable_standard_self_consumption_logic": Def(C.bool, None, HR(199)), "cmd_bms_flash_update": Def(C.bool, None, HR(200)), # + # Holding Registers, block 300-359 + # + "battery_pause_mode": Def(C.uint16, BatteryPauseMode, HR(318)), + "battery_pause_slot_1": Def(C.timeslot, None, HR(319), HR(320)), + # # Holding Registers, block 4080-4139 # "pv_power_setting": Def(C.uint32, None, HR(4107), HR(4108)), diff --git a/custom_components/givenergy_local/select.py b/custom_components/givenergy_local/select.py new file mode 100644 index 0000000..1d9f353 --- /dev/null +++ b/custom_components/givenergy_local/select.py @@ -0,0 +1,80 @@ +"""Select platform.""" +from __future__ import annotations + +from homeassistant.components.select import SelectEntity, SelectEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from custom_components.givenergy_local.givenergy_modbus.client.commands import ( + set_battery_pause_mode, +) + +from .givenergy_modbus.model.inverter import BatteryPauseMode, Generation, Model + +from . import GivEnergyUpdateCoordinator +from .const import DOMAIN, Icon +from .entity import InverterEntity + + +_BATTERY_PAUSE_MODE_OPTIONS = { + BatteryPauseMode.DISABLED: "DISABLED", + BatteryPauseMode.PAUSE_CHARGE: "PAUSE_CHARGE", + BatteryPauseMode.PAUSE_DISCHARGE: "PAUSE_DISCHARGE", + BatteryPauseMode.PAUSE_BOTH: "PAUSE_BOTH", +} + + +_BATTERY_PAUSE_MODE_DESCRIPTION = SelectEntityDescription( + key="battery_pause_mode", + options=list(_BATTERY_PAUSE_MODE_OPTIONS.values()), + icon=Icon.BATTERY_PAUSE, + name="Battery Pause Mode", +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up select entities.""" + coordinator: GivEnergyUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + entities: list[SelectEntity] = [] + + is_gen1 = coordinator.data.inverter.generation == Generation.GEN1 + is_aio = coordinator.data.inverter.model == Model.ALL_IN_ONE + + if not is_gen1 or is_aio: + entities += BatteryPauseModeSelect( + coordinator, + config_entry, + _BATTERY_PAUSE_MODE_DESCRIPTION, + ) + async_add_entities(entities) + + +class BatteryPauseModeSelect(InverterEntity, SelectEntity): + """Bubbles selection for spa devices that support 3 levels.""" + + def __init__( + self, + coordinator: GivEnergyUpdateCoordinator, + config_entry: ConfigEntry, + description: SelectEntityDescription, + ) -> None: + """Initialize thermostat.""" + super().__init__(coordinator, config_entry) + self.entity_description = description + self._attr_unique_id = f"{self.data.serial_number}_{description.key}" + + @property + def current_option(self) -> str | None: + """Return the selected entity option.""" + return _BATTERY_PAUSE_MODE_OPTIONS[self.data.battery_pause_mode] + + async def async_select_option(self, option: str) -> None: + """Change the selected option.""" + for val in BatteryPauseMode: + if option == _BATTERY_PAUSE_MODE_OPTIONS[val]: + await self.coordinator.execute(set_battery_pause_mode(val))