Public Access
Add a web UI to the clock firmware
- Web UI on port 80: live view of the display, choose the page (clock or a bitmap), bitmaps (list, show, live, edit, delete), draw / image upload with conversion in the browser, live mode, settings, Home Assistant - Live view from a shadow buffer in the BROSE9323 driver (shownPixel) - Settings in NVS with the original behaviour as defaults: schedule times, quarter hours, yawning Pikachu, No WiFi screen, flip time, status LED, Home Assistant broker (restart on change) - Bitmaps on LittleFS; new 320 KB partition, ota slots now 1.31 MB - DisplayController: pages, live mode, settings; app.cpp holds the objects - README for the recovery app; docs for the web UI - Tests: settings, time parsing, bitmap format and names, schedule with settings, pages and live mode (95 total) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c9e0800e36
commit
2b1ac12ed7
34 files changed
+1989
-117
No files matched your search
@@ -7,6 +7,42 @@ DisplayController::DisplayController(DisplayPower &power, Frames &frames) : _pow
|
||||
{
|
||||
}
|
||||
|
||||
void DisplayController::setSettings(const ScheduleSettings &schedule, const ClockSettings &clock)
|
||||
{
|
||||
_schedule = schedule;
|
||||
_clock = clock;
|
||||
_content_revision++; // e.g. evening time changed: redraw
|
||||
}
|
||||
|
||||
void DisplayController::setPage(Page page)
|
||||
{
|
||||
_page = page;
|
||||
_content_revision++;
|
||||
}
|
||||
|
||||
void DisplayController::pageChanged()
|
||||
{
|
||||
_content_revision++;
|
||||
}
|
||||
|
||||
void DisplayController::showLive(uint32_t now)
|
||||
{
|
||||
_live = true;
|
||||
_live_since = now;
|
||||
_content_revision++;
|
||||
}
|
||||
|
||||
void DisplayController::stopLive()
|
||||
{
|
||||
_live = false;
|
||||
_content_revision++;
|
||||
}
|
||||
|
||||
bool DisplayController::liveActive() const
|
||||
{
|
||||
return _live;
|
||||
}
|
||||
|
||||
void DisplayController::setClockEnabled(bool enabled)
|
||||
{
|
||||
_clock_enabled = enabled;
|
||||
@@ -36,7 +72,10 @@ void DisplayController::update(uint32_t now, bool time_valid, int hour, int minu
|
||||
_wifi_lost = true;
|
||||
_wifi_lost_since = now;
|
||||
}
|
||||
bool show_no_wifi = _wifi_lost && now - _wifi_lost_since >= NO_WIFI_SHOW_TIME;
|
||||
bool show_no_wifi = _clock.no_wifi_screen && _wifi_lost && now - _wifi_lost_since >= NO_WIFI_SHOW_TIME;
|
||||
|
||||
if (_live && now - _live_since >= LIVE_TIMEOUT)
|
||||
stopLive();
|
||||
|
||||
// Switched off elsewhere (power timer, Nano flashing): the Nano lost its frame
|
||||
if (!_power.isOn())
|
||||
@@ -53,12 +92,21 @@ void DisplayController::update(uint32_t now, bool time_valid, int hour, int minu
|
||||
{
|
||||
if (_power.isOn())
|
||||
{
|
||||
_showBlank();
|
||||
_showOnce(Screen::Blank);
|
||||
_off_timer.request(now, CLEAR_BEFORE_OFF_TIME);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Live mode from the web UI: shown right away, whatever the time
|
||||
if (_live)
|
||||
{
|
||||
_off_timer.cancel();
|
||||
_ensureOn();
|
||||
_showOnce(Screen::Live);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the boot screen until the time is known
|
||||
if (!time_valid)
|
||||
return;
|
||||
@@ -68,64 +116,83 @@ void DisplayController::update(uint32_t now, bool time_valid, int hour, int minu
|
||||
// The logo is only shown while the display is on; it never switches the power on
|
||||
if (_power.isOn())
|
||||
{
|
||||
_showLogo();
|
||||
_showOnce(Screen::Logo);
|
||||
_off_timer.request(now, POWER_OFF_DELAY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (scheduleFor(hour, minute))
|
||||
switch (scheduleFor(hour, minute, _schedule))
|
||||
{
|
||||
case ScheduleMode::Night:
|
||||
if (_power.isOn())
|
||||
{
|
||||
_showBlank();
|
||||
_showOnce(Screen::Blank);
|
||||
_off_timer.request(now, POWER_OFF_DELAY);
|
||||
}
|
||||
break;
|
||||
case ScheduleMode::Clock:
|
||||
_off_timer.cancel();
|
||||
if (!_power.isOn())
|
||||
{
|
||||
_power.on();
|
||||
_screen = Screen::Unknown;
|
||||
}
|
||||
_showClock(hour, minute, show_no_wifi);
|
||||
_ensureOn();
|
||||
if (_page == Page::Clock)
|
||||
_showClock(hour, minute, show_no_wifi);
|
||||
else
|
||||
_showOnce(Screen::Page);
|
||||
break;
|
||||
case ScheduleMode::Standby:
|
||||
// Flipdots keep their image without power, so the last time stays visible
|
||||
// Flipdots keep their image without power, so the last image stays visible
|
||||
if (_power.isOn())
|
||||
_off_timer.request(now, POWER_OFF_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayController::_ensureOn()
|
||||
{
|
||||
if (_power.isOn())
|
||||
return;
|
||||
_power.on();
|
||||
_screen = Screen::Unknown;
|
||||
}
|
||||
|
||||
void DisplayController::_showClock(int hour, int minute, bool no_wifi)
|
||||
{
|
||||
Screen screen = no_wifi ? Screen::NoWifi : Screen::Clock;
|
||||
if (_screen == screen && _shown_hour == hour && _shown_minute == minute)
|
||||
bool evening = isEvening(hour, minute, _clock);
|
||||
if (_screen == screen && _shown_hour == hour && _shown_minute == minute && _shown_evening == evening &&
|
||||
_shown_revision == _content_revision)
|
||||
return;
|
||||
if (no_wifi)
|
||||
_frames.noWifi(hour, minute);
|
||||
else
|
||||
_frames.clock(hour, minute);
|
||||
_frames.clock(hour, minute, evening);
|
||||
_screen = screen;
|
||||
_shown_hour = hour;
|
||||
_shown_minute = minute;
|
||||
_shown_evening = evening;
|
||||
_shown_revision = _content_revision;
|
||||
}
|
||||
|
||||
void DisplayController::_showLogo()
|
||||
// Screens without the time: only sent again when they or their content changed
|
||||
void DisplayController::_showOnce(Screen screen)
|
||||
{
|
||||
if (_screen == Screen::Logo)
|
||||
if (_screen == screen && _shown_revision == _content_revision)
|
||||
return;
|
||||
_frames.logo();
|
||||
_screen = Screen::Logo;
|
||||
}
|
||||
|
||||
void DisplayController::_showBlank()
|
||||
{
|
||||
if (_screen == Screen::Blank)
|
||||
return;
|
||||
_frames.blank();
|
||||
_screen = Screen::Blank;
|
||||
switch (screen)
|
||||
{
|
||||
case Screen::Page:
|
||||
_frames.page();
|
||||
break;
|
||||
case Screen::Live:
|
||||
_frames.live();
|
||||
break;
|
||||
case Screen::Logo:
|
||||
_frames.logo();
|
||||
break;
|
||||
default:
|
||||
_frames.blank();
|
||||
break;
|
||||
}
|
||||
_screen = screen;
|
||||
_shown_revision = _content_revision;
|
||||
}
|
||||
Reference in new issue
Block a user