Files
mc-webui/tests/test_repeater_cli_replies.py
T
MarekWoandClaude Opus 5 e03aaca489 feat(repeaters): capability-aware settings, v1.17 fields and power-off
Firmware v1.17 changed no protocol constants and left FIRMWARE_VER_CODE at
13, so the companion protocol cannot tell a v1.16 node from a v1.17 one.
Capability is instead read from the CLI reply text, which self-describes and
works for any version and board without an extra round-trip:

  `??: <field>` / `unknown config: <field>`  → firmware has no such setting
  `Error: unsupported`                       → hardware cannot do it
  `> <value>`                                → supported

Both of the first two now yield an 'unsupported' status rather than a
failure, rendered as a muted badge with the control locked and excluded from
Apply. Without this, every field added below would read as a red error on
each repeater that predates it.

Applying to an unsupported field deliberately asserts nothing about the
stored value: v1.17 persists radio.rxgain before it learns the radio cannot
apply it, so the old value may or may not still hold. The baseline is
dropped instead of being reasserted, and only Refresh reveals the truth.

Also guards a pre-existing trap: up to v1.16 the `radio.rxgain` getter was
compiled out on non-SX126x radios, and `get radio.rxgain` fell through to
the `radio` branch (its memcmp has no trailing space), answering with the
radio parameters. That parsed as a valid reply and silently rendered the
switch as off. An on/off field that answers neither on nor off is now
treated as absent.

New fields: cad (v1.17+), flood.max.unscoped and flood.max.advert (both
present since v1.16, previously just undocumented upstream).

New action: poweroff (v1.17+). Unlike reboot it cannot be undone over the
mesh, so it takes a typed repeater name rather than a confirm(), checked
server-side as well as in the UI.

Telemetry: the MCU temperature v1.17 appends to channel 1 is labelled as
silicon rather than as an environmental reading. Absent stays absent —
boards without a usable sensor emit nothing, and a zero would be a lie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 18:31:32 +02:00

48 lines
1.7 KiB
Python

"""
Unit tests for classifying repeater CLI replies in the settings batch.
The firmware's reply text is the only capability signal available for a remote
repeater: FIRMWARE_VER_CODE stayed at 13 across v1.16 → v1.17, so it cannot
tell us whether a node knows a setting added in v1.17 (`cad`).
Run: python -m pytest tests/test_repeater_cli_replies.py -v
"""
import pytest
from app.routes.api import _reply_is_unsupported
# ================================================================
# Replies that mean "this node does not have that setting"
# ================================================================
@pytest.mark.parametrize('reply', [
'??: cad', # unknown `get` field (any version)
'unknown config: cad on', # unknown `set` field
'Error: unsupported', # v1.17 hardware refusal
'Error: unsupported by this board', # v1.16 wording, still recognised
' ??: radio.fem.rxgain ', # surrounding whitespace
'ERROR: UNSUPPORTED', # case-insensitive
])
def test_unsupported_replies(reply):
assert _reply_is_unsupported(reply) is True
# ================================================================
# Everything else — a value, a real failure, or nothing at all
# ================================================================
@pytest.mark.parametrize('reply', [
'> on',
'> 869.525,250.00,11,5',
'OK',
'OK - repeat is now ON',
'Error, max 64', # a genuine rejection, not a capability gap
'Error: failed to apply LoRa FEM RX gain',
'',
None,
])
def test_supported_or_failed_replies(reply):
assert _reply_is_unsupported(reply) is False