Skip to content

Commit

Permalink
Correct parsing of RMU data fields (#145)
Browse files Browse the repository at this point in the history
- Setting RMU target temperature needs 16 bit signed values
- Setpoint for offsets with RMU does not have any scaling applied
- Correct jump of outdoor temperature when received from RMU since pump
seem to apply the scaling in reverse
  • Loading branch information
yozik04 authored Jan 3, 2024
2 parents ad84e91 + c109a85 commit 7d23e1d
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 10 deletions.
49 changes: 40 additions & 9 deletions nibe/connection/nibegw.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,37 @@ def _build(self, obj, stream, context, path):
)


class FixedPointStrange(Adapter):
"""Strange fixed point calculation
The pump seems to apply the offset in reverse when
crossing over to negative values. My guess it's some
bug in firmware that was never resolved, and can't
be fixed.
"""

def __init__(self, subcon, scale, offset, ndigits=1) -> None:
super().__init__(subcon)
self._offset = offset
self._scale = scale
self._ndigits = ndigits

def _decode(self, obj, context, path):
scaled = obj * self._scale
if scaled >= self._offset:
scaled += self._offset
else:
scaled -= self._offset
return round(scaled, self._ndigits)

def _encode(self, obj, context, path):
if obj >= 0:
val = obj - self._offset
else:
val = obj + self._offset
return val / self._scale


class FixedPoint(Adapter):
def __init__(self, subcon, scale, offset, ndigits=1) -> None:
super().__init__(subcon)
Expand Down Expand Up @@ -532,31 +563,31 @@ def _encode(self, obj, context, path):
"hw_production" / Flag,
),
),
"bt1_outdoor_temperature" / FixedPoint(Int16sl, 0.1, -0.5),
"bt1_outdoor_temperature" / FixedPointStrange(Int16sl, 0.1, -0.5),
"bt7_hw_top" / FixedPoint(Int16sl, 0.1, -0.5),
"setpoint_or_offset_s1"
/ IfThenElse(
lambda this: this.flags.use_room_sensor_s1,
FixedPoint(Int8ub, 0.1, 5.0),
FixedPoint(Int8sb, 0.1, 0),
FixedPoint(Int8sb, 1.0, 0),
),
"setpoint_or_offset_s2"
/ IfThenElse(
lambda this: this.flags.use_room_sensor_s2,
FixedPoint(Int8ub, 0.1, 5.0),
FixedPoint(Int8sb, 0.1, 0),
FixedPoint(Int8sb, 1.0, 0),
),
"setpoint_or_offset_s3"
/ IfThenElse(
lambda this: this.flags.use_room_sensor_s3,
FixedPoint(Int8ub, 0.1, 5.0),
FixedPoint(Int8sb, 0.1, 0),
FixedPoint(Int8sb, 1.0, 0),
),
"setpoint_or_offset_s4"
/ IfThenElse(
lambda this: this.flags.use_room_sensor_s4,
FixedPoint(Int8ub, 0.1, 5.0),
FixedPoint(Int8sb, 0.1, 0),
FixedPoint(Int8sb, 1.0, 0),
),
"bt50_room_temp_sX" / FixedPoint(Int16sl, 0.1, -0.5),
"temporary_lux" / Int8ub,
Expand Down Expand Up @@ -699,10 +730,10 @@ def _encode(self, obj, context, path):
allow_cooling=0x04,
),
"OPERATIONAL_MODE": Int8ub,
"SETPOINT_S1": FixedPoint(Int8ub, 0.1, 0.0),
"SETPOINT_S2": FixedPoint(Int8ub, 0.1, 0.0),
"SETPOINT_S3": FixedPoint(Int8ub, 0.1, 0.0),
"SETPOINT_S4": FixedPoint(Int8ub, 0.1, 0.0),
"SETPOINT_S1": FixedPoint(Int16sl, 0.1, 0.0),
"SETPOINT_S2": FixedPoint(Int16sl, 0.1, 0.0),
"SETPOINT_S3": FixedPoint(Int16sl, 0.1, 0.0),
"SETPOINT_S4": FixedPoint(Int16sl, 0.1, 0.0),
},
default=Select(
Int16ul,
Expand Down
95 changes: 94 additions & 1 deletion tests/connection/test_nibegw_message_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from construct import ChecksumError, Container, Int16sl, Int32ul
import pytest

from nibe.connection.nibegw import Request, Response
from nibe.connection.nibegw import Request, Response, RmuData


class MessageResponseParsingTestCase(unittest.TestCase):
Expand Down Expand Up @@ -242,6 +242,99 @@ def _parse_hexlified_raw_message(txt_raw):
return value


class MessageRmuDataParsingTestCase(unittest.TestCase):
def test_negative_outdoor(self):
data = self.parse("faff12029b9b9bff1200000000000003791534000300000100")

assert data == Container(
alarm=0,
bt1_outdoor_temperature=-0.1,
bt50_room_temp_sX=1.3,
bt7_hw_top=52.5,
clock_time_hour=21,
clock_time_min=52,
fan_mode=0,
fan_time_hour=0,
fan_time_min=0,
flags=Container(
unknown_8000=False,
unknown_4000=False,
unknown_2000=False,
unknown_1000=False,
unknown_0800=False,
allow_cooling=False,
allow_heating=True,
allow_additive_heating=True,
use_room_sensor_s4=False,
use_room_sensor_s3=True,
use_room_sensor_s2=True,
use_room_sensor_s1=True,
unknown_0008=True,
unknown_0004=False,
unknown_0002=False,
hw_production=True,
),
hw_time_hour=0,
hw_time_min=0,
operational_mode=0,
setpoint_or_offset_s1=20.5,
setpoint_or_offset_s2=20.5,
setpoint_or_offset_s3=20.5,
setpoint_or_offset_s4=-1.0,
temporary_lux=0,
unknown4=b"\x03",
unknown5=b"\x01\x00",
)

def test_zero_outdoor(self):
data = self.parse("050012029b9b9bff1200000000000003791534000300000100")

assert data == Container(
alarm=0,
bt1_outdoor_temperature=0.0,
bt50_room_temp_sX=1.3,
bt7_hw_top=52.5,
clock_time_hour=21,
clock_time_min=52,
fan_mode=0,
fan_time_hour=0,
fan_time_min=0,
flags=Container(
unknown_8000=False,
unknown_4000=False,
unknown_2000=False,
unknown_1000=False,
unknown_0800=False,
allow_cooling=False,
allow_heating=True,
allow_additive_heating=True,
use_room_sensor_s4=False,
use_room_sensor_s3=True,
use_room_sensor_s2=True,
use_room_sensor_s1=True,
unknown_0008=True,
unknown_0004=False,
unknown_0002=False,
hw_production=True,
),
hw_time_hour=0,
hw_time_min=0,
operational_mode=0,
setpoint_or_offset_s1=20.5,
setpoint_or_offset_s2=20.5,
setpoint_or_offset_s3=20.5,
setpoint_or_offset_s4=-1.0,
temporary_lux=0,
unknown4=b"\x03",
unknown5=b"\x01\x00",
)

@staticmethod
def parse(txt_raw):
raw = binascii.unhexlify(txt_raw)
return RmuData.parse(raw)


class MessageRequestParsingTestCase(unittest.TestCase):
@staticmethod
def _parse_hexlified_raw_message(txt_raw):
Expand Down

0 comments on commit 7d23e1d

Please sign in to comment.