From 52677f08e9f95ced2b8e0f676d1ee8ebb9cdcd2f Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Tue, 7 Feb 2023 19:33:16 +0100 Subject: [PATCH 1/3] Catch invalid temperature for offline inverters --- omnikinverter/tcp.py | 9 ++++- tests/fixtures/tcp_reply_offline.data | Bin 0 -> 183 bytes tests/test_models.py | 46 ++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/tcp_reply_offline.data diff --git a/omnikinverter/tcp.py b/omnikinverter/tcp.py index 949eda8..7abb3bb 100644 --- a/omnikinverter/tcp.py +++ b/omnikinverter/tcp.py @@ -272,10 +272,17 @@ def int_to_bool(num: int) -> bool: "firmware_slave": None, } - result = {} + result: dict[str, Any] = {} for (name, extractor) in field_extractors.items(): value = getattr(tcp_data, name) + + # Set temperature to None if it matches 65326, this is returned + # when the inverter is "offline". + if name == "temperature" and value == 65326: + result["temperature"] = None + continue + if name == "ac_output": # Flatten the list of frequency+power AC objects diff --git a/tests/fixtures/tcp_reply_offline.data b/tests/fixtures/tcp_reply_offline.data new file mode 100644 index 0000000000000000000000000000000000000000..95bba5439edb7c50244442ee91ee9c4437ac68e4 GIT binary patch literal 183 zcmc~;>A2y bytes: assert inverter.firmware == "NL1-V1.0-0077-4" assert inverter.firmware_slave == "V2.0-0024" + async def test_inverter_tcp_offline(self) -> None: + """Test request from an Inverter (offline) - TCP source.""" + serial_number = 1608449224 + socket_mock = asynctest.SocketMock() + socket_mock.type = socket.SOCK_STREAM + + def send_side_effect(data: bytes) -> int: + assert data == tcp.create_information_request(serial_number) + asynctest.set_read_ready(socket_mock, self.loop) + return len(data) + + def recv_side_effect(_max_bytes: int) -> bytes: + return load_fixture_bytes("tcp_reply_offline.data") + + socket_mock.send.side_effect = send_side_effect + socket_mock.recv.side_effect = recv_side_effect + + client = OmnikInverter( + host="example.com", + source_type="tcp", + serial_number=serial_number, + _socket_mock=socket_mock, + ) + + inverter: Inverter = await client.inverter() + + assert inverter + assert inverter.solar_rated_power is None + assert inverter.solar_current_power == 0 + + assert inverter.model is None + assert inverter.serial_number == "NLBN4020157P9024" + assert inverter.temperature is None + assert inverter.dc_input_voltage == [0.0, 0.0, 0.0] + assert inverter.dc_input_current == [0.0, 0.0, 0.0] + assert inverter.ac_output_voltage == [0.0, 0.0, 0.0] + assert inverter.ac_output_current == [0.0, 0.0, 0.0] + assert inverter.ac_output_frequency == [0.0, 0.0, 0.0] + assert inverter.ac_output_power == [0.0, 0.0, 0.0] + assert inverter.solar_energy_today == 4.7 + assert inverter.solar_energy_total == 15818.0 + assert inverter.solar_hours_total == 0 + assert inverter.inverter_active is False + assert inverter.firmware == "" + assert inverter.firmware_slave == "" + async def test_connection_broken(self) -> None: """Test on connection broken after success - TCP source.""" serial_number = 1 From 789bb8335dfbcbd78961ed5df3b7405bf475757d Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Wed, 8 Feb 2023 10:11:01 +0100 Subject: [PATCH 2/3] Move to field extractor --- omnikinverter/tcp.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/omnikinverter/tcp.py b/omnikinverter/tcp.py index 7abb3bb..1054bca 100644 --- a/omnikinverter/tcp.py +++ b/omnikinverter/tcp.py @@ -255,10 +255,15 @@ def int_to_bool(num: int) -> bool: 1: True, }[num] + # Set temperature to None if it matches 65326, this is returned + # when the inverter is "offline". + def temperature_to_int(temp: int) -> Optional[float]: + return None if temp == 65326 else temp * 0.1 + # Only these fields will be extracted from the structure field_extractors = { "serial_number": None, - "temperature": 0.1, + "temperature": temperature_to_int, "dc_input_voltage": list_divide_10, "dc_input_current": list_divide_10, "ac_output_current": list_divide_10, @@ -277,12 +282,6 @@ def int_to_bool(num: int) -> bool: for (name, extractor) in field_extractors.items(): value = getattr(tcp_data, name) - # Set temperature to None if it matches 65326, this is returned - # when the inverter is "offline". - if name == "temperature" and value == 65326: - result["temperature"] = None - continue - if name == "ac_output": # Flatten the list of frequency+power AC objects From 5dfeb18628cd3a0134643f97d2c7bae4ce7a9906 Mon Sep 17 00:00:00 2001 From: Robbin Janssen Date: Wed, 8 Feb 2023 12:01:54 +0100 Subject: [PATCH 3/3] Fix method name --- omnikinverter/tcp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/omnikinverter/tcp.py b/omnikinverter/tcp.py index 1054bca..6908996 100644 --- a/omnikinverter/tcp.py +++ b/omnikinverter/tcp.py @@ -257,13 +257,13 @@ def int_to_bool(num: int) -> bool: # Set temperature to None if it matches 65326, this is returned # when the inverter is "offline". - def temperature_to_int(temp: int) -> Optional[float]: + def temperature_to_float(temp: int) -> Optional[float]: return None if temp == 65326 else temp * 0.1 # Only these fields will be extracted from the structure field_extractors = { "serial_number": None, - "temperature": temperature_to_int, + "temperature": temperature_to_float, "dc_input_voltage": list_divide_10, "dc_input_current": list_divide_10, "ac_output_current": list_divide_10,