Skip to content

Add auxiliary relay support - #248

Draft
dberlin wants to merge 21 commits into
nebhead:developmentfrom
dberlin:add-auxiliary-relay
Draft

Add auxiliary relay support#248
dberlin wants to merge 21 commits into
nebhead:developmentfrom
dberlin:add-auxiliary-relay

Conversation

@dberlin

@dberlin dberlin commented Aug 11, 2026

Copy link
Copy Markdown

Adds support for auxillary relays.
Just a draft PR for now so people can test it.

dberlin and others added 21 commits August 11, 2026 11:52
Mirrors Task 6's default-dashboard implementation: renders a plug icon
and name badge per configured aux relay (aux1-aux4, hidden when
unconfigured), tracks/updates their on/off state, and wires clicks to
toggle via /api/set/aux/<name>/toggle.

Required two additional fixes beyond the two in-scope files:
- dash_basic.js has no dash_api_set helper; used the equivalent
  cp_api_set from static/js/control_panel.js instead.
- render_status_card() never received settings, so referencing
  settings['platform'] inside it would have raised UndefinedError;
  added the parameter and updated its one call site in dash_basic.html.
.superpowers/sdd/ was only ignored by a nested .gitignore containing `*`,
which ignored itself and so was untracked -- a `git clean -fdx` would remove
it and leave the whole workspace committable. Cover it from the tracked root
.gitignore instead, along with .serena/ and .worktrees/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…spatch reaches it

_configure_aux_menu generated button names like aux_toggle_aux1, but both
display/protoflex.py and display/dsi_800x480t.py only call
_command_handler() when 'cmd_' is a substring of the selected button name
(the ENTER-key path at line 334/355 and the touch path at line 393/429 in
each file respectively). Every other actionable button in these layouts
(cmd_fan_toggle, cmd_igniter_toggle, cmd_monitor, etc.) carries this prefix
for exactly that reason; the aux toggle buttons did not, so they were
unreachable from the touchscreen. _command_handler's substring match on
'aux_toggle_' and its aux_name extraction are unaffected since the prefix
is additive.
The previous commit's edit tool normalized the whole file to LF,
turning a 4-line functional change into a 950-line diff. Restore the
file's original CRLF line endings; no functional change.
The dashboard JS for both the Default and Basic dashboards read the
Jinja-escaped aux relay label back out of the DOM via
getAttribute('title') (which returns the decoded string) and
re-injected it into innerHTML on every relay status change. A label
containing a double quote could close the title attribute and inject
an executable attribute that fired on the next toggle.

Update the icon in place via property/textContent assignment instead
of rebuilding it with innerHTML, for both the pre- and post-Font
Awesome-SVG-conversion DOM shapes. Read the label once from a new
Jinja-escaped data-label attribute rather than parsing it back out of
rendered text. Add server-side length validation on the label as
defence in depth.
- default dashboard: add the missing aux badge-row loop so badges line
  up under their icons (basic dashboard already had this)
- control.py: _process_aux_requests now takes the caller's control
  dict instead of re-reading/re-writing its own snapshot, so a
  caller's later stale write can no longer resurrect an
  already-applied (and supposedly cleared) aux request
- raspberry_pi_all.py: cleanup() now explicitly de-energizes aux via
  _all_aux_off() before closing the devices, matching prototype.py's
  implicit guarantee via power_off()
- spec doc: replace the stale pre-revision text describing dash icons
  (aux_status_N / _configure_dash / _update_dash_objects) with a
  description of the actual shipped menu_aux screen

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds an optional per-button colour override (button_colors) to
MenuGeneric so it stays byte-identical when absent, then uses it to
show each configured auxiliary relay's button green when energised.
Colours are recomputed on every aux-menu build from status_data, and
_fetch_data() now forces a menu rebuild when a relay's state changes
while the menu is open, so the display doesn't need a per-frame
update path. Toggling a relay from the menu now stays on the aux
menu instead of bouncing to the dashboard, so the user sees the
colour change land.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The settings page tells users to configure aux relays in the wizard, but
the four PCB board presets (pcb_2.00a, pcb_3.01a, pcb_pwm, pcb_4.x.x) never
exposed the output_aux1..output_aux4 dropdowns - only Custom Build did,
despite all presets sharing the same raspberry_pi_all platform module.
Copy custom's aux relay settings_dependencies entries into each preset.
The wizard let a user pick the same GPIO for two different grillplatform
fields (e.g. an aux relay and the igniter). gpiozero then raises
GPIOPinInUse when the platform is constructed, control.py's blanket
except-clause swallows it, and the grill silently falls back to the
simulated prototype platform - driving no hardware during a "normal"
looking cook.

Add find_platform_pin_collisions() in wizard.py, which mirrors exactly
what GrillPlatform.__init__ (grillplat/raspberry_pi_all.py) constructs
so inert fields (e.g. the AC fan pin on a DC-fan build, the selector pin
on a standalone build) are never flagged. Wire it into routes.py's
'finish' action before anything is stored or the install is launched;
on collision the request falls through to the existing error-rendering
path instead, naming every colliding pair of fields.
…uard

board-config.py writes system.1WIRE and the SPI0 bus into the boot
config as device-tree overlays (dtoverlay=w1-gpio and dtparam=spi=on,
which fixes CE0/CE1 to GPIO8/GPIO7). The kernel claims these pins at
boot, before control.py ever runs - so if an aux relay is assigned one
of them, GrillPlatform.__init__ loses that race and raises, landing on
the same silent grillplat.prototype fallback this guard exists to
prevent, just through a second door beyond what GrillPlatform.__init__
itself constructs.

Verified independently (not just on the review's word) that adding
these three produces zero false positives against every preset's own
default configuration under both dc_fan states, and that the only
other duplicate-pin default anywhere in the manifest (pcb_4.x.x's
auger/device_distance_trig sharing GPIO23) stays correctly out of
scope since device_distance_trig remains excluded.
board-config.py's enable_gpio_shutdown() writes
dtoverlay=gpio-shutdown,gpio_pin=N whenever inputs.shutdown is set -
the same boot-time kernel pin claim, made the same way, as the
w1-gpio/SPI0 overlays already covered. An aux relay (or the selector)
assigned that pin makes GrillPlatform.__init__ lose the race at
construction time and fall back to the silent prototype platform,
same as every other collision this guard catches.

Verified independently that no preset's own default configuration
collides across selector/shutdown, under every dc_fan and standalone
combination each preset actually offers, and reproduced the
common/common.py default_settings() case (Custom build, standalone
False, selector=shutdown=GPIO17) to confirm the guard both catches it
and produces a comprehensible message naming both user-visible fields.
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