Public Access
- Text elements can scroll in a window at a set speed; clock, countdown and stopwatch can show seconds. Moving pages are sent as instant frames (new Nano command D1) and paced by the estimated flip time. - Countdown to a date and time (smart, days, days + time, with seconds), stopwatch (web UI, HA switch and reset button), image element (fixed or by Home Assistant state), multiplier for HA values, degree sign (char 127) in all fonts and the font converter. - Page format version 2; version 1 pages are converted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
188 lines
4.0 KiB
C++
188 lines
4.0 KiB
C++
#include "logic/display_controller.h"
|
|
|
|
#include "config.h"
|
|
#include "logic/schedule.h"
|
|
|
|
DisplayController::DisplayController(DisplayPower &power, Frames &frames) : _power(power), _frames(frames)
|
|
{
|
|
}
|
|
|
|
void DisplayController::setSettings(const ScheduleSettings &schedule, const ClockSettings &clock)
|
|
{
|
|
_schedule = schedule;
|
|
_clock = clock;
|
|
_content_revision++; // e.g. the No WiFi setting changed: redraw
|
|
}
|
|
|
|
void DisplayController::setEffectTime(uint32_t ms)
|
|
{
|
|
_effect_time = ms;
|
|
}
|
|
|
|
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::setPowerEnabled(bool enabled)
|
|
{
|
|
_power_enabled = enabled;
|
|
}
|
|
|
|
bool DisplayController::powerEnabled() const
|
|
{
|
|
return _power_enabled;
|
|
}
|
|
|
|
void DisplayController::update(uint32_t now, bool time_valid, const LocalTime &time, bool wifi_connected)
|
|
{
|
|
if (wifi_connected)
|
|
_wifi_lost = false;
|
|
else if (!_wifi_lost)
|
|
{
|
|
_wifi_lost = true;
|
|
_wifi_lost_since = now;
|
|
}
|
|
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())
|
|
_screen = Screen::Unknown;
|
|
|
|
if (_off_timer.expired(now))
|
|
{
|
|
_power.off();
|
|
_screen = Screen::Unknown;
|
|
return;
|
|
}
|
|
|
|
if (!_power_enabled)
|
|
{
|
|
if (_power.isOn())
|
|
{
|
|
_showOnce(Screen::Off);
|
|
_off_timer.request(now, CLEAR_BEFORE_OFF_TIME + _effect_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;
|
|
|
|
switch (scheduleFor(time.hour, time.minute, _schedule))
|
|
{
|
|
case ScheduleMode::Night:
|
|
if (_power.isOn())
|
|
{
|
|
_showOnce(Screen::Off);
|
|
_off_timer.request(now, POWER_OFF_DELAY);
|
|
}
|
|
break;
|
|
case ScheduleMode::Clock:
|
|
_off_timer.cancel();
|
|
_ensureOn();
|
|
if (_page == Page::Dynamic)
|
|
_showDynamic(now, time, show_no_wifi);
|
|
else
|
|
_showOnce(Screen::Page);
|
|
break;
|
|
case ScheduleMode::Standby:
|
|
// 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;
|
|
}
|
|
|
|
// Drawn again each minute (time, date and background can change), when the content changed,
|
|
// and as often as a moving page asks for (scrolling, seconds)
|
|
void DisplayController::_showDynamic(uint32_t now, const LocalTime &time, bool no_wifi)
|
|
{
|
|
Screen screen = no_wifi ? Screen::NoWifi : Screen::Dynamic;
|
|
bool redraw = _screen != screen || _shown_revision != _content_revision;
|
|
bool due = screen == Screen::Dynamic && _dynamic_moving && (int32_t)(now - _dynamic_due) >= 0;
|
|
if (!redraw && !due && _shown_hour == time.hour && _shown_minute == time.minute)
|
|
return;
|
|
if (no_wifi)
|
|
{
|
|
_frames.noWifi(time.hour, time.minute);
|
|
_dynamic_moving = false;
|
|
}
|
|
else
|
|
{
|
|
uint32_t wait = _frames.dynamicPage(time, now, redraw);
|
|
_dynamic_moving = wait != 0;
|
|
_dynamic_due = now + wait;
|
|
}
|
|
_screen = screen;
|
|
_shown_hour = time.hour;
|
|
_shown_minute = time.minute;
|
|
_shown_revision = _content_revision;
|
|
}
|
|
|
|
// Screens without the time: only sent again when they or their content changed
|
|
void DisplayController::_showOnce(Screen screen)
|
|
{
|
|
if (_screen == screen && _shown_revision == _content_revision)
|
|
return;
|
|
switch (screen)
|
|
{
|
|
case Screen::Page:
|
|
_frames.page();
|
|
break;
|
|
case Screen::Live:
|
|
_frames.live();
|
|
break;
|
|
default:
|
|
_frames.off();
|
|
break;
|
|
}
|
|
_screen = screen;
|
|
_shown_revision = _content_revision;
|
|
}
|