Every row says what it does, not what it is
The descriptions were written for someone who already knew the answer. A user reading "Airtime budget: duty cycle = 100 / (factor + 1) %" or "Per-hop path-hash size; larger resists collisions, adds overhead" has to ask, which is the opposite of what a description is for. So both registries now answer the reader's actual question -- what does this do to me -- in as few words as it takes. "Wider is faster, narrower reaches further" instead of naming the unit twice. "Caps how much of the air we use; 0 = no cap" instead of the formula. "How loud this radio transmits." Labels went with them where the name was ours to choose: Sweep floor and Sweep ceiling are Lowest power and Highest power, Opening frame is Map fit, Silence alarm is Alert after, Basemap source is Map tiles. The radio's own names stay -- someone matching Spreading factor against their phone app should find the words they came for. History kept is just History. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
fe4780e070
commit
03f8e1496f
3 files changed
+56
-54
No files matched your search
@@ -331,7 +331,7 @@ _PATH_HASH_CHOICES = {
|
||||
def _telemetry_spec(key: str, label: str) -> SettingSpec:
|
||||
"""Build a telemetry-mode setting spec (shared shape for base/loc/env)."""
|
||||
return SettingSpec(
|
||||
key=key, label=label, help="Telemetry reporting mode", category="Behavior",
|
||||
key=key, label=label, help="Who may read this node's sensor readings", category="Behavior",
|
||||
value_type="enum", choices=_TELEMETRY_CHOICES, minimum=0, maximum=3,
|
||||
getter=_get(key), apply=_telemetry_apply(key),
|
||||
)
|
||||
@@ -452,16 +452,16 @@ def current_preset(snapshot: dict[str, Any]) -> Optional[RadioPreset]:
|
||||
DEVICE_SETTINGS: list[SettingSpec] = [
|
||||
# Identity
|
||||
SettingSpec(
|
||||
"name", "Node name", "Advertised node name", "Identity", "str",
|
||||
"name", "Node name", "The name other nodes see", "Identity", "str",
|
||||
getter=_get("name"), apply=lambda d, v, s: d.set_name(v),
|
||||
),
|
||||
SettingSpec(
|
||||
"adv_lat", "Latitude", "Advertised latitude, degrees", "Identity",
|
||||
"adv_lat", "Latitude", "How far north or south this node says it is", "Identity",
|
||||
"float", minimum=-90.0, maximum=90.0,
|
||||
getter=_get("adv_lat"), apply=_coords_apply("adv_lat"),
|
||||
),
|
||||
SettingSpec(
|
||||
"adv_lon", "Longitude", "Advertised longitude, degrees", "Identity",
|
||||
"adv_lon", "Longitude", "How far east or west this node says it is", "Identity",
|
||||
"float", minimum=-180.0, maximum=180.0,
|
||||
getter=_get("adv_lon"), apply=_coords_apply("adv_lon"),
|
||||
),
|
||||
@@ -472,34 +472,36 @@ DEVICE_SETTINGS: list[SettingSpec] = [
|
||||
# backups still restore; reading the firmware's own name means the row shows a
|
||||
# value instead of "?" on real hardware. The fallback keeps the canonical key
|
||||
# working for anything that reports it directly.
|
||||
"device_pin", "Device PIN", "BLE pairing PIN", "Identity", "int",
|
||||
"device_pin", "Device PIN", "Code a phone needs to pair over Bluetooth", "Identity", "int",
|
||||
minimum=0, maximum=999999,
|
||||
getter=lambda snapshot: snapshot.get("ble_pin", snapshot.get("device_pin")),
|
||||
apply=lambda d, v, s: d.set_device_pin(v),
|
||||
),
|
||||
# Radio
|
||||
SettingSpec(
|
||||
"radio_freq", "Frequency (MHz)", "Carrier frequency in MHz", "Radio", "float",
|
||||
"radio_freq", "Frequency (MHz)", "Must match every other node on your mesh",
|
||||
"Radio", "float",
|
||||
minimum=100.0, maximum=1000.0,
|
||||
getter=_get("radio_freq"), apply=_radio_apply("freq"),
|
||||
),
|
||||
SettingSpec(
|
||||
"radio_bw", "Bandwidth (kHz)", "Channel bandwidth in kHz", "Radio", "float",
|
||||
"radio_bw", "Bandwidth (kHz)", "Wider is faster, narrower reaches further",
|
||||
"Radio", "float",
|
||||
minimum=1.0, maximum=1000.0,
|
||||
getter=_get("radio_bw"), apply=_radio_apply("bw"),
|
||||
),
|
||||
SettingSpec(
|
||||
"radio_sf", "Spreading factor", "LoRa spreading factor", "Radio", "int",
|
||||
"radio_sf", "Spreading factor", "Higher reaches further but sends slower", "Radio", "int",
|
||||
minimum=5, maximum=12,
|
||||
getter=_get("radio_sf"), apply=_radio_apply("sf"),
|
||||
),
|
||||
SettingSpec(
|
||||
"radio_cr", "Coding rate", "LoRa coding-rate denominator (4/5–4/8)",
|
||||
"radio_cr", "Coding rate", "Higher survives noise better, sends slower",
|
||||
"Radio", "int", minimum=5, maximum=8,
|
||||
getter=_get("radio_cr"), apply=_radio_apply("cr"),
|
||||
),
|
||||
SettingSpec(
|
||||
"tx_power", "TX power (dBm)", "Transmit power, up to this board's reported max",
|
||||
"tx_power", "TX power (dBm)", "How loud this radio transmits",
|
||||
"Radio", "int", minimum=1, maximum=30, max_key="max_tx_power",
|
||||
getter=_get("tx_power"), apply=lambda d, v, s: d.set_tx_power(v),
|
||||
),
|
||||
@@ -508,44 +510,44 @@ DEVICE_SETTINGS: list[SettingSpec] = [
|
||||
# here: companion firmware ignores them — they are remote-CLI settings on repeaters.)
|
||||
SettingSpec(
|
||||
"airtime_factor", "Airtime factor",
|
||||
"Airtime budget: duty cycle = 100 / (factor + 1) %, so 0 = unlimited", "Tuning",
|
||||
"Caps how much of the air we use; 0 = no cap", "Tuning",
|
||||
"float", minimum=0.0, maximum=9.0,
|
||||
getter=_get("airtime_factor"), apply=_tuning_apply("airtime_factor"),
|
||||
),
|
||||
SettingSpec(
|
||||
"rx_delay", "RX delay",
|
||||
"Base delay before handling received packets, seconds", "Tuning",
|
||||
"Wait this long before acting on what we hear", "Tuning",
|
||||
"float", minimum=0.0, maximum=20.0,
|
||||
getter=_get("rx_delay"), apply=_tuning_apply("rx_delay"),
|
||||
),
|
||||
# Behavior
|
||||
SettingSpec(
|
||||
"manual_add_contacts", "Manual add contacts",
|
||||
"Require adding contacts manually", "Behavior", "bool",
|
||||
"manual_add_contacts", "Add contacts by hand",
|
||||
"Only save a node when you say so", "Behavior", "bool",
|
||||
getter=_get("manual_add_contacts"),
|
||||
apply=lambda d, v, s: d.set_manual_add_contacts(v),
|
||||
),
|
||||
SettingSpec(
|
||||
"autoadd_config", "Contact auto-add",
|
||||
"Bitmask of advert types auto-added as contacts (0 = none)", "Behavior", "int",
|
||||
"autoadd_config", "Auto-add contacts",
|
||||
"Which kinds of node get saved on their own; 0 = none", "Behavior", "int",
|
||||
minimum=0, maximum=255,
|
||||
getter=_get("autoadd_config"), apply=lambda d, v, s: d.set_autoadd_config(v),
|
||||
),
|
||||
SettingSpec(
|
||||
"flood_scope", "Flood scope",
|
||||
"Limit flood routing to one #scope; empty reaches the whole mesh", "Behavior",
|
||||
"Keep traffic to one group; empty reaches everyone", "Behavior",
|
||||
"str", max_length=30,
|
||||
getter=_get("flood_scope"), apply=lambda d, v, s: d.set_default_flood_scope(v),
|
||||
),
|
||||
SettingSpec(
|
||||
"adv_loc_policy", "Advert location policy",
|
||||
"Share location in adverts", "Behavior", "enum",
|
||||
"adv_loc_policy", "Share location",
|
||||
"Tell other nodes where this one is", "Behavior", "enum",
|
||||
choices=_ADV_LOC_CHOICES, minimum=0, strict_choices=False,
|
||||
getter=_get("adv_loc_policy"), apply=lambda d, v, s: d.set_adv_loc_policy(v),
|
||||
),
|
||||
SettingSpec(
|
||||
"multi_acks", "Multi-acks",
|
||||
"Extra acknowledgements for reliability", "Behavior",
|
||||
"Send extra receipts so replies get through", "Behavior",
|
||||
"enum", choices=_MULTI_ACKS_CHOICES, minimum=0, strict_choices=False,
|
||||
getter=_get("multi_acks"), apply=lambda d, v, s: d.set_multi_acks(v),
|
||||
),
|
||||
@@ -555,7 +557,7 @@ DEVICE_SETTINGS: list[SettingSpec] = [
|
||||
# Experimental
|
||||
SettingSpec(
|
||||
"path_hash_mode", "Path-hash mode",
|
||||
"Per-hop path-hash size; larger resists collisions, adds overhead",
|
||||
"Longer hop ids mix up fewer nodes, but cost space",
|
||||
"Experimental", "enum", choices=_PATH_HASH_CHOICES, minimum=0, maximum=3,
|
||||
getter=_get("path_hash_mode"), apply=lambda d, v, s: d.set_path_hash_mode(v),
|
||||
),
|
||||
|
||||
@@ -54,7 +54,7 @@ GROUPS: tuple[str, ...] = (
|
||||
"Transmit power",
|
||||
"Watchtower",
|
||||
"Map",
|
||||
"History kept",
|
||||
"History",
|
||||
"Display",
|
||||
)
|
||||
|
||||
@@ -127,8 +127,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
# --- Sending -----------------------------------------------------------------
|
||||
PrefSpec(
|
||||
key="trace_cooldown_s",
|
||||
label="Transmit cooldown",
|
||||
help="Pause between our own transmissions, for duty-cycle safety",
|
||||
label="Pause between sends",
|
||||
help="Wait this long before transmitting again",
|
||||
group="Sending",
|
||||
value_type="float",
|
||||
default=5.0,
|
||||
@@ -139,7 +139,7 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
PrefSpec(
|
||||
key="direct_message_soft_retries",
|
||||
label="Message retries",
|
||||
help="Automatic re-sends of an unacknowledged direct message",
|
||||
help="Times to resend a message that gets no reply",
|
||||
group="Sending",
|
||||
value_type="int",
|
||||
default=0, # one shot: a re-send is a second transmission on a shared mesh
|
||||
@@ -149,8 +149,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
# --- Transmit power ----------------------------------------------------------
|
||||
PrefSpec(
|
||||
key="tx_opt_min",
|
||||
label="Sweep floor",
|
||||
help="Lowest power the transmit-power optimizer explores",
|
||||
label="Lowest power",
|
||||
help="Weakest setting the power search tries",
|
||||
group="Transmit power",
|
||||
value_type="int",
|
||||
default=18,
|
||||
@@ -160,8 +160,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
),
|
||||
PrefSpec(
|
||||
key="tx_opt_max",
|
||||
label="Sweep ceiling",
|
||||
help="Highest power the transmit-power optimizer explores",
|
||||
label="Highest power",
|
||||
help="Strongest setting the power search tries",
|
||||
group="Transmit power",
|
||||
value_type="int",
|
||||
default=28,
|
||||
@@ -171,8 +171,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
),
|
||||
PrefSpec(
|
||||
key="tx_snr_tolerance_db",
|
||||
label="Tie tolerance",
|
||||
help="Within this much SNR of the best, the lower power wins",
|
||||
label="Tie margin",
|
||||
help="Signals this close are a tie, so less power wins",
|
||||
group="Transmit power",
|
||||
value_type="float",
|
||||
default=1.0,
|
||||
@@ -183,8 +183,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
# --- Watchtower --------------------------------------------------------------
|
||||
PrefSpec(
|
||||
key="watch_silence_hours",
|
||||
label="Silence alarm",
|
||||
help="Silence a newly watched node is allowed before the alarm",
|
||||
label="Alert after",
|
||||
help="Warn when a watched node goes quiet this long",
|
||||
group="Watchtower",
|
||||
value_type="enum",
|
||||
default=DEFAULT_SILENCE_HOURS,
|
||||
@@ -193,7 +193,7 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
PrefSpec(
|
||||
key="watch_alerts_kept",
|
||||
label="Alerts kept",
|
||||
help="Alerts the Watchtower log retains, oldest dropped first",
|
||||
help="How many past alerts to keep",
|
||||
group="Watchtower",
|
||||
value_type="int",
|
||||
default=200,
|
||||
@@ -203,8 +203,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
# --- Map ---------------------------------------------------------------------
|
||||
PrefSpec(
|
||||
key="map_view_fraction",
|
||||
label="Opening frame",
|
||||
help="Fraction of nodes a map frames, so outliers don't zoom it out",
|
||||
label="Map fit",
|
||||
help="Share of nodes to fit on screen; 1 shows them all",
|
||||
group="Map",
|
||||
value_type="float",
|
||||
default=0.5,
|
||||
@@ -213,8 +213,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
),
|
||||
PrefSpec(
|
||||
key="basemap_tilejson_url",
|
||||
label="Basemap source",
|
||||
help="TileJSON the map's vector tiles are fetched from",
|
||||
label="Map tiles",
|
||||
help="Where map images come from",
|
||||
group="Map",
|
||||
value_type="str",
|
||||
default="https://tiles.openfreemap.org/planet",
|
||||
@@ -223,9 +223,9 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
# --- History kept ------------------------------------------------------------
|
||||
PrefSpec(
|
||||
key="history_days",
|
||||
label="Packet history",
|
||||
help="Days of overheard history kept; 0 keeps everything forever",
|
||||
group="History kept",
|
||||
label="Keep packets for",
|
||||
help="Older ones are deleted; 0 keeps everything",
|
||||
group="History",
|
||||
value_type="int",
|
||||
default=365,
|
||||
minimum=0,
|
||||
@@ -234,9 +234,9 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
),
|
||||
PrefSpec(
|
||||
key="chat_history_limit",
|
||||
label="Transcript depth",
|
||||
help="Past messages loaded when a conversation opens",
|
||||
group="History kept",
|
||||
label="Chat history",
|
||||
help="Old messages to load when you open a chat",
|
||||
group="History",
|
||||
value_type="int",
|
||||
default=200,
|
||||
minimum=20,
|
||||
@@ -245,8 +245,8 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
PrefSpec(
|
||||
key="courier_history_kept",
|
||||
label="Courier history",
|
||||
help="Delivered and given-up outbox entries kept for the screen",
|
||||
group="History kept",
|
||||
help="Finished outbox messages to keep",
|
||||
group="History",
|
||||
value_type="int",
|
||||
default=100,
|
||||
minimum=10,
|
||||
@@ -256,7 +256,7 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
PrefSpec(
|
||||
key="fast_render",
|
||||
label="Fast redraw",
|
||||
help="Write rows straight to the terminal instead of through the stock renderer",
|
||||
help="Faster screen updates; turn off if it looks wrong",
|
||||
group="Display",
|
||||
value_type="bool",
|
||||
default=True,
|
||||
@@ -265,7 +265,7 @@ PREFERENCES: tuple[PrefSpec, ...] = (
|
||||
PrefSpec(
|
||||
key="full_width",
|
||||
label="Last column",
|
||||
help="Use the column a terminal that under-reports its width hides",
|
||||
help="Use the extra column some terminals hide",
|
||||
group="Display",
|
||||
value_type="enum",
|
||||
default="auto",
|
||||
|
||||
@@ -158,8 +158,8 @@ def test_the_file_reads_the_way_the_page_does(tmp_path: Path) -> None:
|
||||
prefs = Preferences(tmp_path / "preferences.yaml")
|
||||
prefs.set("history_days", 30)
|
||||
text = prefs.as_yaml()
|
||||
assert "# --- History kept ---" in text
|
||||
assert "# Days of overheard history kept" in text
|
||||
assert "# --- History ---" in text
|
||||
assert "# Older ones are deleted; 0 keeps everything" in text
|
||||
assert "# default: 365 days" in text
|
||||
# A group with nothing changed in it earns no heading.
|
||||
assert "# --- Display ---" not in text
|
||||
@@ -298,9 +298,9 @@ def test_a_staged_change_shows_its_arrow_and_the_save_action() -> None:
|
||||
def test_a_long_value_is_capped_so_the_descriptions_keep_their_lane() -> None:
|
||||
"""The basemap URL is shortened in the lane rather than eating the prose column."""
|
||||
_, rows = _rows(Preferences())
|
||||
basemap = next(row for row in rows if "Basemap source" in row)
|
||||
basemap = next(row for row in rows if "Map tiles" in row)
|
||||
assert "https://tiles.openf…" in basemap
|
||||
assert "TileJSON" in basemap # its description still made it onto the row
|
||||
assert "Where map images" in basemap # its description still made it onto the row
|
||||
|
||||
|
||||
def _table_lines(prefs: Preferences, width: int) -> list[str]:
|
||||
@@ -345,8 +345,8 @@ def test_the_printed_table_names_every_value_and_its_default() -> None:
|
||||
body = "\n".join(_table_lines(prefs, 160))
|
||||
assert "https://tiles.openfreemap.org/planet" in body # never capped here
|
||||
assert "30 days" in body and "365 days" in body # value beside its default
|
||||
assert "── History kept ──" in body
|
||||
assert "Days of overheard history kept" in body # the description lane, at this width
|
||||
assert "── History ──" in body
|
||||
assert "Older ones are deleted" in body # the description lane, at this width
|
||||
|
||||
|
||||
def test_the_printed_table_never_elides_a_key() -> None:
|
||||
|
||||
Reference in new issue
Block a user