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:
mangavalkandClaude Opus 5.5 committed 2026-10-16 22:36:29 +02:00
1 parent 839e86e8f1
commit a6b946e17c
33 files changed
+1918 -1050

No files matched your search

+51 -3
View File
@@ -57,7 +57,7 @@ static String token;
static char plex_user[PLEX_USER_SIZE] = "";
static std::vector<Entry> entries;
// Kept for the editor's previews, besides the watched ones
static const size_t MAX_ENTRIES = 24;
static const size_t MAX_ENTRIES = 32;
static QueueHandle_t requests = nullptr;
static QueueHandle_t responses = nullptr;
@@ -74,6 +74,46 @@ static uint32_t preview_done_at = 0;
// The task
// ---------------------------------------------------------------------------
// Attributes can be a path into lists and objects: "forecast.1.condition" is the condition
// of the second forecast. One part of it (up to the next dot) goes into `part`; returns the
// rest, nullptr after the last part.
static const char *nextPart(const char *path, char *part, size_t size)
{
size_t n = strcspn(path, ".");
strlcpy(part, path, n + 1 < size ? n + 1 : size);
return path[n] == '.' ? path + n + 1 : nullptr;
}
static bool isIndex(const char *part)
{
if (!part[0])
return false;
for (const char *c = part; *c; c++)
if (!isdigit((unsigned char)*c))
return false;
return true;
}
// Only the path is kept from the answer. List indexes become [0]: ArduinoJson applies the
// first element of a filter list to all elements.
static void addFilter(JsonVariant f, const char *path)
{
char part[ELEMENT_ATTRIBUTE_SIZE];
const char *rest = nextPart(path, part, sizeof(part));
if (isIndex(part))
{
JsonVariant element = f.to<JsonArray>().add<JsonVariant>();
if (rest)
addFilter(element, rest);
else
element.set(true);
}
else if (rest)
addFilter(f[(const char *)part].to<JsonVariant>(), rest); // char arrays would be taken as literals
else
f[(const char *)part] = true;
}
// Blocks at most about 2 x HA_HTTP_TIMEOUT (connect, then the answer)
static const char *fetch(const Request &r, char *value, size_t value_size)
{
@@ -99,7 +139,7 @@ static const char *fetch(const Request &r, char *value, size_t value_size)
JsonDocument filter;
if (r.attribute[0])
filter["attributes"][(const char *)r.attribute] = true; // a char array would be taken as a literal of 32 characters
addFilter(filter["attributes"].to<JsonVariant>(), r.attribute);
else
filter["state"] = true;
JsonDocument doc;
@@ -110,7 +150,15 @@ static const char *fetch(const Request &r, char *value, size_t value_size)
JsonVariantConst v = doc["state"];
if (r.attribute[0])
v = doc["attributes"][(const char *)r.attribute];
{
v = doc["attributes"];
char part[ELEMENT_ATTRIBUTE_SIZE];
for (const char *path = r.attribute; path && !v.isNull();)
{
path = nextPart(path, part, sizeof(part));
v = isIndex(part) ? v[(size_t)atoi(part)] : v[(const char *)part];
}
}
if (v.isNull())
return "Unknown attribute";
String text;