Public Access
- net/ha_api: the HTTP requests run in a FreeRTOS task on core 0; loop()
only hands over copies through queues. The web UI's test button polls.
No more 3-4 s loop() stalls once a minute; timeout raised to 5 s.
- Progress bar page element (page format 4, v3 pages are converted)
- Plex user setting (settings v6): {plex_user} in entity ids
- Plex page (examples): watchers, play/pause icon, title, progress
- Bitmaps cached in RAM: a LittleFS read took ~100 ms on every render
and web preview; long JSON lists sent in 2 KB pieces
- Backup includes the refresh settings and the Plex user
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
2.8 KiB
C++
81 lines
2.8 KiB
C++
#include "logic/settings.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "logic/bitmap_format.h"
|
|
|
|
static bool terminated(const char *text, int size)
|
|
{
|
|
return memchr(text, '\0', size) != nullptr;
|
|
}
|
|
|
|
const char *validateSettings(const Settings &s)
|
|
{
|
|
if (!terminated(s.page, sizeof(s.page)) || !validBitmapName(s.page))
|
|
return "Unknown page";
|
|
if (!terminated(s.off_page, sizeof(s.off_page)) || (s.off_page[0] && !validBitmapName(s.off_page)))
|
|
return "Unknown off image";
|
|
|
|
const ScheduleSettings &t = s.schedule;
|
|
if (t.day_start >= MINUTES_PER_DAY || t.late_start > MINUTES_PER_DAY || t.night_start > MINUTES_PER_DAY)
|
|
return "Times must be between 00:00 and 24:00";
|
|
if (t.day_start > t.late_start || t.late_start > t.night_start)
|
|
return "The times must be in order: day start, late evening, night";
|
|
|
|
if (s.flip_time < SETTINGS_FLIP_TIME_MIN || s.flip_time > SETTINGS_FLIP_TIME_MAX)
|
|
return "The flip time must be between 50 and 2000 us";
|
|
|
|
const DriveSettings &d = s.drive;
|
|
if (d.flip_gap > 5000)
|
|
return "The flip gap must be between 0 and 5000 us";
|
|
if (d.pulses < 1 || d.pulses > 3)
|
|
return "Pulses must be 1, 2 or 3";
|
|
if (d.effect >= DRIVE_EFFECT_COUNT)
|
|
return "Unknown refresh effect";
|
|
if (d.effect_time < 100 || d.effect_time > 10000)
|
|
return "The effect time must be between 100 and 10000 ms";
|
|
if (s.refresh.dots > REFRESH_MINUTES_MAX || s.refresh.image > REFRESH_MINUTES_MAX)
|
|
return "Refresh intervals must be between 0 and 1440 minutes";
|
|
|
|
const HomeAssistantSettings &ha = s.home_assistant;
|
|
if (!terminated(ha.host, sizeof(ha.host)) || !terminated(ha.user, sizeof(ha.user)) ||
|
|
!terminated(ha.password, sizeof(ha.password)) || !terminated(ha.api_url, sizeof(ha.api_url)) ||
|
|
!terminated(ha.token, sizeof(ha.token)) || !terminated(s.plex_user, sizeof(s.plex_user)))
|
|
return "Home Assistant text too long";
|
|
if (ha.port == 0)
|
|
return "Invalid MQTT port";
|
|
if (ha.api_url[0] && strncmp(ha.api_url, "http://", 7) != 0)
|
|
return "The Home Assistant URL must start with http:// (https is not supported)";
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
uint32_t effectTime(const DriveSettings &drive)
|
|
{
|
|
if (drive.effect == DRIVE_EFFECT_INSTANT)
|
|
return 0;
|
|
if (drive.effect == DRIVE_EFFECT_PUSH || drive.effect == DRIVE_EFFECT_RANDOM)
|
|
return drive.effect_time + DRIVE_PUSH_EXTRA_TIME;
|
|
return drive.effect_time;
|
|
}
|
|
|
|
bool parseTime(const char *text, uint16_t &minutes)
|
|
{
|
|
if (text == nullptr)
|
|
return false;
|
|
int hour = 0, minute = 0;
|
|
char extra;
|
|
if (sscanf(text, "%d:%d%c", &hour, &minute, &extra) != 2)
|
|
return false;
|
|
if (hour < 0 || minute < 0 || minute > 59 || hour * 60 + minute > MINUTES_PER_DAY)
|
|
return false;
|
|
minutes = hour * 60 + minute;
|
|
return true;
|
|
}
|
|
|
|
void formatTime(uint16_t minutes, char *out)
|
|
{
|
|
snprintf(out, 6, "%02u:%02u", (unsigned)(minutes / 60) % 100, (unsigned)(minutes % 60));
|
|
}
|