Add frequency slot lookup to Radio Config and channel delete to Channels#38
Conversation
Radio Config card (Tuning pane): - New Slot input to the left of Frequency. Entering a slot number fills the corresponding center frequency; entering a frequency fills the slot number (or shows "--" if the frequency is not on a slot boundary). - Bidirectional sync updates on onchange for both fields; preset and region changes also re-evaluate the slot. - Covers BW 125/250/500 across all six regions (US, EU_868, ANZ, IN, KR, SG_923) using the Meshtastic firmware formula: freq = freqStart + BW/2000 + (slot-1) * BW/1000. - Region band limits embedded as a small lookup table; toFixed(4) used so BW 125 offsets (0.0625 MHz) round correctly. Channels card: - "Delete Channel" button appears in the actions bar (left of Add Channel) when any input in a non-primary row has focus; hidden otherwise. mousedown preventDefault keeps row focus alive so the button does not disappear before the click lands. - Asks for browser confirm() before removing the row. - Channel 0 (primary) is never deletable. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The update_channels handler was only calling add_channel_key() for channels in the new list, leaving deleted channels' keys alive in _crypto._keys for the lifetime of the process. Clear _keys before the re-add loop so a Save immediately reflects deletions without requiring a service restart. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RadioConfig gains a slot field (Optional[int]). At startup, load_config()
calls _resolve_radio_frequency() which applies this priority:
1. frequency_mhz set in YAML -> use as-is, slot is ignored
2. slot set in YAML -> compute via Meshtastic firmware formula
freq = freqStart + BW/2000 + (slot-1)*BW/1000
3. neither set -> regional default frequency
frequency_mhz is now Optional[float] = None in RadioConfig so the
presence/absence of the field in YAML is detectable after merging.
_REGION_FREQ_START and _REGION_DEFAULT_FREQ lookup tables are inlined
in config.py (stable Meshtastic spec values, no new imports needed).
default.yaml drops the explicit frequency_mhz line so that a local.yaml
with only slot: N resolves correctly without the default layer clobbering
the None sentinel. A comment block documents both usage options.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks Kendel: this is exactly the kind of PR I want to see. Slot lookup has been overdue (operators coordinate by slot number, not MHz), and the crypto-keys cleanup is a real bug I'd missed. You also already wrote the whole thing against the v0.7.1 file split ( Slot formula crosses out clean: US LongFast slot 20 = Two small asks before merge: 1. The crypto fix reaches into a private attribute.
def clear_channel_keys(self) -> None:
"""Drop all non-default channel keys."""
self._keys.clear()Then 2. Regression test on the cleanup. Without one, the bug grows back the next time someone refactors Optional but appreciated: if you have a node on a non-US region around (or a friend with EU_868 / ANZ hardware), one cross-region slot sanity check would lock the formula in across all six bands. US is solid; EU and ANZ are where the band-start values diverge. Thanks again: this is the good version of an AI-assisted PR. |
Replace direct _crypto._keys.clear() in the route handler with a public method on CryptoService. Eliminates the encapsulation break flagged in PR review. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Yes, I ran into the key cleanup issue about five minutes after submitting the PR. I had two identical channels, with the resulting behavior that Ch 0 could TX and Ch 1 grabbed all the RX. When I deleted the duplicate channel, the second channel's key remained and it was swallowing received messages. That fix has been pushed back to my fork. I also added the slot number to config.yaml, with logic to resolve any conflict between slot number and frequency in the favor of the frequency. This keeps the change from breaking existing installations. Using the formula to calculate frequency should eliminate the need to set the default in each region, so that change was made as well. Although I can't see a reason it wouldn't work correctly, the change is untested through a new install. |
|
Thanks for the fast turnaround Kendel, and for going above and beyond on the schema work too. The The slot-in-config schema work in One small note for future cleanup (no need to block this PR): I'll do a quick fresh-SD validation on EU_868 on my end before merging, since you flagged the multi-region piece as untested and I have the hardware for it. That way you don't have to chase region-by-region. Really appreciate the work on this one. Both the slot lookup and the channel deletion (and the bug you caught while building it) are genuinely useful. P.S. Apologies for the earlier version of this comment. AI is dumb sometimes. The above is what I should have led with. |
Covers _resolve_radio_frequency() across all three priority paths (explicit MHz, slot-based formula, regional fallback) and verifies both region tables include every entry in SUPPORTED_REGIONS. 15 tests, all passing.
|
CI green and hardware-validated end-to-end. Merging this shortly. Tests run:
The schema change you bundled in Plan: merging to Thanks for the work and the patience through review. |
Bundles three improvements to the Radio configuration surface plus a
small schema cleanup that simplifies the radio config for fresh
installs.
Frontend (Radio tab):
* Bidirectional slot ↔ frequency lookup on the Radio Configuration
card. Typing a slot number updates the frequency input via the
Meshtastic firmware formula
(freq = freqStart + BW/2000 + (slot-1) * BW/1000); typing a
frequency clears the slot when it does not map to a channel
center. Covers BW 125/250/500 across all six supported regions.
* Per-channel delete button on the Channels card with mousedown
preventDefault to keep row focus alive while the click fires.
Channel 0 (primary) is never deletable.
Backend:
* New CryptoService.clear_channel_keys() public method replaces a
direct ._keys.clear() call from config_routes.update_channels,
keeping the internal dict private. Save now reflects channel
deletions immediately without requiring a service restart.
* RadioConfig.frequency_mhz is now Optional[float] (default None).
New radio.slot field for slot-based config. New
_resolve_radio_frequency() helper applies priority:
1. frequency_mhz set in YAML → use as-is
2. slot set in YAML → compute via the firmware formula
3. neither set → regional default
Backward-compatible: existing configs with explicit
frequency_mhz are preserved unchanged.
config/default.yaml: removed hardcoded frequency_mhz: 906.875,
documented the explicit-MHz vs slot vs regional-default options
inline. Fresh installs now get correct regional defaults
automatically (previously EU/IN/etc. installs silently inherited
US 906.875 unless the user explicitly overrode).
Tests:
* tests/test_radio_frequency_resolver.py covers all 6 supported
regions across the explicit-MHz, slot, and fallthrough paths,
plus a region-table coverage check against
setup_wizard.SUPPORTED_REGIONS. 15 tests, all passing.
Hardware-validated end-to-end:
* Existing-Meshpoint regression on RAK V2 (.141): explicit
frequency_mhz: 905.0 preserved through the resolver, banner
reads `905.000 MHz / SF11 / BW250 (US)`, NodeInfo TX confirmed
at 905 MHz.
* Fresh-install on RAK V2 (.49): wiped local.yaml and SQLite,
re-ran setup wizard with region: EU_868, banner reads
`869.525 MHz / SF11 / BW250 (EU_868)`, NodeInfo TX confirmed
at 869.525 MHz.
* Channel delete UI exercised on the fresh-install Pi: focus a
non-primary row, delete button appears, click removes the row,
save persists.
Co-authored-by: kendelmccarley <60525114+kendelmccarley@users.noreply.github.com>
Summary
frequency lookups
Why
These changes make it easier to develop and test multiple LoRa channel configurations. The Meshtastic slot numbering is the common currency when coordinating frequencies with other operators, but the dashboard previously only accepted raw MHz values, requiring a manual table lookup every time. Being able to delete an entry will ease development and testing.
Type
Testing
Hardware:
Impact
No config schema changes. Slot field is a pure UI convenience — only the resolved frequency_mhz value is written to config on save. Delete Channel removes the row from the DOM; the channel list is only committed on "Save Channels".
AI-assisted?
Yes — developed with Claude Code (Anthropic). Logic reviewed against Meshtastic firmware formula and validated against the slot tables in docs/RADIO-CONFIG-EXPLAINED.md.