Skip to content

Commit

Permalink
Add support for battery pause mode
Browse files Browse the repository at this point in the history
  • Loading branch information
cdpuk committed Feb 4, 2024
1 parent f7696f1 commit 3a7abb9
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 24 deletions.
1 change: 1 addition & 0 deletions custom_components/givenergy_local/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Platform.BINARY_SENSOR,
Platform.NUMBER,
Platform.SENSOR,
Platform.SELECT,
Platform.SWITCH,
]

Expand Down
5 changes: 4 additions & 1 deletion custom_components/givenergy_local/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ async def _async_update_data(self) -> Plant:
"""Fetch data from the inverter."""
if not self.client.connected:
await self.client.connect()
self.require_full_refresh = True
await self.client.detect_plant()
self.require_full_refresh = False
self.last_full_refresh = datetime.utcnow()
return self.client.plant

if self.last_full_refresh < (datetime.utcnow() - _FULL_REFRESH_INTERVAL):
self.require_full_refresh = True
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,35 @@ async def connect(self) -> None:
self.connected = True
_logger.info("Connection established to %s:%d", self.host, self.port)

async def detect_plant(self, timeout: int = 1, retries: int = 3) -> None:
"""Detect inverter capabilities that influence how subsequent requests are made."""
_logger.info("Detectig plant")

# Refresh the core set of registers that work across all inverters
await self.refresh_plant(True, timeout=timeout, retries=retries)

# Use that to detect the number of batteries
self.plant.detect_batteries()
_logger.info("Batteries detected: %d", self.plant.number_batteries)

# Some devices support additional registers
# When unsupported, devices appear to simple ignore requests
possible_additional_holding_registers = [180]
for hr in possible_additional_holding_registers:
try:
reqs = commands.refresh_additional_holding_registers(hr)
await self.execute(reqs, timeout=timeout, retries=retries)
_logger.info(
"Detected additional holding register support (base_register=%d)",
hr,
)
self.plant.additional_holding_registers.append(hr)
except asyncio.TimeoutError:
_logger.debug(
"Inverter did not respond to additional holder register query (base_register=%d)",
hr,
)

async def close(self) -> None:
"""Disconnect from the remote host and clean up tasks and queues."""
if not self.connected:
Expand Down Expand Up @@ -124,10 +153,6 @@ async def refresh_plant(
full_refresh, self.plant.number_batteries, max_batteries
)
await self.execute(reqs, timeout=timeout, retries=retries)

if full_refresh:
self.plant.detect_batteries()

return self.plant

async def watch_plant(
Expand All @@ -142,7 +167,6 @@ async def watch_plant(
"""Refresh data about the Plant."""
await self.connect()
await self.refresh_plant(True, max_batteries=max_batteries)
self.plant.detect_batteries()
while True:
if handler:
handler()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,25 @@ class RegisterMap:
BATTERY_PAUSE_MODE = 318


def refresh_additional_holding_registers(
base_register: int,
) -> list[TransparentRequest]:
"""Requests one specific set of holding registers.
This is intended to be used in cases where registers may or may not be present,
depending on device capabilities."""
return [
ReadHoldingRegistersRequest(
base_register=base_register, register_count=60, slave_address=0x32
)
]


def refresh_plant_data(
complete: bool, number_batteries: int = 1, max_batteries: int = 5
complete: bool,
number_batteries: int = 1,
max_batteries: int = 5,
additional_holding_registers: Optional[list[int]] = None,
) -> list[TransparentRequest]:
"""Refresh plant data."""
requests: list[TransparentRequest] = [
Expand All @@ -77,16 +94,16 @@ 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
)
)

if additional_holding_registers:
for hr in additional_holding_registers:
requests.extend(refresh_additional_holding_registers(hr))

number_batteries = max_batteries
for i in range(number_batteries):
requests.append(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Plant(GivEnergyBaseModel):
"""Representation of a complete GivEnergy plant."""

register_caches: dict[int, RegisterCache] = {}
additional_holding_registers: list[int] = []
inverter_serial_number: str = ""
data_adapter_serial_number: str = ""
number_batteries: int = 0
Expand Down Expand Up @@ -93,7 +94,6 @@ def detect_batteries(self) -> None:
assert Battery.from_orm(self.register_caches[i + 0x32]).is_valid()
except (KeyError, AssertionError):
break
_logger.debug("Updating connected battery count to %d", i)
self.number_batteries = i

@property
Expand Down
18 changes: 7 additions & 11 deletions custom_components/givenergy_local/select.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Select platform."""

from __future__ import annotations

from homeassistant.components.select import SelectEntity, SelectEntityDescription
Expand All @@ -10,18 +11,16 @@
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

from .givenergy_modbus.model.inverter import BatteryPauseMode

_BATTERY_PAUSE_MODE_OPTIONS = {
BatteryPauseMode.DISABLED: "DISABLED",
BatteryPauseMode.PAUSE_CHARGE: "PAUSE_CHARGE",
BatteryPauseMode.PAUSE_DISCHARGE: "PAUSE_DISCHARGE",
BatteryPauseMode.PAUSE_BOTH: "PAUSE_BOTH",
BatteryPauseMode.DISABLED: "Not Paused",
BatteryPauseMode.PAUSE_CHARGE: "Pause Charge",
BatteryPauseMode.PAUSE_DISCHARGE: "Pause Discharge",
BatteryPauseMode.PAUSE_BOTH: "Pause Charge & Discharge",
}


Expand All @@ -42,10 +41,7 @@ async def async_setup_entry(
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:
if coordinator.data.inverter.battery_pause_mode is not None:
entities += BatteryPauseModeSelect(
coordinator,
config_entry,
Expand Down

0 comments on commit 3a7abb9

Please sign in to comment.