Skip to content

Commit

Permalink
Attempt to fix problems with integer limits (#161)
Browse files Browse the repository at this point in the history
Related to #160
  • Loading branch information
yozik04 authored May 22, 2024
2 parents db099fe + f53d62b commit 9886999
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 18 deletions.
19 changes: 16 additions & 3 deletions nibe/connection/nibegw.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from operator import xor
import socket
import struct
from typing import Dict, Optional, Union
from typing import Dict, Literal, Optional, Union

from construct import (
Adapter,
Expand Down Expand Up @@ -99,6 +99,7 @@ def __init__(
listening_port: int = 9999,
read_retries: int = 3,
write_retries: int = 3,
table_processing_mode: Literal["permissive", "strict"] = "permissive",
) -> None:
super().__init__()

Expand All @@ -110,6 +111,8 @@ def __init__(
self._remote_read_port = remote_read_port
self._remote_write_port = remote_write_port

self._table_processing_mode = table_processing_mode

self._transport = None

self._send_lock = asyncio.Lock()
Expand Down Expand Up @@ -432,19 +435,29 @@ def _on_raw_coil_value(self, coil_address: int, raw_value: bytes) -> None:
self._on_coil_read_error(coil_address, raw_value, e)

def _on_raw_coil_set(self, data: dict[int, bytes]) -> None:
successful_coil_data = []
decode_exception_occurred = False
while data:
coil_address = min(data.keys())
raw_value = data.pop(coil_address)
coil = self._heatpump.get_coil_by_address(coil_address)
try:
coil = self._heatpump.get_coil_by_address(coil_address)
if coil.size in ("u32", "s32"):
raw_value = raw_value + data.pop(coil_address + 1, b"")
coil_data = self.coil_encoder.decode(coil, raw_value)
self._on_coil_read_success(coil_data)
successful_coil_data.append(coil_data)
except DecodeException as e:
self._on_coil_read_error(coil_address, raw_value, e)
decode_exception_occurred = True
except NibeException as e:
self._on_coil_read_error(coil_address, raw_value, e)

if self._table_processing_mode == "permissive" or (
self._table_processing_mode == "strict" and not decode_exception_occurred
):
for coil_data in successful_coil_data:
self._on_coil_read_success(coil_data)

def _on_coil_value(self, coil_address: int, value: Union[float, int, str]) -> None:
try:
coil = self._heatpump.get_coil_by_address(coil_address)
Expand Down
16 changes: 16 additions & 0 deletions nibe/data/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -497,5 +497,21 @@
"name": "bt71-ext-return-temp-40152"
}
}
},
{
"description": "Limit allowed Brine In/Out temperature values for F1155/F1255",
"files": [
"f1155_f1255.json"
],
"data": {
"40015": {
"min": -400,
"max": 800
},
"40016": {
"min": -400,
"max": 800
}
}
}
]
24 changes: 14 additions & 10 deletions nibe/data/f1155_f1255.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,19 @@
"unit": "\u00b0C",
"size": "s16",
"factor": 10,
"name": "eb100-ep14-bt10-brine-in-temp-40015"
"name": "eb100-ep14-bt10-brine-in-temp-40015",
"min": -400,
"max": 800
},
"40016": {
"title": "EB100-EP14-BT11 Brine Out Temp",
"info": "Brine out temperature, BT11",
"unit": "\u00b0C",
"size": "s16",
"factor": 10,
"name": "eb100-ep14-bt11-brine-out-temp-40016"
"name": "eb100-ep14-bt11-brine-out-temp-40016",
"min": -400,
"max": 800
},
"40017": {
"title": "EB100-EP14-BT12 Condensor Out",
Expand Down Expand Up @@ -425,14 +429,6 @@
"factor": 10,
"name": "bt70-hw-comfort-supply-temp-40147"
},
"40152": {
"title": "BT71 Ext. Return Temp",
"info": "External return temperature, BT71",
"unit": "\u00b0C",
"size": "s16",
"factor": 10,
"name": "bt71-ext-return-temp-40152"
},
"40155": {
"title": "EQ1-BT57 Collector Temp.",
"info": "External collector temperature, BT57, for ACS",
Expand Down Expand Up @@ -9558,5 +9554,13 @@
"factor": 1,
"name": "aux-ers-fire-place-guard-49430",
"write": true
},
"40152": {
"title": "BT71 Ext. Return Temp",
"info": "External return temperature, BT71",
"unit": "\u00b0C",
"size": "s16",
"factor": 10,
"name": "bt71-ext-return-temp-40152"
}
}
30 changes: 27 additions & 3 deletions tests/connection/test_encoders.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest

from nibe.coil import Coil
from nibe.connection.encoders import CoilDataEncoderModbus, CoilDataEncoderNibeGw


