2026-10-13 01:52:34 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
2026-10-15 19:43:59 +02:00
|
|
|
#include "logic/builtin_pages.h"
|
|
|
|
|
#include "logic/page.h"
|
|
|
|
|
|
2026-10-13 01:52:34 +02:00
|
|
|
// Everything that can be changed in the web UI. Stored in NVS (system/settings_store),
|
|
|
|
|
// the defaults are the original behaviour. Pure logic, tested in test/test_esp32.
|
|
|
|
|
//
|
|
|
|
|
// Times are minutes since midnight (8:00 = 480).
|
|
|
|
|
|
|
|
|
|
struct ScheduleSettings
|
|
|
|
|
{
|
|
|
|
|
bool enabled = true; // false: the display is always on
|
|
|
|
|
uint16_t day_start = 8 * 60; // display on from
|
|
|
|
|
uint16_t late_start = 22 * 60; // from here only at the quarter hours (see quarter_hours)
|
|
|
|
|
bool quarter_hours = true; // false: on all the time until night_start
|
|
|
|
|
uint16_t night_start = 23 * 60 + 31; // display off from (up to 24:00 = never)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ClockSettings
|
|
|
|
|
{
|
2026-10-15 19:43:59 +02:00
|
|
|
bool no_wifi_screen = true; // "No WiFi" instead of a dynamic page when WiFi is down
|
2026-10-13 01:52:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct HomeAssistantSettings
|
|
|
|
|
{
|
|
|
|
|
bool enabled = true;
|
|
|
|
|
char host[64] = ""; // empty: BROKER_ADDR from secrets.h
|
|
|
|
|
uint16_t port = 1883;
|
|
|
|
|
char user[32] = "";
|
|
|
|
|
char password[64] = "";
|
2026-10-15 19:43:59 +02:00
|
|
|
// Values for the dynamic pages, read over Home Assistant's REST API (net/ha_api)
|
|
|
|
|
char api_url[96] = ""; // empty: http://<MQTT broker>:8123
|
|
|
|
|
char token[256] = ""; // long-lived access token (HA user profile, Security)
|
2026-10-13 01:52:34 +02:00
|
|
|
};
|
|
|
|
|
|
2026-10-15 01:29:01 +02:00
|
|
|
// How the Nano drives the dots (besides the flip time). Sent to the Nano, which keeps
|
|
|
|
|
// them in its EEPROM. The defaults match the Nano's (src/nano/config.h).
|
|
|
|
|
const uint8_t DRIVE_EFFECT_INSTANT = 0; // numbers: RefreshEffect in lib/BROSE9323/dot_sequence.h
|
|
|
|
|
const uint8_t DRIVE_EFFECT_PUSH = 8;
|
|
|
|
|
const uint8_t DRIVE_EFFECT_RANDOM = 9;
|
|
|
|
|
const uint8_t DRIVE_EFFECT_COUNT = 10;
|
|
|
|
|
// Pushing flips many dots per column step, so it can take much longer than effect_time
|
|
|
|
|
const uint32_t DRIVE_PUSH_EXTRA_TIME = 20000;
|
|
|
|
|
|
|
|
|
|
struct DriveSettings
|
|
|
|
|
{
|
|
|
|
|
uint16_t flip_gap = 200; // pause after each pulse in us (0-5000)
|
|
|
|
|
uint8_t pulses = 2; // pulses for each changed dot (1-3)
|
|
|
|
|
uint8_t effect = DRIVE_EFFECT_INSTANT;
|
|
|
|
|
uint16_t effect_time = 1500; // ms (100-10000)
|
|
|
|
|
bool effect_small_only = false; // big changes (a new page) appear instantly
|
|
|
|
|
};
|
|
|
|
|
|
2026-10-16 15:20:26 +02:00
|
|
|
// Full redraws that fix the picture, in minutes (0 = never)
|
|
|
|
|
const uint16_t REFRESH_MINUTES_MAX = 1440;
|
|
|
|
|
struct RefreshSettings
|
|
|
|
|
{
|
|
|
|
|
// The Nano flips every dot once more (fixes dots that didn't flip). Sent to the Nano,
|
|
|
|
|
// which keeps it in its EEPROM (I command, default there: 2).
|
|
|
|
|
uint16_t dots = 2;
|
|
|
|
|
// The ESP32 sends the whole image again (fixes a wrong image on the Nano, e.g. after
|
|
|
|
|
// lost bytes). Only the dots that differ flip, so a correct image doesn't change.
|
|
|
|
|
uint16_t image = 10;
|
|
|
|
|
};
|
|
|
|
|
|
2026-10-15 19:43:59 +02:00
|
|
|
// What is shown during the day: the name of a dynamic page (logic/page.h) or a bitmap.
|
|
|
|
|
// Both share one namespace. When it is gone, the built-in Clock page is shown.
|
|
|
|
|
#define DEFAULT_PAGE BUILTIN_PAGE_CLOCK
|
2026-10-15 01:29:01 +02:00
|
|
|
// What is left on the dots when the display switches off: empty = blank, or a bitmap name
|
|
|
|
|
#define OFF_PAGE_BLANK ""
|
2026-10-13 01:52:34 +02:00
|
|
|
|
|
|
|
|
struct Settings
|
|
|
|
|
{
|
2026-10-15 19:43:59 +02:00
|
|
|
char page[PAGE_NAME_SIZE] = DEFAULT_PAGE;
|
2026-10-13 01:52:34 +02:00
|
|
|
ScheduleSettings schedule;
|
|
|
|
|
ClockSettings clock;
|
|
|
|
|
HomeAssistantSettings home_assistant;
|
|
|
|
|
uint16_t flip_time = 225; // flip pulse length in us, sent to the Nano
|
|
|
|
|
bool status_led = true;
|
2026-10-15 01:29:01 +02:00
|
|
|
char off_page[PAGE_NAME_SIZE] = OFF_PAGE_BLANK;
|
|
|
|
|
DriveSettings drive;
|
2026-10-16 15:20:26 +02:00
|
|
|
RefreshSettings refresh;
|
2026-10-16 16:17:23 +02:00
|
|
|
// Plex user name for {plex_user} in entity ids (Tautulli's sensors of that user)
|
|
|
|
|
char plex_user[PLEX_USER_SIZE] = "";
|
2026-10-15 19:43:59 +02:00
|
|
|
// New fields go here, at the end (see system/settings_store.cpp)
|
2026-10-13 01:52:34 +02:00
|
|
|
};
|
|
|
|
|
|
2026-10-15 01:29:01 +02:00
|
|
|
// How long a new frame can take to appear on the dots in ms (0 when instant)
|
|
|
|
|
uint32_t effectTime(const DriveSettings &drive);
|
|
|
|
|
|
2026-10-13 01:52:34 +02:00
|
|
|
const uint16_t MINUTES_PER_DAY = 24 * 60;
|
|
|
|
|
const uint16_t SETTINGS_FLIP_TIME_MIN = 50;
|
|
|
|
|
const uint16_t SETTINGS_FLIP_TIME_MAX = 2000;
|
|
|
|
|
|
|
|
|
|
// Checks the values. Returns nullptr when they are fine, otherwise a message for the user.
|
|
|
|
|
const char *validateSettings(const Settings &settings);
|
|
|
|
|
|
|
|
|
|
// "HH:MM" (00:00 - 24:00) <-> minutes since midnight
|
|
|
|
|
bool parseTime(const char *text, uint16_t &minutes);
|
|
|
|
|
// out must hold 6 characters
|
|
|
|
|
void formatTime(uint16_t minutes, char *out);
|