Skip to content

Parameter presets#4

Merged
wjcloudy merged 15 commits into
mainfrom
feature/param-presets
Jul 11, 2026
Merged

Parameter presets#4
wjcloudy merged 15 commits into
mainfrom
feature/param-presets

Conversation

@wjcloudy

Copy link
Copy Markdown
Owner

Named parameter-value sets (presets.json), created and managed in a Parameters-rail editor and applied there or from an Apply preset action tile on a gauges page.

  • Editor populates from a searchable picker, favourites, a full snapshot, or changed-from-default (captures exactly your tune); values prefill live and every row is editable/removable
  • Apply = confirm → progress bar → summary naming rejections → optional save-to-flash
  • Presets ride the settings export/import bundle
  • 6 new E2E specs (95 total)

🤖 Generated with Claude Code

wjcloudy added 15 commits July 11, 2026 09:54
…ge button

A preset is a named name->value map stored in presets.json on the
device (and carried wholesale by the settings export/import). The
Parameters action rail gains a Presets group: one row per preset with
apply/edit/delete, plus New preset.

The editor solves 'not every param belongs in a preset': start empty
and add params one at a time (searchable picker, live values prefilled
and editable), pull in the favourites, snapshot everything, or — the
one people actually want — Add changed-from-default, which captures
exactly the parameters that differ from their schema defaults. Every
row is removable.

Applying reuses the file-load machinery: confirmation with the count,
the rail progress bar, and a summary naming every rejection; the
summary then offers an optional save-to-flash (declining keeps the
values live-only, as any 'set'). Action tiles gain a third kind, Apply
preset, so a gauges-page button can fire a whole set — confirm-first
like all action buttons, with the applied/rejected count in the
tooltip.
Set_Day/Hour/Min/Sec always differ from their defaults — they're the
time of day, not tune — so changed-from-default would drag them into
every capture. The mock gains a Set_Hour param so the spec proves the
exclusion.
The tick icon said nothing about what applying does with flash — it's
now two labelled buttons: Apply (live only, reverts on reboot) and
Apply & save (applies then runs 'save'), replacing the post-apply
question dialog. Preset rows get uniform-height compact buttons (the ×
rendered taller than the icon buttons) with the name on its own line.

Parameter values in the table are now always-editable inputs instead
of click-to-edit spans: focusing snapshots the value so the poll can't
revert keystrokes, Enter/blur commit only when changed, Escape reverts.
Backup & Restore moves off the Web Interface sub-tab into a new
Configuration sub-tab (deep-linkable as #settings/config). Alongside
the full bundle it gains an Individual Files card: gauge pages,
parameter presets, favourites, plot layouts and UI preferences each
back up as their raw device file and restore one at a time — with a
light shape check so a presets file can't silently replace the gauge
layout, a confirmation naming what's replaced, and a reload after.
Loading the sample replaced the pages in state, but when the new first
page carried the SAME id as the old one the grid container kept its DOM
(same render key) — and gridstack had already stripped those tiles'
gs-* attributes, so the re-init rendered the stale layout until a page
switch forced a remount. The grid now also keys on a layout generation
counter that bumps on wholesale replacement.
A Centre value (blank = off) makes the arc/fill grow FROM that point:
values above it draw forward, values below draw backward from the
pivot, which is marked with a tick across the track. The classic use
is a 0-centred power gauge — drive sweeps one way, regen the other —
and the sample's Driving power radial (-50..300) now ships centred
on 0.
An Invert checkbox (radial/bar settings) mirrors the whole scale: the
fill anchors at what is now the Max end, the Min/Max corner and end
labels swap, and the centre-point pivot (when set) mirrors with it.
The compact title row (built for indicator tiles) now covers text tiles
too, and the value beneath survives it: the body render gate drops to
~8px of gauge area for text tiles, tiny tiles stop reserving padding,
and the value font caps at the available height (9px floor) instead of
overflowing. A phone 2x1 (~53x23px content) now reads label over value.
Gauges: needle-dial style for radials; peak-hold markers on radial/bar
(session max, plus min on centred gauges); value alarms on the warn
threshold (flash tile / beep / browser notification, edge-triggered);
formula tiles computing a value from spot values (whitelisted arithmetic,
own unit text); second series on line tiles with per-series colours and
dual live readouts; drive mode (full-screen gauges, chrome hidden,
floating exit, fullscreen API).

Parameters: applying a preset now opens a dry-run diff (current vs preset,
matching rows skipped) before anything is sent; compare-to-file shows a
read-only diff of a params file against live values with 'save differences
as preset'; a parameter change log records every UI-issued set in a
200-entry ring buffer on the device (paramlog.json) with CSV export.

Device nickname: stored in settings.json, shown in the sidebar and browser
tab, doubles as the sanitised mDNS/OTA/WiFi hostname after reboot.

Mock ESP learns dev_name; specs updated for the preset diff flow and the
decimals select id, plus new gauge-extras and paramtools suites (109 total).
Recorder: a Record button on the gauges page captures every streamed
sample (in memory, across page switches) and on Stop shows a review
chart with CSV export — a zero-hardware drive logger.

Streaming: the firmware now pushes spot values over SSE from a minimal
raw-TCP listener on port 81 (the synchronous WebServer can't hold a
connection open without blocking everything else). One 'get' per
interval from loop(), one client at a time, names whitelisted, UART
mode only; /settings advertises sse_port. The gauges page prefers the
stream and falls back to the classic polling loop on any error, so old
firmware keeps working unchanged.

Mock ESP serves the same stream on its own port (response-close, not
request-close, ends the interval — req 'close' fires immediately on
modern Node); fixtures force-close held SSE connections at teardown.
LastPass ignores data-lpignore for its injected in-field icons and pairs
the SSID inputs with the adjacent passphrase fields as a login form; the
multi-column layout also mispositions the overlay. Nothing on the page is
a credential, so hide the injected overlay elements outright and label the
WiFi secrets as Passphrase (accurate, and off the login-form heuristic).
@wjcloudy
wjcloudy merged commit f7bc773 into main Jul 11, 2026
3 checks passed
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.

1 participant