From aad0df10132542f9107cf3529a0f3a48c8065e59 Mon Sep 17 00:00:00 2001 From: Steve Date: Wed, 9 Sep 2026 21:37:17 +0100 Subject: [PATCH] Add internal metrics sensor module reporting relay on time Exposes relay_history's cumulative active seconds as a sensor reading (relay_on_time) via a new InternalMetrics module, so it flows through the existing sensor polling/push pipeline for SMIB howfresh reporting. Co-Authored-By: Claude Sonnet 5 --- src/lib/sensors/__init__.py | 3 +++ src/lib/sensors/internal_metrics.py | 39 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/lib/sensors/internal_metrics.py diff --git a/src/lib/sensors/__init__.py b/src/lib/sensors/__init__.py index 633206e..70e6f64 100644 --- a/src/lib/sensors/__init__.py +++ b/src/lib/sensors/__init__.py @@ -9,6 +9,7 @@ from lib.sensors.PMSA003I import PMSA003I from lib.sensors.sensor_module import SensorModule from lib.sensors.file_logging import FileLogger +from lib.sensors.internal_metrics import InternalMetrics from lib.sensors.alarm import Alarm from lib.displays.display import Display from lib.networking import WirelessNetwork @@ -32,6 +33,8 @@ def __init__(self, i2c: I2C, display: Display, wifi: WirelessNetwork, space_stat self.file_logger = FileLogger(init_files=True) self.load_modules(self.SENSOR_MODULES) self._configure_modules() + self.internal_metrics = InternalMetrics(self.space_state) + self.configured_modules['InternalMetrics'] = self.internal_metrics self.alarm = Alarm(self.display, self.space_state) if CO2_ALARM_THRESHOLD_PPM > 0 and 'SCD30' in self.configured_modules: self.alarm.enable() diff --git a/src/lib/sensors/internal_metrics.py b/src/lib/sensors/internal_metrics.py new file mode 100644 index 0000000..a2a94cb --- /dev/null +++ b/src/lib/sensors/internal_metrics.py @@ -0,0 +1,39 @@ +from lib.relay_history import RTCUnreliableError +from lib.sensors.sensor_module import SensorModule +from lib.space_state import SpaceState +from lib.ulogging import uLogger + +class InternalMetrics(SensorModule): + """ + Reports internal SMIBHID metrics as sensor readings, rather than + readings from an external, physically attached, I2C sensor. + """ + + def __init__(self, space_state: SpaceState) -> None: + super().__init__([{"name": "relay_on_time", "unit": "s"}]) + self.log = uLogger("InternalMetrics") + self.space_state = space_state + + def _get_relay_on_time(self) -> float | None: + """ + Return cumulative relay active seconds from relay history, or None + if relay history is not available or not enabled. + """ + relay_history = getattr(self.space_state, "relay_history", None) + if relay_history is None: + self.log.info("Relay history not available - no relay on time to report") + return None + + try: + return relay_history.get_total_active_seconds(bool(self.space_state._last_relay_state)) + except RTCUnreliableError: + self.log.info("System clock not yet reliable - no relay on time to report") + return None + + def get_reading(self) -> dict[str, float | None]: + """ + Get internal metrics reading in SMIBHID format. + """ + return { + "relay_on_time": self._get_relay_on_time() + }