Skip to content

Commit

Permalink
Enhance helper functions for register value manipulation
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
maslyankov committed Jan 6, 2025
1 parent 4bf51f2 commit 861954b
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 12 deletions.
28 changes: 24 additions & 4 deletions src/sunsynk/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import logging
import math
from typing import Any
from typing import Any, Tuple
import struct

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 = '<i' if signed else '<I'
return struct.unpack(fmt, struct.pack('<2H', low16_value, high16_value))[0]


def split_to_16bit(value: int, signed: bool = True) -> Tuple[int, int]:
"""Split a 32-bit value into two 16-bit values using struct unpacking."""
fmt = '<i' if signed else '<I'
return struct.unpack('<2H', struct.pack(fmt, value))
8 changes: 5 additions & 3 deletions src/sunsynk/rwsensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import attrs
from mqtt_entity.utils import BOOL_OFF, BOOL_ON

from sunsynk.helpers import NumType, RegType, SSTime, ValType, as_num, hex_str
from sunsynk.helpers import NumType, RegType, SSTime, ValType, as_num, hex_str, split_to_16bit
from sunsynk.sensors import Sensor

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -68,17 +68,19 @@ def dependencies(self) -> 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}")


Expand Down
15 changes: 10 additions & 5 deletions src/sunsynk/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
int_round,
signed,
slug,
make_32bit,
)

_LOGGER = logging.getLogger(__name__)
Expand All @@ -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
Expand Down

0 comments on commit 861954b

Please sign in to comment.