Skip to content

Commit

Permalink
Merge pull request #511 from canton7/feature/h3-write
Browse files Browse the repository at this point in the history
Registers 41007-41011 must be read 1-by-1 on the H3, it seems
  • Loading branch information
canton7 authored Jan 13, 2024
2 parents 03cc0f1 + 7d83cfc commit 9fe66c8
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 27 deletions.
4 changes: 0 additions & 4 deletions custom_components/foxess_modbus/entities/invalid_ranges.py

This file was deleted.

68 changes: 48 additions & 20 deletions custom_components/foxess_modbus/inverter_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,37 @@
from .const import LAN
from .const import SK_HWR
from .const import STAR_H3
from .entities import invalid_ranges
from .entities.charge_periods import CHARGE_PERIODS
from .entities.entity_descriptions import ENTITIES
from .entities.modbus_charge_period_config import ModbusChargePeriodInfo

_LOGGER = logging.getLogger(__package__)


class SpecialRegisterConfig:
def __init__(
self,
*,
invalid_register_ranges: list[tuple[int, int]] | None = None,
individual_read_register_ranges: list[tuple[int, int]] | None = None
) -> None:
if invalid_register_ranges is None:
invalid_register_ranges = []
self.invalid_register_ranges = invalid_register_ranges

if individual_read_register_ranges is None:
individual_read_register_ranges = []
self.individual_read_register_ranges = individual_read_register_ranges


H1_AC1_REGISTERS = SpecialRegisterConfig(invalid_register_ranges=[(11096, 39999)])
# See https://github.com/nathanmarlor/foxess_modbus/discussions/503
H3_REGISTERS = SpecialRegisterConfig(
invalid_register_ranges=[(41001, 41006), (41012, 41013), (41015, 41015)],
individual_read_register_ranges=[(41007, 41011)],
)


class InverterModelConnectionTypeProfile:
"""Describes the capabilities of an inverter when connected to over a particular interface"""

Expand All @@ -38,16 +61,21 @@ def __init__(
inverter_model: str,
connection_type: str,
register_type: RegisterType,
invalid_register_ranges: list[tuple[int, int]],
special_registers: SpecialRegisterConfig,
) -> None:
self.inverter_model = inverter_model
self.connection_type = connection_type
self.register_type = register_type
self.invalid_register_ranges = invalid_register_ranges
self.special_registers = special_registers

def overlaps_invalid_range(self, start_address: int, end_address: int) -> bool:
"""Determines whether the given inclusive address range overlaps any invalid address ranges"""
return any(r[0] <= end_address and start_address <= r[1] for r in self.invalid_register_ranges)
return any(
r[0] <= end_address and start_address <= r[1] for r in self.special_registers.invalid_register_ranges
)

def is_individual_read(self, address: int) -> bool:
return any(r[0] <= address <= r[1] for r in self.special_registers.individual_read_register_ranges)

def create_entities(
self,
Expand Down Expand Up @@ -105,19 +133,19 @@ def add_connection_type(
self,
connection_type: str,
register_type: RegisterType,
invalid_register_ranges: list[tuple[int, int]] | None = None,
special_registers: SpecialRegisterConfig | None = None,
) -> "InverterModelProfile":
"""Add the given connection type to the profile"""

assert connection_type not in self.connection_types
if invalid_register_ranges is None:
invalid_register_ranges = []
if special_registers is None:
special_registers = SpecialRegisterConfig()

self.connection_types[connection_type] = InverterModelConnectionTypeProfile(
self.model,
connection_type,
register_type,
invalid_register_ranges,
special_registers,
)
return self

Expand All @@ -129,7 +157,7 @@ def add_connection_type(
.add_connection_type(
AUX,
RegisterType.INPUT,
invalid_register_ranges=invalid_ranges.H1_AC1,
special_registers=H1_AC1_REGISTERS,
)
.add_connection_type(
LAN,
Expand All @@ -139,7 +167,7 @@ def add_connection_type(
.add_connection_type(
AUX,
RegisterType.INPUT,
invalid_register_ranges=invalid_ranges.H1_AC1,
special_registers=H1_AC1_REGISTERS,
)
.add_connection_type(
LAN,
Expand All @@ -149,7 +177,7 @@ def add_connection_type(
.add_connection_type(
AUX,
RegisterType.INPUT,
invalid_register_ranges=invalid_ranges.H1_AC1,
special_registers=H1_AC1_REGISTERS,
)
.add_connection_type(
LAN,
Expand All @@ -163,34 +191,34 @@ def add_connection_type(
.add_connection_type(
LAN,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
)
.add_connection_type(
AUX,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
),
InverterModelProfile(AC3, r"^AC3-")
.add_connection_type(
LAN,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
)
.add_connection_type(
AUX,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
),
InverterModelProfile(AIO_H3, r"^AIO-H3-")
.add_connection_type(
AUX,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
)
.add_connection_type(
LAN,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
),
# Kuara 6.0-3-H: H3-6.0-E
# Kuara 8.0-3-H: H3-8.0-E
Expand All @@ -200,23 +228,23 @@ def add_connection_type(
InverterModelProfile(KUARA_H3, r"^Kuara [^-]+-3-H$").add_connection_type(
AUX,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
),
# Sonnenkraft:
# SK-HWR-8: H3-8.0-E
# (presumably there are other sizes also)
InverterModelProfile(SK_HWR, r"^SK-HWR-").add_connection_type(
AUX,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
),
# STAR
# STAR-H3-12.0-E: H3-12.0-E
# (presumably there are other sizes also)
InverterModelProfile(STAR_H3, r"^STAR-H3-").add_connection_type(
AUX,
RegisterType.HOLDING,
invalid_register_ranges=invalid_ranges.H3,
special_registers=H3_REGISTERS,
),
]
}
Expand Down
14 changes: 11 additions & 3 deletions custom_components/foxess_modbus/modbus_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,14 @@ def _create_read_ranges(self, max_read: int) -> Iterable[tuple[int, int]]:
read_size = 0
# TODO: Do we want to cache the result of this?
for address in sorted(self._data.keys()):
if start_address is None:
# This register must be read in a single individual read. Yield any ranges we've found so far,
# and yield just this register on its own
if self._connection_type_profile.is_individual_read(address):
if start_address is not None:
yield (start_address, read_size)
start_address, read_size = None, 0
yield (address, 1)
elif start_address is None:
start_address, read_size = address, 1
# If we're just increasing the previous read size by 1, then don't test whether we're extending
# the read over an invalid range (as we assume that registers we're reading to read won't be
Expand All @@ -270,7 +277,8 @@ def _create_read_ranges(self, max_read: int) -> Iterable[tuple[int, int]]:
start_address, read_size = address, 1

if read_size == max_read:
yield (start_address, read_size)
# (can't get here if start_address is None, as read_size would be 0
yield (start_address, read_size) # type: ignore
start_address, read_size = None, 0

if start_address is not None:
Expand All @@ -281,7 +289,7 @@ def register_modbus_entity(self, listener: ModbusControllerEntity) -> None:
for address in listener.addresses:
assert not self._connection_type_profile.overlaps_invalid_range(address, address), (
f"Entity {listener} address {address} overlaps an invalid range in "
f"{self._connection_type_profile.invalid_register_ranges}"
f"{self._connection_type_profile.special_registers.invalid_register_ranges}"
)
if address not in self._data:
self._data[address] = None
Expand Down

0 comments on commit 9fe66c8

Please sign in to comment.