From 22f0e9cd9070b5bbf9bb32a7f99a20fe03f5df5e Mon Sep 17 00:00:00 2001 From: DevOldSchool Date: Thu, 23 Jul 2026 21:53:04 +1000 Subject: [PATCH] Reject invalid power readings. --- hub-server/hub_server.py | 15 ++++++++++++++- hub-server/tests/test_hub_server.py | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/hub-server/hub_server.py b/hub-server/hub_server.py index 64695c1..c2b1513 100755 --- a/hub-server/hub_server.py +++ b/hub-server/hub_server.py @@ -5,6 +5,7 @@ Efergy hub, logging incoming sensor data to a sqlite database. """ import logging +import math import socket import sys from http.server import HTTPServer, SimpleHTTPRequestHandler @@ -24,6 +25,14 @@ ENERGY_UNIT_OF_MEASUREMENT ) +INT32_MAX = 2_147_483_647 + + +def is_valid_power_reading(value: float) -> bool: + """Reject non-finite readings and the INT32_MAX emitted by faulty hubs.""" + return math.isfinite(value) and value < INT32_MAX + + class EfergyHTTPServer(HTTPServer): """ A custom HTTPServer subclass that holds the database instance. @@ -287,7 +296,11 @@ def process_parsed_results(self, parsed_results: list[dict], hub_version: str, f value = data["value"] firmware_version = data.get("firmware_version", firmware_version) timestamp = data.get("timestamp") - + + if not is_valid_power_reading(value): + logging.warning(f"Skipping invalid power reading for {label}: {value}") + continue + logging.debug(f"Logging sensor: {label}, raw: {value}") database.log_data(label, value, firmware_version, timestamp=timestamp) diff --git a/hub-server/tests/test_hub_server.py b/hub-server/tests/test_hub_server.py index b5381f4..0bbef6c 100644 --- a/hub-server/tests/test_hub_server.py +++ b/hub-server/tests/test_hub_server.py @@ -89,6 +89,32 @@ def test_post_h2(test_server, mock_db, mock_mqtt): assert mock_mqtt.publish_power.called +def test_post_h3_rejects_invalid_power_readings(test_server, mock_db, mock_mqtt): + host, port = test_server + payload = b"\r\n".join([ + b"782792|1|EFCT|P1,12761.72|-61", + b"782792|1|EFCT|P1,2147483647.2147483647|-61", + b"782792|1|EFCT|P1,2147483647|-61", + b"782792|1|EFCT|P1,nan|-61", + b"782792|1|EFCT|P1,inf|-61", + b"782792|1|EFCT|P1,12285.65|-61", + ]) + headers = { + "Content-Type": "application/eh-data", + "Content-Length": str(len(payload)), + "X-Version": "3.7.1", + } + + status, data = http_request(host, port, "POST", "/h3", body=payload, headers=headers) + + assert status == 200 + assert data == b"success" + assert mock_db.log_data.call_count == 2 + assert [call.args[1] for call in mock_db.log_data.call_args_list] == [12761.72, 12285.65] + assert mock_mqtt.publish_power.call_count == 2 + assert [call.args[4] for call in mock_mqtt.publish_power.call_args_list] == [12761.72, 12285.65] + + def test_post_h3bulk(test_server, mock_db, mock_mqtt): host, port = test_server payload = bytes.fromhex(