Location screen and settings restructure (client #969, epic #975)

Material 3 blue seed, light, drawn at 390×844 against the real widgets: SettingsShell renders each category as a ListTile with a leading Icon, a subtitle and a trailing chevron under a centred AppBar, and detail panes are Cards of ListTiles split by Divider(height: 1). Icon choices match settings_screen.dart; they are drawn here as inline SVG so this file needs no network to render. Values are examples; the bullets are the behavior to build. GPS mode rides firmware 0xC5 sub-codes 0x0D / 0x0E, gated on caps byte 2 0x20 (meshcore-firmware#1789). Owner-approved 2026-10-23 Approved as drawn: one three-way GPS control above the coordinates, one consolidated status row between them, Location as a top-level category under Radio Settings, Radio stats unchanged for now, and button and buzzer split across Node Settings and Message Settings. Corrected 2026-10-24: wire facts only (the command is 0xC5, not a new 0xC7; the buzzer-only example boards; the sleep-interval floor). The design is unchanged.

The Location screen, one control and three states

1GPS off

The selector is the only on/off control. The status row goes quiet and the sleep row is gone, because neither has meaning here.

Location
GPS
Off
On
Power save
GPS is off
Showing the last stored position
Query
Coordinates
39.103118, -84.512020
Position reporting interval
Every 15 minutes

2GPS on

Continuous acquisition. The sleep row stays hidden: it governs Power save only, and a dead row invites someone to set it.

Location
GPS
Off
On
Power save
GPS is on
Fix 2 minutes ago, 3 satellites
Query
Coordinates
39.103118, -84.512020
Position reporting interval
Every 15 minutes

3Power save, backed off

The state a badge at an event actually sits in. One status row carries activity, next attempt and last fix, so nothing is said twice.

Location
GPS
Off
On
Power save
Backed off
Next try in 48 min, last fix 2 min ago
Query
Sleep between fix attempts
Firmware default, minimum 5 minutes
Coordinates
39.103118, -84.512020
Position reporting interval
Every 15 minutes

4A build that cannot do Power save

Most variants do not compile the duty cycle. The third option is absent rather than offered and ignored.

Location
GPS
Off
On
GPS is on
Fix 2 minutes ago, 3 satellites
Query
Coordinates
39.103118, -84.512020
Position reporting interval
Every 15 minutes

Where it lives

5Settings, current and proposed

GPS sits two levels down today, inside Node Settings then the Location dialog, with its enable toggle duplicated in both. Location becomes its own category and owns GPS outright.

Current
Settings
Node Settings
Radio Settings
Frequency, power, spreading factor
Radio stats
Noise floor, RSSI, SNR, and airtime
Privacy Settings
Control what information is shared.
Contacts
Settings for how contacts are added.
Blocked
Users you've blocked
Message Settings
Notifications and message handling
App Settings
Appearance, translation, and map preferences
Device Info
Proposed
Settings
Node Settings
Radio Settings
Frequency, power, spreading factor
Locationnew
GPS, coordinates, and reporting
Radio statsunchanged for now
Noise floor, RSSI, SNR, and airtime
Privacy Settings
Control what information is shared.
Contacts
Settings for how contacts are added.
Blocked
Users you've blocked
Message Settings
Notifications and message handling
App Settings
Appearance, translation, and map preferences
Device Info

6Button and buzzer split across two panes

One entry today, shown on button matrix or notify scope together. Boards such as the M5Stack Unit C6L, ThinkNode M9 and Wio Tracker L1 e-ink have a buzzer and no button, so they are not one feature.

Node Settings keeps the button
Node Settings
Node name
Strycher
Button actions
Assign what the button does
Message Settings gains the buzzer
Message Settings
Notifications
When this phone alerts you
Buzzer and tonesnew
When this radio beeps, and which tone

Why these choices, so a later change knows what it is breaking

One three-way control, not a toggle plus a picker

The badge's own screen does exactly this: setGpsMode() at MyMesh.h:458 writes both fields, and gpsEnabledForMode exists for the writer that sets both. What is forbidden is a bool-only writer clobbering the mode byte. An earlier draft had two controls and was wrong; #969's original wording pointed that way.

One status row, not two

Activity and last fix were separate blocks in the first draft. In a dense app that is one job taking two slots, so they are merged into a single ListTile between the selector and the coordinates.

Sleep interval is not the reporting interval

gps_interval is a reporting cadence; gps_ps_interval_s is a power duty cycle (NodePrefs.h:73-78). They live in different cards so the difference is visible, and are never merged.

Deferred, tracked separately

GPS fix state in the Channels and Contacts headers is #974; it lands in the same shared app-bar widget as epic #613, so it must follow #614 rather than run beside it. Where Radio stats lives is #980.

The save reply must echo what was stored

The client renders the device's answer, never its own assumption. Choosing Off does not change the stored mode byte, so a client modelling its own write would show the wrong mode. Clamping has the same shape: the reporting interval is already constrained to 0 through 86400, so the user must see the clamped value, not what they typed. Delivered: the 0xC5 GPS reply carries the stored values after every write.

Power save is hidden where the build cannot drive it

OFFBAND_GPS_POWER_SAVE is per variant and currently only on the QCC badge. The GPS reply's ps_ok byte says whether this radio can run it; without that, the client would offer a mode that does nothing.