Skip to content

feat(lora): surface FEM LNA Mode config on Device Config + Remote Admin (#3599)#3600

Merged
Yeraze merged 1 commit into
mainfrom
feat/3599-fem-lna-mode
Jun 21, 2026
Merged

feat(lora): surface FEM LNA Mode config on Device Config + Remote Admin (#3599)#3600
Yeraze merged 1 commit into
mainfrom
feat/3599-fem-lna-mode

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Summary

Adds support for Config.LoRaConfig.fem_lna_mode (the FEM_LNA_Mode enum), a LoRa Front-End Module (FEM) / Low Noise Amplifier (LNA) mode option added in Meshtastic firmware v2.7.20 (meshtastic/firmware#9809). Users on amplified boards with an external LNA (e.g. certain RAK modules) can now configure it from the MeshMonitor UI instead of dropping to the Python CLI.

Closes #3599

Protobuf

The field was already vendored — the protobufs submodule on main is at v2.7.25, which defines enum FEM_LNA_Mode { DISABLED = 0; ENABLED = 1; NOT_PRESENT = 2; } and FEM_LNA_Mode fem_lna_mode = 106; inside Config.LoRaConfig. No submodule bump was required. The enum is mirrored into shared constants (src/server/constants/meshtastic.ts) per project rules — no magic numbers.

Both surfaces (read + display + write)

  • Device Configuration (LoRaConfigSection / ConfigurationTab): new "FEM LNA Mode" select. Load, change-tracking (save bar), save payload, and config-import all carry femLnaMode.
  • Remote Admin (AdminCommandsTab): new "FEM LNA Mode" select. Both remote-config read spots and the setLoRaConfig save payload carry femLnaMode.

Both save paths funnel through protobufService.createSetLoRaConfigMessage, which now encodes femLnaMode for the local config-set and for the remote admin message.

proto3 elision (#3594)

The zero enum value DISABLED is a real selectable mode, so:

  • It is read with a 0 default (never a non-zero ?? fallback).
  • The backend proto3-default block fills femLnaMode = 0 when the field is elided on the wire.

Firmware gating

The control is shown unconditionally; writing 0 (DISABLED) is harmless on older firmware that doesn't know field 106. No new gating framework was introduced.

Tests

  • src/server/protobufService.femLnaMode.test.ts — round-trips femLnaMode through encode/decode (ENABLED, NOT_PRESENT, and the proto3-zero DISABLED elision case, plus an absent-field case).
  • src/components/configuration/LoRaConfigSection.test.tsx — covers the FEM_LNA_MODE_OPTIONS constants and the save payload.
  • Full Vitest suite passes: 7051 tests, 0 failures.
  • tsc -p tsconfig.server.json and frontend tsc --noEmit show no new errors (only the documented pre-existing baseline).

🤖 Generated with Claude Code

…in (#3599)

Adds support for Config.LoRaConfig.fem_lna_mode (FEM_LNA_Mode enum), a LoRa
Front-End Module / Low Noise Amplifier mode option added in Meshtastic firmware
v2.7.20 (meshtastic/firmware#9809). Users on amplified boards with an external
LNA (e.g. certain RAK modules) can now configure it from the UI instead of the
Python CLI.

The protobuf field was already vendored (submodule at v2.7.25), so no submodule
bump was needed. The enum (DISABLED=0, ENABLED=1, NOT_PRESENT=2, field tag 106)
is added to shared constants in src/server/constants/meshtastic.ts.

Surfaced on BOTH UI surfaces, end-to-end (read + display + write):
- Device Configuration (LoRaConfigSection / ConfigurationTab): new "FEM LNA Mode"
  select; load, change-tracking, save payload, and config-import all carry
  femLnaMode.
- Remote Admin (AdminCommandsTab): new "FEM LNA Mode" select; both remote-config
  read spots and the setLoRaConfig save payload carry femLnaMode.

Both save paths funnel through protobufService.createSetLoRaConfigMessage, which
now encodes femLnaMode for the local config-set and the remote admin message.

proto3 elision (#3594): the zero enum value DISABLED is a real selectable mode,
so it is read with a 0 default (never a non-zero ?? fallback) and the backend
proto3-default block fills femLnaMode=0 when elided. Firmware gating: the control
is shown unconditionally; writing 0 (DISABLED) is harmless on firmware that does
not know field 106.

Tests: protobufService.femLnaMode.test.ts round-trips the field (incl. the
proto3-zero elision case); LoRaConfigSection.test.tsx covers the option constants
and save payload. Full Vitest suite passes (7051 tests, 0 failures).

Closes #3599

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: feat(lora): surface FEM LNA Mode config

Tasks:

  • Read changed files
  • Analyze code quality and best practices
  • Check for bugs and issues
  • Evaluate test coverage
  • Post final review

Overall Assessment

This is a clean, well-structured implementation of fem_lna_mode support. The author clearly understands the proto3 elision pitfall (which burned the project in #3594) and has handled it correctly throughout. The PR follows established project patterns for adding new enum-backed config fields.


Strengths

Proto3 Elision Handled Correctly — The zero-value DISABLED (0) is explicitly defaulted in every relevant codepath rather than using a non-zero fallback. The commit even calls this out in comments (e.g. src/server/meshtasticManager.ts) and the test file has a dedicated case for it.

Single Encode/Decode Path — Both Device Configuration and Remote Admin save paths funnel through protobufService.createSetLoRaConfigMessage, so there's no duplicated encoding logic to drift.

Constants Properly MirroredFemLnaMode is added to src/server/constants/meshtastic.ts following the same pattern as other project constants (const object + type alias + name-lookup function).

Change Tracking CompletefemLnaMode appears in the initialValuesRef, hasChanges memo, resetChanges callback, and handleSave callback in LoRaConfigSection.tsx. No save-bar gap.


Issues / Observations

Minor: FEM_LNA_MODE_OPTIONS reuses RegionOption type

File: src/components/configuration/constants.ts:129

export const FEM_LNA_MODE_OPTIONS: RegionOption[] = [

RegionOption is typed elsewhere for regions, but FEM_LNA_MODE_OPTIONS is structurally compatible ({ value, label }). This is technically fine but slightly confusing — a reader unfamiliar with the codebase might wonder why a LoRa option uses RegionOption. A FemLnaModeOption type alias (even just type FemLnaModeOption = RegionOption) or inline { value: number; label: string }[] would be clearer. Not a correctness concern, just a readability nit.

Minor: AdminCommandsTab description text is not i18n'd

File: src/components/AdminCommandsTab.tsx (FEM LNA Mode select block)

The Device Configuration tab correctly uses t('lora_config.fem_lna_mode') and t('lora_config.fem_lna_mode_description'). The Remote Admin tab has the description inline:

<span className="setting-description">Front-End Module (FEM) Low Noise Amplifier (LNA) mode for amplified boards with an external LNA. Requires firmware v2.7.20 or later.</span>

Other fields in the Remote Admin tab also use hardcoded strings, so this is consistent with surrounding code. But if the project ever fully i18n's the admin tab, this field will need the same treatment. Low priority.

Minor: useAdminCommandsState.ts initial value for femLnaMode

File: src/components/admin-commands/useAdminCommandsState.ts

The interface at line 22 declares femLnaMode: number. The initial state value (where the reducer is initialized) should be verified to be 0 and not undefined. The PR description says it was added but this test file only shows the interface. Worth confirming the initial state in the reducer's initialState object defaults femLnaMode to 0 — but from the context this appears correct.

Observation: getFemLnaModeName helper added but not used in new UI

File: src/server/constants/meshtastic.ts:147

getFemLnaModeName is exported and follows the same pattern as getTransportMechanismName. It's currently unused in any display path within this PR. This is fine (consistent pattern, useful for logging/debug), but worth noting in case a future display of the value needs it.

Test Coverage: LoRaConfigSection.test.tsx Save Config test is shallow

File: src/components/configuration/LoRaConfigSection.test.tsx:141

The "includes femLnaMode in the LoRa save payload" test constructs a plain object manually — it doesn't actually call any save function or exercise the component's handleSave. This tests the shape of the payload but not whether ConfigurationTab actually passes femLnaMode through to the API call. The protobuf round-trip test in protobufService.femLnaMode.test.ts covers the wire encoding well, but there's no test verifying the full frontend→API→protobuf chain for femLnaMode. This is consistent with how other LoRa fields are tested in this file, so it doesn't block merging.


Summary

The implementation is correct and production-ready. The proto3 elision concern is the main footgun for this type of change, and it's been addressed on every relevant codepath: the backend config parser, the meshtasticManager defaults block, both save payloads, and the protobuf encoding test. The minor issues above are low-severity and consistent with surrounding code style — they don't warrant blocking this PR.

Recommend merge with optional cleanup of the RegionOption type reuse if desired.

@Yeraze Yeraze merged commit e0c5b17 into main Jun 21, 2026
19 checks passed
@Yeraze Yeraze deleted the feat/3599-fem-lna-mode branch June 21, 2026 13:47
Yeraze added a commit to TheWISPRer/meshmonitor that referenced this pull request Jun 21, 2026
Resolve migration-number collision: renumber the dead-drop migration
094_create_dead_drop -> 095_create_dead_drop (keeping main's
094_add_meshcore_node_favorite). Update migrations.ts registry,
migrations.test.ts (count 95, last=create_dead_drop), and the migration's
internal Postgres/MySQL export names. meshtasticManager.ts auto-merged
cleanly (main's Yeraze#3593/Yeraze#3598/Yeraze#3600 changes + the mailbox dispatch coexist).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Support FEM LNA Mode configuration (LoRa)

1 participant