Add a page schedule with rotation, Home Assistant trigger rules and a Stopwatch tab

- Schedule: daily blocks from a start time, each with up to 6 rotating pages
  or bitmaps (own duration), switching with the effect or instantly. A manual
  pick (Show tab, HA Mode) lasts until the next block; Back to schedule ends it.
- Rules: an HA state (=, is not, above, below) shows a page while true, adds it
  to the rotation while true, or shows it for N seconds when it becomes true.
  Each rule is polled at its own interval, with a 60 s back-off on errors.
- PageSelector (pure logic, tested) decides the shown page; schedule stored in
  NVS; HA sensor Shown page and button Back to schedule.
- Web UI: Schedule and Stopwatch tabs, bitmaps used by pages or rules in their
  own subsection, schedule in export/import. Version 2.6.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-15 21:36:56 +02:00
1 parent 47ef65ee7d
commit 93b9bec69a
25 files changed
+1217 -50

No files matched your search

+4 -2
View File
@@ -18,7 +18,8 @@ uint32_t FlipdotFrames::dynamicPage(const LocalTime &time, uint32_t now, bool re
bool moving = wait != 0;
// The Nano has no acknowledgement: the next frame waits until it has received this one
// and flipped its dots, or the next frame overflows its serial buffer
uint32_t busy = _show(bitmap, !redraw, moving);
uint32_t busy = _show(bitmap, !redraw, moving || _instant_next);
_instant_next = false;
return moving && wait < busy ? busy : wait;
}
@@ -50,7 +51,8 @@ void FlipdotFrames::noWifi(int hour, int minute)
void FlipdotFrames::page()
{
_drawFullScreen(_page);
_show(_page, false, _instant_next);
_instant_next = false;
}
void FlipdotFrames::live()
+5 -1
View File
@@ -26,6 +26,9 @@ public:
void off() override;
void setDynamicPage(const DynamicPage &page);
// The next page frame is shown without the refresh effect (a rule's page, a schedule
// block that switches instantly)
void showNextInstant() { _instant_next = true; }
// How the Nano flips the dots: for the time a frame takes
void setDrive(uint16_t flip_time, const DriveSettings &drive);
void setPageBitmap(const uint8_t *bitmap);
@@ -37,7 +40,8 @@ private:
BROSE9323 &_display;
PageResources &_resources;
DynamicPage _dynamic;
uint32_t _dot_us = 1000; // per changed dot: pulses x (flip time + gap)
uint32_t _dot_us = 1000;
bool _instant_next = false; // per changed dot: pulses x (flip time + gap)
uint8_t _page[BITMAP_BYTES] = {};
uint8_t _live[BITMAP_BYTES] = {};
uint8_t _off[BITMAP_BYTES] = {}; // all zero: blank