Add detailed and retained MQTT sleep timer status - #446
Open
TMHEINE wants to merge 19 commits into
Open
Conversation
STATIC_IP_ENABLE, PLAY_LAST_RFID_AFTER_REBOOT, PAUSE_WHEN_RFID_REMOVED, DONT_ACCEPT_SAME_RFID_TWICE, RESUME_ON_SAME_RFID and NEOPIXEL_REVERSE_ROTATION only ever controlled the pre-first-save NVS default; every one of them has long been fully configurable at runtime via the web interface. Collapsing them to a single hardcoded false default removes the class of bug where the compile-time default and the web-UI's read/factory-defaults code could drift out of sync (as DONT_ACCEPT_SAME_RFID_TWICE's did). ACCEPT_SAME_RFID_AFTER_TRACK_END, previously always coupled 1:1 to PAUSE_WHEN_RFID_REMOVED with no independent toggle, is collapsed to its permissive branch (allow reapplying the same RFID after track/playlist end). Breaking for custom builds: a settings-override.h still defining one of these macros will silently no longer have any effect. Static IP itself is unaffected - it has been a fully runtime, per-network web-UI setting for a while; the removed macro only fed a one-time migration for pre-multi-network configs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SHUTDOWN_ON_BAT_CRITICAL only ever controlled the pre-first-save NVS
default; move it fully to the web interface with a new checkbox
("Bei kritischer Spannung automatisch abschalten"), off by default -
same behavior as before for anyone who didn't compile with the flag.
The threshold slider's minimum is raised from 2.0V to 2.8V (2.0V is
below any supported chemistry's safe cutoff), and its row is now
reachable/visible independent of any compile flag. Battery fieldset
also reordered (measurement interval moved to the top) and the unused
"Status über Neopixel anzeigen" description line removed.
Breaking for custom builds: a settings-override.h still defining
SHUTDOWN_ON_BAT_CRITICAL will silently no longer have any effect -
enable the checkbox in the web UI instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Nothing in the codebase ever checked this macro via #ifdef - mono/stereo playback has been a runtime web-UI setting (playMono in NVS) only. Just dead documentation left over in settings.h/.sample and settings-complete.h. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Led_Task was still running its idle animation when esp_deep_sleep_start() was called; NeoPixels just hold their last-sent color with no active refresh, so the last-shown frame stayed lit forever. Call Led_Exit() first. Deliberately not cutting peripheral power here (Power_PeripheralOff()): on boards with a port-expander, that write immediately re-triggers the wakeup-button's shared interrupt line, waking the board right back up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RFID_SCAN_INTERVAL was a compile-time-only constant; move it to the web
interface ("MFRC522 Scan-Intervall") with the same 100ms default, cached
once in RfidMfrc522_Init() rather than read from NVS on every task-loop
iteration. A restart is required for a change to take effect, same as
the other MFRC522/PN5180-specific settings (gain, PN5180 debounce).
Also fix a couple of pre-existing German wording issues found nearby
("RFID reader" -> "RFID-Reader", missing comma).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Settings sliders now show a live value badge and circular +/- step buttons instead of a bare drag handle, sized off each slider's own data-slider-step (falls back to 1). Existing decorative volume/battery/ hourglass icons are removed since the buttons take over that role, and the label/value grid split (col-sm-4 + col-sm-8) is unwrapped so the track gets the full row width on every screen size instead of being squeezed into 2/3 of it. Related settings across every tab (Allgemein sub-tabs, WLAN, MQTT, FTP, Bluetooth, Updates, Tools) are now grouped into rounded cards instead of being separated by <hr> lines, with consistent spacing between groups. A few pre-existing `<span class="container">` wrappers in the Tools tab were promoted to `<div>` - span is inline by default, so margin utility classes on them were silently no-ops. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NUM_INDICATOR_LEDS, NUM_CONTROL_LEDS, CONTROL_LEDS_COLORS, NUM_LEDS_IDLE_DOTS, OFFSET_PAUSE_LEDS, PROGRESS_HUE_START, PROGRESS_HUE_END, ATMO_HUE, ATMO_SATURATION, DIMMABLE_STATES and LED_OFFSET only ever fed the pre-first-save NVS default; every one of them has long been fully configurable at runtime via the web interface, and none are board-specific or used as a compile-time array bound (the LED buffer is heap-allocated at runtime sized off the NVS values). Collapsed to a single hardcoded default matching the old macro value. CHIPSET/COLOR_ORDER are unaffected - those are genuine compile-time FastLED template parameters and can never be runtime-configurable. Breaking for custom builds: a settings-override.h still defining one of the removed macros will silently no longer have any effect - set the value once via the web UI instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Not currently exploitable since the source is always a fixed-length RFID tag ID, but matches the unterminated-copy pattern fixed for a genuinely user-controlled path in biologist79#443. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…A) + silent wedge/removal disambiguation (biologist79#444) * fix(pn5180): disambiguate a wedged reader from a removed card The PN5180 can start returning corrupted responses and then stop answering until it is reset (per the PN5180-Library author). A wedged reader is indistinguishable from a removed card - reads just fail - so declaring the card removed on the pn5180Debounce timeout alone pauses playback on a card that is still resting on the reader. When reads have failed for the whole debounce window, run one silent re-init sweep before declaring removal: it re-runs the reader's own RESET/SETUPRF/read states (the same sequence the post-removal path uses), steered so the full cross-protocol sweep completes before the wedged protocol is re-read, with removal suppressed throughout. If the card is re-found it was a wedge and playback continues silently (the same-card fast path leaves lastCardId intact, so no re-queue or pause); if the sweep finds nothing it was really removed and removal is declared then. One sweep per failure episode; a removed card is declared removed one sweep after the debounce. Three LOGLEVEL_DEBUG lines (suspected / recovered / gave up) make the behaviour visible on the serial log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(pn5180): deterministic ISO-14443 re-read via WUPA + HLTA readCardSerial() polls with REQA (activateTypeA kind=0), which an ISO-14443 PICC answers only from IDLE. After a successful read the PICC is left SELECTed (ACTIVE) and ignores REQA until the RF field is power-cycled, so a resting card is re-read only when the field happens to be cycled and drops out otherwise. Poll with WUPA (activateTypeA kind=1) and park the card in HALT (mifareHalt) after each read instead: WUPA is answered from both IDLE and HALT, so first detection (a freshly placed card is IDLE) and re-detection (a card parked in HALT) are both covered with no field cycle, and a resting card is re-read on every poll. A removed card answers neither, so the read fails cleanly and the existing wedge-vs-removal sweep and debounce run unchanged. readCardSerial() hardcodes REQA and leaves mifareHalt() unused, so both are driven from a small local helper whose UID validation matches readCardSerial(). The ISO-15693 path and the LPCD WakeupCheck() are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…iologist79#413) * RfidMfrc522: deterministic WUPA presence poll for pauseIfRfidRemoved The card-still-present detection in pauseIfRfidRemoved mode was built on PICC_IsNewCardPresent(), which sends REQA (0x26). REQA only invites cards in the IDLE state; a card left ACTIVE after Select (as this mode did, by skipping HaltA) never answers it. The old loop leaned on re-Select 'voodoo' (miguelbalboa/rfid biologist79#188) whose success was non-deterministic on stationary cards, causing pause/resume flapping while a card sat perfectly still on the reader, and forcing a large miss debounce that only masked dropped polls and added removal latency. Fix: park the card in HALT after reading and poll with WUPA (0x52), the only REQ-family command that wakes a HALTed card. Each poll is now a clean yes/no. The new RfidMfrc522_CardStillPresent() template helper works for both the SPI MFRC522 and I2C MFRC522_I2C classes via Reader:: constants (their register addresses, StatusCode values, and WakeupA return types differ). Removal debounce is 2 cycles (~100ms), purely for RF-noise tolerance. Also hoists lastValidcardId out of the loop body so 'same card reapplied' comparisons survive iterations (it was re-zeroed every pass). Live-tested ~2h on ESP32-S3 + RC522 clone (SPI, version reg 0x82) with an audiobook playlist: no flapping on stationary cards (38s+ continuous presence verified), genuine removals detected in 100-400ms. Testing also confirmed a related tuning note: with the default mfrc522Gain (7 = 48dB max) a card lying directly ON the reader coil saturates the receiver and gets phantom removals; mfrc522Gain=5 fixes contact distance. Full analysis and test data in docs/rfid-presence-detection.md. Both reader-class template instantiations compile (esp32-s3-devkitc-1 env with RFID_READER_TYPE_RUNTIME + I2C_2_ENABLE). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HeyF87wQMY3kVv3hz3UZSw * Rfid: forget the last-seen tag when its assignment changes Re-assigning an RFID-tag from the web UI had no effect until the next reboot or deep-sleep cycle: presenting the tag kept starting the *old* book. Both "same tag as last time" dedups live only in RAM and nothing invalidated them when a tag's meaning changed: * pauseIfRfidRemoved: the reader task's own lastValidcardId buffer. When a tag is re-applied and the track/playlist is unfinished, the reader short-circuits it into a play/pause toggle and never puts it on the queue -- so Rfid_PreferenceLookupHandler never runs and NVS is never re-read. The freshly assigned book is never loaded and the previously loaded playlist simply resumes. * dontAcceptRfidTwice: gOldRfidTagId. This one was already reset on the rfidAssign path, but the reset was gated on dontAcceptRfidTwice itself, and the REST POST /rfid handler, the mod-card path, the delete path, the NVS-erase and the NVS-restore all missed it entirely. Add Rfid_ResetLastTag(), which clears both, and call it from every path that changes what a tag means. The reader's buffer is task-local, so it is cleared via an atomic request flag that the reader task consumes at the top of its loop. Also zero-initialise the PN5180 reader's lastValidcardId, which was comparing against uninitialised stack memory. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pauseIfRfidRemoved's card-removed handler queued PAUSEPLAY unconditionally, even when idle after natural playlist end - AudioPlayer_Cyclic() then rejected it with a "no playmode change while idle" error, a confusing message for what is just normal card removal after playback ended. Also backfill changelog entries for biologist79#413, biologist79#442, biologist79#443 and biologist79#444, which were merged without one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The runtime clamp (AudioPlayer_SetVolume honoring AudioPlayer_MinVolume) already existed but was never fed from a setting, so it was stuck at 0. Wire it up end to end: - AudioPlayer_Init reads "minVolume" from NVS and applies it; the boot volume (initial or remembered-last) is raised to the floor if below it. - Web.cpp save handler validates min < both maximums and rejects otherwise, plus settingsToJSON read-back and factory default (0). - New "Minimal" slider in the volume settings card (0..21) with the same stepper/badge treatment as the sibling sliders, plus de/en/fr locales. - Led.cpp volume animation maps from GetMinVolume() instead of a fixed 0. Based on the idea in biologist79#392. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds detailed MQTT status topics for the sleep timer while keeping the existing
sleepTimertopic backward compatible.New retained MQTT topics
sleepTimerModesleepTimerActivesleepTimerRemainingMinutessleepTimerRemainingTracksSupported modes:
OFFMINUTESEOTEOPEO5TBehaviour
The existing
sleepTimertopic remains unchanged and continues to publish:EOTEOPEO5T0The new topics are retained and are republished after MQTT reconnect.
For minute-based timers, the remaining minutes are updated once per minute.
For
EO5T, the remaining number of tracks is updated at track changes.When a timer finishes or is cancelled, the retained state is reset to:
sleepTimerMode = OFFsleepTimerActive = 0sleepTimerRemainingMinutes = 0sleepTimerRemainingTracks = 0This happens before the device enters deep sleep.
Tested
EOTEOPEO5TPlatformIO build: