Public Access
Weather forecast page, 16 elements per page, attribute paths, examples from the display
- Home Assistant attributes can be paths into lists (forecast.1.condition). - Date elements can show a day ahead (day offset 0-7). - Pages hold 16 elements (page format 6; the old formats are frozen at 8). Pages are reset in place (clearPage), not through a 4 KB temporary. - Verwachting page: today (icon, temperature, max/min) and the next 2 days with 7 x 7 icons (weer7 <condition>), from a template sensor (docs/home-assistant.md). - examples/home-pages.json is exported from the display with all pages and their icons, private entity id parts replaced by placeholders (tools/export_examples.py replaces tools/make_example_pages.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
839e86e8f1
commit
a6b946e17c
33 files changed
+1918
-1050
No files matched your search
@@ -26,7 +26,7 @@ const char *builtinPageName(const char *name)
|
||||
|
||||
static void clockPage(DynamicPage &page)
|
||||
{
|
||||
page = DynamicPage();
|
||||
clearPage(page);
|
||||
page.slot_count = 2;
|
||||
page.slots[0].start = 0;
|
||||
strcpy(page.slots[0].bitmap, "Pikachu");
|
||||
|
||||
@@ -127,7 +127,7 @@ static bool strncasecmpAscii(const char *a, const char *b, size_t n)
|
||||
static bool validAttribute(const char *attribute)
|
||||
{
|
||||
for (const char *c = attribute; *c; c++)
|
||||
if (!isalnum((unsigned char)*c) && *c != '_')
|
||||
if (!isalnum((unsigned char)*c) && *c != '_' && *c != '.') // dots: a path, e.g. forecast.1.condition
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -196,6 +196,8 @@ const char *validatePage(const DynamicPage &page)
|
||||
return "The list of texts can only contain plain characters, up to 63";
|
||||
if (e.format >= formatCount(e))
|
||||
return "Unknown format";
|
||||
if (e.day_offset < 0 || e.day_offset > DAY_OFFSET_MAX)
|
||||
return "Date: 0-7 days after today";
|
||||
if (!isfinite(e.factor) || e.factor == 0)
|
||||
return "The multiplier must be a number other than 0";
|
||||
if (e.scroll_width > 2 * BITMAP_WIDTH ||
|
||||
@@ -368,8 +370,37 @@ static int language(const PageElement &e)
|
||||
return e.language < Language::Count ? (int)e.language : 0;
|
||||
}
|
||||
|
||||
static void formatDate(const PageElement &e, const LocalTime &t, char *out, size_t size)
|
||||
static int daysInMonth(int year, int month)
|
||||
{
|
||||
static const int DAYS[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
bool leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
|
||||
return month == 2 && leap ? 29 : DAYS[(month - 1 + 12) % 12];
|
||||
}
|
||||
|
||||
LocalTime addDays(const LocalTime &time, int days)
|
||||
{
|
||||
LocalTime t = time;
|
||||
if (t.month < 1 || t.month > 12)
|
||||
return t;
|
||||
for (int i = 0; i < days; i++)
|
||||
{
|
||||
t.weekday = (t.weekday + 1) % 7;
|
||||
if (++t.day > daysInMonth(t.year, t.month))
|
||||
{
|
||||
t.day = 1;
|
||||
if (++t.month > 12)
|
||||
{
|
||||
t.month = 1;
|
||||
t.year++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static void formatDate(const PageElement &e, const LocalTime &today, char *out, size_t size)
|
||||
{
|
||||
LocalTime t = addDays(today, e.day_offset);
|
||||
int lang = language(e);
|
||||
int wd = t.weekday >= 0 && t.weekday < 7 ? t.weekday : 0;
|
||||
int mo = t.month >= 1 && t.month <= 12 ? t.month - 1 : 0;
|
||||
|
||||
+16
-1
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -16,7 +17,7 @@
|
||||
|
||||
const int PAGE_NAME_SIZE = BITMAP_NAME_MAX + 1;
|
||||
const int PAGE_SLOTS = 8;
|
||||
const int PAGE_ELEMENTS = 8;
|
||||
const int PAGE_ELEMENTS = 16;
|
||||
const int ELEMENT_TEXT_SIZE = 33;
|
||||
const int ELEMENT_SUFFIX_SIZE = 17;
|
||||
const int ELEMENT_ENTITY_SIZE = 65;
|
||||
@@ -78,6 +79,8 @@ const uint8_t DATE_NUMERIC_YEAR = 4; // 15-10-2026
|
||||
const uint8_t DATE_WEEKDAY = 5; // donderdag / Thursday
|
||||
const uint8_t DATE_WEEKDAY_SHORT = 6; // do / Thu
|
||||
const uint8_t DATE_FORMAT_COUNT = 7;
|
||||
// Date elements can show a later day (a forecast's weekday)
|
||||
const int8_t DAY_OFFSET_MAX = 7;
|
||||
|
||||
// Countdown formats
|
||||
const uint8_t COUNTDOWN_SMART = 0; // 12 dagen, 5 uur, 34 min, then 0:45
|
||||
@@ -171,6 +174,8 @@ struct PageElement
|
||||
uint8_t bar_height = 3;
|
||||
float bar_max = 100; // Bar: the value (after the multiplier) that fills it
|
||||
bool bar_frame = true; // Bar: a line around it, filled inside
|
||||
// Added in page format 6
|
||||
int8_t day_offset = 0; // Date: days after today (0-DAY_OFFSET_MAX), e.g. 1: tomorrow
|
||||
};
|
||||
|
||||
struct DynamicPage
|
||||
@@ -181,6 +186,13 @@ struct DynamicPage
|
||||
PageElement elements[PAGE_ELEMENTS];
|
||||
};
|
||||
|
||||
// Back to an empty page with the defaults, without `page = DynamicPage()`: that can put a
|
||||
// copy (about 4 KB) on the stack
|
||||
inline void clearPage(DynamicPage &page)
|
||||
{
|
||||
new (&page) DynamicPage();
|
||||
}
|
||||
|
||||
// Returns nullptr when the page is fine, otherwise a message for the user. Does not check
|
||||
// that the bitmaps and fonts exist.
|
||||
const char *validatePage(const DynamicPage &page);
|
||||
@@ -241,5 +253,8 @@ bool parseTimestamp(const char *text, int64_t &seconds);
|
||||
bool parseTarget(const char *text, uint32_t &minutes);
|
||||
// out must hold 17 characters
|
||||
void formatTarget(uint32_t minutes, char *out);
|
||||
// The date `days` (0 or more) after this one, with the weekday; the time stays
|
||||
LocalTime addDays(const LocalTime &time, int days);
|
||||
|
||||
// The local time in minutes since 2000-01-01 00:00 (seconds left out)
|
||||
uint32_t minutesSince2000(const LocalTime &time);
|
||||
Reference in new issue
Block a user