added current and power to telemetry page

This commit is contained in:
lbibass
2026-07-25 22:41:01 -04:00
parent 30fe28fb6a
commit b955f1d0a3
2 changed files with 70 additions and 12 deletions
+31 -7
View File
@@ -287,10 +287,19 @@ class ProtocolRequestHelper:
# else 0.0 V (voltage-only floor, mirroring the companion self-telemetry).
lpp = bytearray(encode_voltage(TELEM_CHANNEL_SELF, self._battery_voltage(readings)))
# Environment sensors: each gets the next channel starting at 2, in the
# order they are configured (firmware querySensors channel assignment).
# INA219 power telemetry and environment sensors: the power telemetry
# channels are emitted first, then environment sensors continue on the
# next available channel.
if perm_mask & TELEM_PERM_ENVIRONMENT:
channel = TELEM_CHANNEL_SELF + 1
if self._has_sensor_data(readings, "bus_voltage_v"):
lpp.extend(encode_voltage(channel, self._battery_voltage(readings)))
if self._has_sensor_data(readings, "current_ma"):
lpp.extend(encode_current(channel, self._battery_current(readings)))
if self._has_sensor_data(readings, "power_mw"):
lpp.extend(encode_power(channel, self._battery_power(readings)))
channel += 1
for reading in readings:
entry = self._encode_environment_reading(channel, reading)
if entry:
@@ -329,8 +338,21 @@ class ProtocolRequestHelper:
except (TypeError, ValueError):
continue
return 0.0
@staticmethod
def _has_sensor_data(readings, key: str) -> bool:
"""Return True when any reading exposes the requested sensor field."""
for reading in readings:
if not reading.get("ok"):
continue
data = reading.get("data") or {}
if data.get(key) is not None:
return True
return False
@staticmethod
def _battery_current(readings) -> float:
"""First configured sensor's current, else 0.000A."""
"""First configured sensor's current in amps, else 0.000A."""
for reading in readings:
if not reading.get("ok"):
continue
@@ -338,20 +360,22 @@ class ProtocolRequestHelper:
current = data.get("current_ma")
if current is not None:
try:
return float(current)
return float(current) / 1000.0
except (TypeError, ValueError):
continue
return 0.000
@staticmethod
def _battery_power(readings) -> int:
"""First configured sensor's power, else 0W."""
"""First configured sensor's power in watts, else 0W."""
for reading in readings:
if not reading.get("ok"):
continue
data = reading.get("data") or {}
power = data.get("power_mw")/1000
power = data.get("power_mw")
if power is not None:
try:
return int(power)
return int(float(power) / 1000.0)
except (TypeError, ValueError):
continue
return 0
@@ -682,6 +682,8 @@ from openhop_core.node.handlers.protocol_request import ( # noqa: E402
)
from openhop_core.protocol.cayenne_lpp import ( # noqa: E402
TELEM_CHANNEL_SELF,
encode_current,
encode_power,
encode_relative_humidity,
encode_temperature,
encode_voltage,
@@ -722,8 +724,11 @@ def test_telemetry_base_voltage_from_ups_sensor():
lpp = helper._handle_get_telemetry(admin, 0, b"\x00")
# No environment values on the UPS reading -> voltage entry only.
assert lpp == encode_voltage(TELEM_CHANNEL_SELF, 12.6)
# Base voltage stays on channel 1, and the additional INA219 voltage view
# is emitted on the next channel when environment telemetry is allowed.
assert lpp == encode_voltage(TELEM_CHANNEL_SELF, 12.6) + encode_voltage(
TELEM_CHANNEL_SELF + 1, 12.6
)
def test_telemetry_admin_full_mask_includes_environment_sensors():
@@ -734,7 +739,7 @@ def test_telemetry_admin_full_mask_includes_environment_sensors():
"""
sm = _FakeSensorManager(
[
_reading(bus_voltage_v=12.6),
_reading(bus_voltage_v=12.6, current_ma=500.0, power_mw=6000.0),
_reading(temperature_c=21.5, humidity_pct=55.0),
]
)
@@ -748,8 +753,11 @@ def test_telemetry_admin_full_mask_includes_environment_sensors():
expected = (
encode_voltage(TELEM_CHANNEL_SELF, 12.6)
+ encode_temperature(TELEM_CHANNEL_SELF + 1, 21.5)
+ encode_relative_humidity(TELEM_CHANNEL_SELF + 1, 55.0)
+ encode_voltage(TELEM_CHANNEL_SELF + 1, 12.6)
+ encode_current(TELEM_CHANNEL_SELF + 2, 0.5)
+ encode_power(TELEM_CHANNEL_SELF + 3, 6)
+ encode_temperature(TELEM_CHANNEL_SELF + 4, 21.5)
+ encode_relative_humidity(TELEM_CHANNEL_SELF + 4, 55.0)
)
assert lpp == expected
@@ -767,6 +775,32 @@ def test_telemetry_guest_forced_to_base_only():
assert lpp == encode_voltage(TELEM_CHANNEL_SELF, 0.0)
def test_telemetry_includes_ina219_power_channels_before_environment_sensors():
"""INA219 voltage/current/power values are emitted before env sensors."""
sm = _FakeSensorManager(
[
_reading(bus_voltage_v=12.6, current_ma=500.0, power_mw=6000.0),
_reading(temperature_c=21.5, humidity_pct=55.0),
]
)
helper = ProtocolRequestHelper(
identity_manager=MagicMock(), packet_injector=AsyncMock(), sensor_manager=sm
)
admin = SimpleNamespace(is_guest=lambda: False)
lpp = helper._handle_get_telemetry(admin, 0, b"\x00")
expected = (
encode_voltage(TELEM_CHANNEL_SELF, 12.6)
+ encode_voltage(TELEM_CHANNEL_SELF + 1, 12.6)
+ encode_current(TELEM_CHANNEL_SELF + 2, 0.5)
+ encode_power(TELEM_CHANNEL_SELF + 3, 6)
+ encode_temperature(TELEM_CHANNEL_SELF + 4, 21.5)
+ encode_relative_humidity(TELEM_CHANNEL_SELF + 4, 55.0)
)
assert lpp == expected
def test_telemetry_inverse_mask_gates_environment():
"""An inverse mask that clears the environment bit strips env sensors.