3e959b3df9
Everything a user sees or reads is now English: both boards' labels, status replies and logs, the ESP Screens editor and Alerts cheatsheet, and the README and guides (docs/TILES.md, CALIBRATING.md, ACCEPTANCE.md and TROUBLESHOOTING.md replace the Dutch file names). Labels follow Home Assistant's own English names (Heat/Cool modes, Heating/Cooling actions, Away, Returning to dock, weather conditions), dates use English abbreviations and the top bar writes numbers the English way. Docs use HA 2026.9's menu names (Settings -> Apps, App store, Settings -> Tools). Unused legacy files (home-like.yaml, buttons.yaml, a.out) are removed. Firmware 0.2.34 names its entities in English. Home Assistant registers a renamed ESPHome entity under a new entity ID, so the inbox text.<screen>_tegelinstellingen becomes text.<screen>_tile_settings and the layout stored under the old ID would be lost. The manager now recognises the screen by its device, during a run or after a restart, and moves its layout and update history along; the Update button and the nightly round follow the screen through the rename. Screens that still run 0.2.33 or older keep working: both languages are matched. Main is the only release branch from now on. Tested on a Home Assistant Yellow (HA 2026.9.1) with both bench boards: see docs/TEST_RESULTS_0240.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
74 lines
3.1 KiB
C++
74 lines
3.1 KiB
C++
#pragma once
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include "tile_icon.h"
|
|
#include "tile_icon_names.h"
|
|
#include "tile_palette.h"
|
|
|
|
// The card a Home Assistant automation puts over the whole screen (api action
|
|
// show_alert). Pure text and lookups so both boards apply the same rules and
|
|
// tests/test_alert_overlay.cpp can check them; the profiles do the LVGL work.
|
|
namespace screen_alert {
|
|
constexpr const char *FALLBACK_ICON = "alert-outline";
|
|
constexpr const char *FALLBACK_TITLE = "Notification";
|
|
constexpr const char *FALLBACK_BUTTON = "OK";
|
|
constexpr uint32_t DEFAULT_CARD_COLOR = 0xFFFFFF;
|
|
constexpr int MAX_TIMEOUT_SECONDS = 86400;
|
|
|
|
struct Alert {
|
|
std::string title, subtitle, icon, button; // icon: UTF-8 glyph for the icon font
|
|
uint32_t color = DEFAULT_CARD_COLOR;
|
|
int timeout_seconds = 0; // 0: stays until OK
|
|
bool flash = false;
|
|
};
|
|
|
|
inline bool blank(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; }
|
|
inline std::string trimmed(const std::string &text) {
|
|
size_t begin = 0, end = text.size();
|
|
while (begin < end && blank(text[begin])) ++begin;
|
|
while (end > begin && blank(text[end - 1])) --end;
|
|
return text.substr(begin, end - begin);
|
|
}
|
|
inline std::string lowercase(std::string text) {
|
|
for (auto &c : text) if (c >= 'A' && c <= 'Z') c = static_cast<char>(c - 'A' + 'a');
|
|
return text;
|
|
}
|
|
// Cuts on a UTF-8 boundary: LVGL must never get half a code point.
|
|
inline std::string clipped(const std::string &text, size_t max_bytes) {
|
|
if (text.size() <= max_bytes) return text;
|
|
size_t end = max_bytes;
|
|
while (end > 0 && (static_cast<unsigned char>(text[end]) & 0xC0) == 0x80) --end;
|
|
return text.substr(0, end);
|
|
}
|
|
// "doorbell", "mdi:doorbell" or the hex codepoint "F12E6" of a glyph the fonts carry;
|
|
// anything else draws the warning triangle.
|
|
inline uint32_t icon_codepoint(const std::string &value) {
|
|
std::string name = lowercase(trimmed(value));
|
|
if (name.rfind("mdi:", 0) == 0) name.erase(0, 4);
|
|
if (uint32_t code = tile_icon::named(name)) return code;
|
|
const uint32_t code = tile_icon::codepoint(name);
|
|
if (code && tile_icon::carried(code)) return code;
|
|
return tile_icon::named(FALLBACK_ICON);
|
|
}
|
|
inline int clamp_timeout(int seconds) {
|
|
return seconds < 0 ? 0 : seconds > MAX_TIMEOUT_SECONDS ? MAX_TIMEOUT_SECONDS : seconds;
|
|
}
|
|
inline Alert make(const std::string &title, const std::string &subtitle, const std::string &icon,
|
|
const std::string &color, const std::string &button, int timeout, bool flash,
|
|
size_t title_max, size_t subtitle_max, size_t button_max) {
|
|
Alert alert;
|
|
alert.title = clipped(trimmed(title), title_max);
|
|
if (alert.title.empty()) alert.title = FALLBACK_TITLE;
|
|
alert.subtitle = clipped(trimmed(subtitle), subtitle_max);
|
|
alert.button = clipped(trimmed(button), button_max);
|
|
if (alert.button.empty()) alert.button = FALLBACK_BUTTON;
|
|
alert.icon = tile_icon::utf8(icon_codepoint(icon));
|
|
const uint32_t card = tile_palette::color(lowercase(trimmed(color)));
|
|
alert.color = card ? card : DEFAULT_CARD_COLOR;
|
|
alert.timeout_seconds = clamp_timeout(timeout);
|
|
alert.flash = flash;
|
|
return alert;
|
|
}
|
|
} // namespace screen_alert
|