mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-01 11:02:56 +02:00
Add default precision
This commit is contained in:
@@ -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:
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user