Add scrolling, countdown, stopwatch, images, seconds, multiplier and degree sign

- 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>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-15 21:01:14 +02:00
1 parent ca8d78ea8a
commit fc6fa1a180
38 files changed
+1370 -277

No files matched your search

+3 -1
View File
@@ -70,7 +70,8 @@ void haApiWatch(const DynamicPage *page)
for (int i = 0; i < page->element_count; i++)
{
const PageElement &element = page->elements[i];
if (element.type != ElementType::HaValue || !validEntityId(element.entity))
bool uses_state = element.type == ElementType::HaValue || element.type == ElementType::Image;
if (!uses_state || !validEntityId(element.entity))
continue;
Entry &e = add(element.entity, element.attribute);
if (!e.watched)
@@ -127,6 +128,7 @@ static const char *fetch(const String &entity, const String &attribute, String &
value = "";
serializeJson(v, value); // numbers, true/false
}
value.replace("\xC2\xB0", String(DEGREE_SIGN)); // UTF-8 degree sign: the fonts have it as 127
return nullptr;
}
+17
View File
@@ -12,6 +12,7 @@
#include "config.h"
#include "credentials.h"
#include "display/bitmap_store.h"
#include "display/page_resources.h"
#include "display/page_store.h"
#include "net/ha_page_select.h"
#include "system/boot_target.h"
@@ -27,6 +28,8 @@ static HAPageSelect offSelect("FlipDotDisplayOffImage");
static HASensor firmwareSensor("FlipDotDisplayFirmware");
static HASensor ipSensor("FlipDotDisplayIP");
static HAButton recoveryButton("FlipDotDisplayRecovery");
static HASwitch stopwatchSwitch("FlipDotDisplayStopwatch");
static HAButton stopwatchReset("FlipDotDisplayStopwatchReset");
// Removed entities: their retained discovery config is cleared, so Home Assistant
// deletes them instead of showing them as unavailable
@@ -215,6 +218,17 @@ void homeAssistantBegin(DisplayController &controller, const HomeAssistantSettin
offSelect.onCommand([](const char *option) { offImage.command(option); });
offImage.update();
stopwatchSwitch.setName("FlipDot Display Stopwatch");
stopwatchSwitch.setIcon("mdi:timer-outline");
stopwatchSwitch.setCurrentState(stopwatch.running());
stopwatchSwitch.onCommand([](bool state, HASwitch *sender) {
appStopwatch(state ? StopwatchAction::Start : StopwatchAction::Stop);
sender->setState(stopwatch.running());
});
stopwatchReset.setName("FlipDot Display Stopwatch reset");
stopwatchReset.setIcon("mdi:timer-refresh-outline");
stopwatchReset.onCommand([](HAButton *) { appStopwatch(StopwatchAction::Reset); });
recoveryButton.setName("FlipDot Display Recovery");
recoveryButton.setIcon("mdi:lifebuoy");
recoveryButton.onCommand([](HAButton *) { rebootToRecovery(RecoveryReason::Manual); });
@@ -260,6 +274,9 @@ void homeAssistantLoop()
firmware.loop();
mode.update();
offImage.update();
// Started or stopped in the web UI
if (stopwatchSwitch.getCurrentState() != stopwatch.running())
stopwatchSwitch.setState(stopwatch.running());
}
void homeAssistantSetFirmwareStatus(const char *status)
+2
View File
@@ -12,6 +12,8 @@
// FlipDot Display IP sensor: the IP address
// FlipDot Display Firmware sensor: version and health, e.g. "2.1.0, ok, 0 crashes, ..."
// FlipDot Display Recovery button: restart into the recovery app (for 10 minutes)
// FlipDot Display Stopwatch switch: the stopwatch of the dynamic pages runs
// FlipDot Display Stopwatch reset button: back to 0
//
// The broker comes from the settings (web UI); an empty host means BROKER_ADDR, MQTT_USER
// and MQTT_PASSWORD from secrets.h. Changes need a restart.