From 861954ba3effd6eb4a80a022ce7431482a51ed7b Mon Sep 17 00:00:00 2001 From: maslyankov Date: Tue, 7 Jan 2025 00:44:11 +0200 Subject: [PATCH] Enhance helper functions for register value manipulation - Added `make_32bit` and `split_to_16bit` functions to facilitate conversion between 16-bit and 32-bit register values using struct packing. - Updated `value_to_reg` method in `NumberRWSensor` to utilize `split_to_16bit` for handling two 16-bit addresses. - Modified `reg_to_value` method in `Sensor` class to use `make_32bit` for combining two 16-bit registers into a single 32-bit value, improving support for signed values. --- src/sunsynk/helpers.py | 28 ++++++++++++++++++++++++---- src/sunsynk/rwsensors.py | 8 +++++--- src/sunsynk/sensors.py | 15 ++++++++++----- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/sunsynk/helpers.py b/src/sunsynk/helpers.py index d1a71ac1..27829252 100644 --- a/src/sunsynk/helpers.py +++ b/src/sunsynk/helpers.py @@ -2,7 +2,8 @@ import logging import math -from typing import Any +from typing import Any, Tuple +import struct _LOGGER = logging.getLogger(__name__) @@ -49,13 +50,20 @@ def as_num(val: ValType) -> float | int: def signed(val: int | float, bits: int = 16) -> int | float: - """Convert value to signed int.""" + """Convert value to signed int using struct packing.""" + if isinstance(val, float): + return val + + if bits == 16: + return struct.unpack('h', struct.pack('H', val))[0] + if bits == 32: + return struct.unpack('i', struct.pack('I', val))[0] + + # Fallback for non-standard bit lengths sign_bit = 1 << (bits - 1) if val < sign_bit: return val return val - (1 << bits) - # 16-bit only (old) - # return val if val <= 0x7FFF else val - 0x10000 def slug(name: str) -> str: @@ -125,3 +133,15 @@ def hex_str(regs: RegType, address: RegType | None = None) -> str: if address: res = (f"{k}={v}" for k, v in zip(address, res, strict=True)) return f"{{{' '.join(res)}}}" + + +def make_32bit(low16_value: int, high16_value: int, signed: bool = True) -> int: + """Convert two 16-bit registers into a 32-bit value using struct packing.""" + fmt = ' Tuple[int, int]: + """Split a 32-bit value into two 16-bit values using struct unpacking.""" + fmt = ' list[Sensor]: return [s for s in (self.min, self.max) if isinstance(s, Sensor)] def value_to_reg(self, value: ValType, resolve: ResolveType) -> RegType: - """Get the reg value from a display value, or the current reg value if out of range.""" + """Get the reg value from a display value.""" fval = float(value) # type:ignore minv = resolve_num(resolve, self.min, 0) maxv = resolve_num(resolve, self.max, 100) val = int(max(minv, min(maxv, fval)) / abs(self.factor)) + if len(self.address) == 1: if val < 0: val = 0x10000 + val return self.reg(val) if len(self.address) == 2: - return self.reg(val & 0xFFFF, int(val >> 16)) + low, high = split_to_16bit(val, signed=self.factor < 0) + return self.reg(low, high) raise NotImplementedError(f"Address length not supported: {self.address}") diff --git a/src/sunsynk/sensors.py b/src/sunsynk/sensors.py index e0244a9c..5aa5cd33 100644 --- a/src/sunsynk/sensors.py +++ b/src/sunsynk/sensors.py @@ -14,6 +14,7 @@ int_round, signed, slug, + make_32bit, ) _LOGGER = logging.getLogger(__name__) @@ -38,11 +39,15 @@ def id(self) -> str: def reg_to_value(self, regs: RegType) -> ValType: """Return the value from the registers.""" regs = self.masked(regs) - val: NumType = regs[0] - if len(regs) > 1: - val += regs[1] << 16 - if self.factor < 0: # Indicates this register is signed - val = signed(val, bits=16 * len(regs)) + if len(regs) == 1: + val = regs[0] + if self.factor < 0: # Indicates this register is signed + val = signed(val, bits=16) + elif len(regs) == 2: + val = make_32bit(regs[0], regs[1], signed=self.factor < 0) + else: + raise ValueError(f"Unsupported register length: {len(regs)}") + val = int_round(val * abs(self.factor)) _LOGGER.debug("%s=%s%s %s", self.id, val, self.unit, regs) return val