Add default precision

This commit is contained in:
Jack Kingsman
2026-04-12 18:59:44 -07:00
parent 2756b1ae8d
commit e1ee7fcd24
3 changed files with 70 additions and 20 deletions

View File

@@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
# ── Repeater telemetry sensor definitions ─────────────────────────────────
_REPEATER_SENSORS: list[dict[str, str | None]] = [
_REPEATER_SENSORS: list[dict[str, Any]] = [
{
"field": "battery_volts",
"name": "Battery Voltage",
@@ -34,6 +34,7 @@ _REPEATER_SENSORS: list[dict[str, str | None]] = [
"device_class": "voltage",
"state_class": "measurement",
"unit": "V",
"precision": 2,
},
{
"field": "noise_floor_dbm",
@@ -42,6 +43,7 @@ _REPEATER_SENSORS: list[dict[str, str | None]] = [
"device_class": "signal_strength",
"state_class": "measurement",
"unit": "dBm",
"precision": 0,
},
{
"field": "last_rssi_dbm",
@@ -50,6 +52,7 @@ _REPEATER_SENSORS: list[dict[str, str | None]] = [
"device_class": "signal_strength",
"state_class": "measurement",
"unit": "dBm",
"precision": 0,
},
{
"field": "last_snr_db",
@@ -58,6 +61,7 @@ _REPEATER_SENSORS: list[dict[str, str | None]] = [
"device_class": None,
"state_class": "measurement",
"unit": "dB",
"precision": 1,
},
{
"field": "packets_received",
@@ -66,6 +70,7 @@ _REPEATER_SENSORS: list[dict[str, str | None]] = [
"device_class": None,
"state_class": "total_increasing",
"unit": None,
"precision": 0,
},
{
"field": "packets_sent",
@@ -74,6 +79,7 @@ _REPEATER_SENSORS: list[dict[str, str | None]] = [
"device_class": None,
"state_class": "total_increasing",
"unit": None,
"precision": 0,
},
{
"field": "uptime_seconds",
@@ -82,24 +88,25 @@ _REPEATER_SENSORS: list[dict[str, str | None]] = [
"device_class": "duration",
"state_class": None,
"unit": "s",
"precision": 0,
},
]
# ── LPP sensor metadata ─────────────────────────────────────────────────
_LPP_HA_META: dict[str, dict[str, str | None]] = {
"temperature": {"device_class": "temperature", "unit": "°C"},
"humidity": {"device_class": "humidity", "unit": "%"},
"barometer": {"device_class": "atmospheric_pressure", "unit": "hPa"},
"voltage": {"device_class": "voltage", "unit": "V"},
"current": {"device_class": "current", "unit": "mA"},
"luminosity": {"device_class": "illuminance", "unit": "lux"},
"power": {"device_class": "power", "unit": "W"},
"energy": {"device_class": "energy", "unit": "kWh"},
"distance": {"device_class": "distance", "unit": "mm"},
"concentration": {"device_class": None, "unit": "ppm"},
"direction": {"device_class": None, "unit": "°"},
"altitude": {"device_class": None, "unit": "m"},
_LPP_HA_META: dict[str, dict[str, Any]] = {
"temperature": {"device_class": "temperature", "unit": "°C", "precision": 1},
"humidity": {"device_class": "humidity", "unit": "%", "precision": 1},
"barometer": {"device_class": "atmospheric_pressure", "unit": "hPa", "precision": 1},
"voltage": {"device_class": "voltage", "unit": "V", "precision": 2},
"current": {"device_class": "current", "unit": "mA", "precision": 1},
"luminosity": {"device_class": "illuminance", "unit": "lux", "precision": 0},
"power": {"device_class": "power", "unit": "W", "precision": 1},
"energy": {"device_class": "energy", "unit": "kWh", "precision": 2},
"distance": {"device_class": "distance", "unit": "mm", "precision": 0},
"concentration": {"device_class": None, "unit": "ppm", "precision": 0},
"direction": {"device_class": None, "unit": "°", "precision": 0},
"altitude": {"device_class": None, "unit": "m", "precision": 1},
}
@@ -141,6 +148,8 @@ def _lpp_discovery_configs(
cfg["device_class"] = meta["device_class"]
if meta.get("unit"):
cfg["unit_of_measurement"] = meta["unit"]
if meta.get("precision") is not None:
cfg["suggested_display_precision"] = meta["precision"]
topic = f"homeassistant/sensor/meshcore_{nid}/{object_id}/config"
configs.append((topic, cfg))
@@ -150,7 +159,7 @@ def _lpp_discovery_configs(
# ── Local radio sensor definitions ────────────────────────────────────────
_RADIO_SENSORS: list[dict[str, str | None]] = [
_RADIO_SENSORS: list[dict[str, Any]] = [
{
"field": "noise_floor_dbm",
"name": "Noise Floor",
@@ -158,14 +167,16 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": "signal_strength",
"state_class": "measurement",
"unit": "dBm",
"precision": 0,
},
{
"field": "battery_mv",
"field": "battery_volts",
"name": "Battery",
"object_id": "battery",
"device_class": "voltage",
"state_class": "measurement",
"unit": "mV",
"unit": "V",
"precision": 2,
},
{
"field": "uptime_secs",
@@ -174,6 +185,7 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": "duration",
"state_class": None,
"unit": "s",
"precision": 0,
},
{
"field": "last_rssi",
@@ -182,6 +194,7 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": "signal_strength",
"state_class": "measurement",
"unit": "dBm",
"precision": 0,
},
{
"field": "last_snr",
@@ -190,6 +203,7 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": None,
"state_class": "measurement",
"unit": "dB",
"precision": 1,
},
{
"field": "tx_air_secs",
@@ -198,6 +212,7 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": "duration",
"state_class": "total_increasing",
"unit": "s",
"precision": 0,
},
{
"field": "rx_air_secs",
@@ -206,6 +221,7 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": "duration",
"state_class": "total_increasing",
"unit": "s",
"precision": 0,
},
{
"field": "packets_recv",
@@ -214,6 +230,7 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": None,
"state_class": "total_increasing",
"unit": None,
"precision": 0,
},
{
"field": "packets_sent",
@@ -222,6 +239,7 @@ _RADIO_SENSORS: list[dict[str, str | None]] = [
"device_class": None,
"state_class": "total_increasing",
"unit": None,
"precision": 0,
},
]
@@ -343,6 +361,8 @@ def _radio_discovery_configs(
cfg["state_class"] = sensor["state_class"]
if sensor["unit"]:
cfg["unit_of_measurement"] = sensor["unit"]
if sensor.get("precision") is not None:
cfg["suggested_display_precision"] = sensor["precision"]
topic = f"homeassistant/sensor/meshcore_{nid}/{sensor['object_id']}/config"
configs.append((topic, cfg))
@@ -376,6 +396,8 @@ def _repeater_discovery_configs(
cfg["state_class"] = sensor["state_class"]
if sensor["unit"]:
cfg["unit_of_measurement"] = sensor["unit"]
if sensor.get("precision") is not None:
cfg["suggested_display_precision"] = sensor["precision"]
# 10 hours — margin over the 8-hour auto-collect cycle
cfg["expire_after"] = 36000
@@ -632,6 +654,13 @@ class MqttHaModule(FanoutModule):
field = sensor["field"]
if field is not None:
payload[field] = data.get(field)
# Normalize battery from millivolts to volts for consistency with
# repeater battery and the discovery config (unit: V, precision: 2).
battery_mv = data.get("battery_mv")
if battery_mv is not None:
payload["battery_volts"] = battery_mv / 1000.0
await self._publisher.publish(f"{self._prefix}/{nid}/health", payload)
async def on_contact(self, data: dict) -> None:

View File

@@ -1,12 +1,12 @@
{
"name": "remoteterm-meshcore-frontend",
"version": "3.8.0",
"version": "3.11.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "remoteterm-meshcore-frontend",
"version": "3.8.0",
"version": "3.11.0",
"dependencies": {
"@codemirror/lang-python": "^6.2.1",
"@codemirror/theme-one-dark": "^6.1.3",

View File

@@ -104,6 +104,21 @@ class TestRadioDiscovery:
for _, cfg in configs[1:]:
assert cfg["expire_after"] == 120
def test_sensor_configs_have_display_precision(self):
configs = _radio_discovery_configs("mc", "aabbccddeeff", "R")
# All sensor configs (skip the binary_sensor at index 0)
for _, cfg in configs[1:]:
assert "suggested_display_precision" in cfg
assert isinstance(cfg["suggested_display_precision"], int)
def test_battery_sensor_uses_volts(self):
configs = _radio_discovery_configs("mc", "aabbccddeeff", "R")
battery_cfgs = [(t, c) for t, c in configs if "battery" in t]
assert len(battery_cfgs) == 1
_, cfg = battery_cfgs[0]
assert cfg["unit_of_measurement"] == "V"
assert cfg["suggested_display_precision"] == 2
class TestRepeaterDiscovery:
def test_produces_sensor_per_field(self):
@@ -124,6 +139,11 @@ class TestRepeaterDiscovery:
for _, cfg in configs:
assert cfg["expire_after"] == 36000
def test_sensors_have_display_precision(self):
configs = _repeater_discovery_configs("mc", "ccdd", "Rep1", None)
for _, cfg in configs:
assert "suggested_display_precision" in cfg
class TestContactTrackerDiscovery:
def test_config_shape(self):
@@ -263,7 +283,7 @@ class TestMqttHaHealth:
payload = health_calls[-1][0][1]
assert payload["connected"] is True
assert payload["noise_floor_dbm"] == -110
assert payload["battery_mv"] == 4150
assert payload["battery_volts"] == 4.15
assert payload["uptime_secs"] == 3600
assert payload["last_rssi"] == -85
assert payload["packets_recv"] == 500
@@ -524,6 +544,7 @@ class TestLppDiscoveryConfigs:
assert cfg["unit_of_measurement"] == "°C"
assert cfg["state_class"] == "measurement"
assert cfg["expire_after"] == 36000
assert cfg["suggested_display_precision"] == 1
assert "lpp_temperature_ch1" in cfg["value_template"]
def test_unknown_sensor_type_no_device_class(self):