Expand All @@ -25,7 +26,7 @@
("s32", b"(\x06\x00\x00", 1576, True),
],
)
def test_nibegw_decode(
def test_nibegw_decode_raw_value(
size,
raw,
raw_value,
Expand All @@ -37,6 +38,29 @@ def test_nibegw_decode(
assert CoilDataEncoderNibeGw(False).decode_raw_value(size, raw) == raw_value


@pytest.mark.parametrize(
"size, raw, expected, word_swap",
[
# Test with integer limits
("u8", b"\xFF", None, None),
("s8", b"\x80", None, None),
("u16", b"\xFF\xFF", None, None),
("s16", b"\x00\x80", None, None),
("s32", b"\x00\x00\x00\x80", None, True),
("s32", b"\x00\x80\x00\x00", None, False),
],
)
def test_nibegw_decode(size, raw, expected, word_swap):
coil = Coil(address=1, name="test", title="test", size=size)

if word_swap in (True, None):
result = CoilDataEncoderNibeGw(True).decode(coil, raw).value
assert (result is None and expected is None) or (result == expected)
if word_swap in (False, None):
result = CoilDataEncoderNibeGw(False).decode(coil, raw).value
assert (result is None and expected is None) or (result == expected)


@pytest.mark.parametrize(
"size, raw, raw_value, word_swap",
[
Expand Down Expand Up @@ -89,7 +113,7 @@ def test_nibegw_encode(
("s32", [0xF9D8, 0xFFFF], -0x628, True),
],
)
def test_modbus_decode(
def test_modbus_decode_raw_value(
size,
raw,
raw_value,
Expand Down Expand Up @@ -120,7 +144,7 @@ def test_modbus_decode(
("s32", [0xF9D8, 0xFFFF], -0x628, True),
],
)
def test_modbus_encode(
def test_modbus_encode_raw_value(
size,
raw,
raw_value,
Expand Down
36 changes: 34 additions & 2 deletions tests/connection/test_nibegw.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,11 @@ async def test_read_product_info(nibegw: NibeGW):


@pytest.mark.parametrize(
("raw", "calls"),
("raw", "table_processing_mode", "calls"),
[
(
"5c00206850c9af0000889c7100a9a90a00a3a91400aba90000939c0000949c0000919c3c00929c00008f9c0000909c00003ab95000ada94600a7a91400faa90200ffff0000ffff0000ffff0000ffff0000ffff0000f0",
"strict",
[
(40072, 11.3),
(40079, 0.0),
Expand All @@ -168,8 +169,34 @@ async def test_read_product_info(nibegw: NibeGW):
(47418, 80),
],
),
(
"5c00206850c9af0000889c7100a9a90a00a3a91400aba90000939c0000949c0000919c3c00929c00008f9c0000909c0000 3ab97000 ada94600 a7a91400 faa90200 ffff0000 ffff0000 ffff0000 ffff0000 ffff0000 d0",
"permissive",
[
(40072, 11.3),
(40079, 0.0),
(40081, 6.0),
(40083, 0.0),
(43427, "STOPPED"),
(43431, "ON"),
(43433, "OFF"),
(43435, "OFF"),
(43437, 70),
(43514, 2),
(45001, 0),
# (47418, 80), # This coil will be ignored because of decoding error
],
),
(
"5c00206850c9af0000889c7100a9a90a00a3a91400aba90000939c0000949c0000919c3c00929c00008f9c0000909c0000 3ab97000 ada94600 a7a91400 faa90200 ffff0000 ffff0000 ffff0000 ffff0000 ffff0000 d0",
"strict",
[
# All will be ignored because of single decoding error
],
),
(
"5c00206850 489ce400 4c9ce300 4e9ca101 889c4500 d5a1ae00 d6a1a300 fda718f8 c5a5ad98c6a50100 cda5d897cea50100 cfa51fb7d0a50600 98a96d23 99a90000 a0a9cf05 a1a90000 9ca9a01a 9da90000 449c4500 e5",
"strict",
[
(40004, 6.9),
(40008, 22.8),
Expand Down Expand Up @@ -199,8 +226,13 @@ async def test_read_product_info(nibegw: NibeGW):
],
)
async def test_read_multiple_with_u32(
nibegw: NibeGW, heatpump: HeatPump, raw: str, calls: list[tuple[int, Any]]
nibegw: NibeGW,
heatpump: HeatPump,
raw: str,
table_processing_mode: str,
calls: list[tuple[int, Any]],
):
nibegw._table_processing_mode = table_processing_mode
on_coil_update_mock = Mock()
heatpump.subscribe("coil_update", on_coil_update_mock)
nibegw.datagram_received(
Expand Down

0 comments on commit 9886999

Please sign in to comment.