2026-10-13 01:52:34 +02:00
|
|
|
#include "app.h"
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "display/bitmap_store.h"
|
|
|
|
|
#include "system/settings_store.h"
|
|
|
|
|
|
|
|
|
|
NanoLink nano(Serial2, NANO_RX_PIN, NANO_TX_PIN, RELAY_PIN, NANO_RESET_PIN);
|
|
|
|
|
BROSE9323 display(DISPLAY_WIDTH, DISPLAY_HEIGHT, PANEL_WIDTH);
|
|
|
|
|
FlipdotFrames frames(display);
|
|
|
|
|
StatusScreen status(display, nano);
|
|
|
|
|
DisplayController controller(nano, frames);
|
|
|
|
|
|
|
|
|
|
static Settings settings;
|
|
|
|
|
static bool restart_pending = false;
|
|
|
|
|
static uint32_t restart_at = 0;
|
|
|
|
|
|
|
|
|
|
// Load the page bitmap into the frames; falls back to the clock when it is gone
|
|
|
|
|
static void applyPage()
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(settings.page, PAGE_CLOCK) != 0)
|
|
|
|
|
{
|
|
|
|
|
uint8_t bitmap[BITMAP_BYTES];
|
|
|
|
|
if (loadBitmap(settings.page, bitmap))
|
|
|
|
|
{
|
|
|
|
|
frames.setPageBitmap(bitmap);
|
|
|
|
|
controller.setPage(Page::Bitmap);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Serial.printf("Page bitmap \"%s\" not found, showing the clock\n", settings.page);
|
|
|
|
|
}
|
|
|
|
|
controller.setPage(Page::Clock);
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-15 01:29:01 +02:00
|
|
|
// Load the off bitmap into the frames; blank when it is gone
|
|
|
|
|
static void applyOffPage()
|
|
|
|
|
{
|
|
|
|
|
uint8_t bitmap[BITMAP_BYTES];
|
|
|
|
|
if (settings.off_page[0] && loadBitmap(settings.off_page, bitmap))
|
|
|
|
|
frames.setOffBitmap(bitmap);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (settings.off_page[0])
|
|
|
|
|
Serial.printf("Off bitmap \"%s\" not found, the display is cleared instead\n", settings.off_page);
|
|
|
|
|
frames.setOffBitmap(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-13 01:52:34 +02:00
|
|
|
static void apply()
|
|
|
|
|
{
|
|
|
|
|
controller.setSettings(settings.schedule, settings.clock);
|
2026-10-15 01:29:01 +02:00
|
|
|
controller.setEffectTime(effectTime(settings.drive));
|
|
|
|
|
status.setDrive(settings.flip_time, settings.drive);
|
|
|
|
|
applyOffPage();
|
2026-10-13 01:52:34 +02:00
|
|
|
applyPage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Settings &appSettings()
|
|
|
|
|
{
|
|
|
|
|
return settings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appLoadSettings()
|
|
|
|
|
{
|
|
|
|
|
loadSettings(settings);
|
|
|
|
|
apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool sameHomeAssistant(const HomeAssistantSettings &a, const HomeAssistantSettings &b)
|
|
|
|
|
{
|
|
|
|
|
return a.enabled == b.enabled && a.port == b.port && strcmp(a.host, b.host) == 0 &&
|
|
|
|
|
strcmp(a.user, b.user) == 0 && strcmp(a.password, b.password) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *appSaveSettings(const Settings &new_settings, bool &restart_needed)
|
|
|
|
|
{
|
|
|
|
|
const char *error = validateSettings(new_settings);
|
|
|
|
|
if (error)
|
|
|
|
|
return error;
|
|
|
|
|
if (strcmp(new_settings.page, PAGE_CLOCK) != 0 && !bitmapExists(new_settings.page))
|
|
|
|
|
return "Bitmap not found";
|
2026-10-15 01:29:01 +02:00
|
|
|
if (new_settings.off_page[0] && !bitmapExists(new_settings.off_page))
|
|
|
|
|
return "Off bitmap not found";
|
2026-10-13 01:52:34 +02:00
|
|
|
|
|
|
|
|
restart_needed = !sameHomeAssistant(settings.home_assistant, new_settings.home_assistant);
|
2026-10-15 01:29:01 +02:00
|
|
|
const DriveSettings &a = settings.drive, &b = new_settings.drive;
|
|
|
|
|
bool drive_changed = settings.flip_time != new_settings.flip_time || a.flip_gap != b.flip_gap ||
|
|
|
|
|
a.pulses != b.pulses || a.effect != b.effect || a.effect_time != b.effect_time ||
|
|
|
|
|
a.effect_small_only != b.effect_small_only;
|
2026-10-13 01:52:34 +02:00
|
|
|
settings = new_settings;
|
|
|
|
|
saveSettings(settings);
|
|
|
|
|
apply();
|
2026-10-15 01:29:01 +02:00
|
|
|
if (drive_changed)
|
|
|
|
|
status.sendDrive();
|
2026-10-13 01:52:34 +02:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *appSelectPage(const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(name, PAGE_CLOCK) != 0 && !bitmapExists(name))
|
|
|
|
|
return "Bitmap not found";
|
|
|
|
|
strncpy(settings.page, name, sizeof(settings.page) - 1);
|
|
|
|
|
settings.page[sizeof(settings.page) - 1] = '\0';
|
|
|
|
|
saveSettings(settings);
|
|
|
|
|
applyPage();
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-15 01:29:01 +02:00
|
|
|
const char *appSelectOffPage(const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (name[0] && !bitmapExists(name))
|
|
|
|
|
return "Bitmap not found";
|
|
|
|
|
strncpy(settings.off_page, name, sizeof(settings.off_page) - 1);
|
|
|
|
|
settings.off_page[sizeof(settings.off_page) - 1] = '\0';
|
|
|
|
|
saveSettings(settings);
|
|
|
|
|
applyOffPage();
|
|
|
|
|
controller.pageChanged();
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-13 01:52:34 +02:00
|
|
|
void appBitmapChanged(const char *name)
|
|
|
|
|
{
|
2026-10-15 01:29:01 +02:00
|
|
|
if (strcmp(settings.off_page, name) == 0)
|
|
|
|
|
{
|
|
|
|
|
applyOffPage();
|
|
|
|
|
controller.pageChanged();
|
|
|
|
|
}
|
2026-10-13 01:52:34 +02:00
|
|
|
if (strcmp(settings.page, name) == 0)
|
|
|
|
|
{
|
|
|
|
|
applyPage();
|
|
|
|
|
controller.pageChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appShowLive(const uint8_t *bitmap)
|
|
|
|
|
{
|
|
|
|
|
frames.setLiveBitmap(bitmap);
|
|
|
|
|
controller.showLive(millis());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appStopLive()
|
|
|
|
|
{
|
|
|
|
|
controller.stopLive();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appRestartLater(uint32_t delay_ms)
|
|
|
|
|
{
|
|
|
|
|
restart_pending = true;
|
|
|
|
|
restart_at = millis() + delay_ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void appLoop()
|
|
|
|
|
{
|
|
|
|
|
if (restart_pending && (int32_t)(millis() - restart_at) >= 0)
|
|
|
|
|
{
|
|
|
|
|
Serial.println("Restarting for the new settings");
|
|
|
|
|
ESP.restart();
|
|
|
|
|
}
|
|
|
|
|
}
|