Files
planar-EL512.256-H/components/el512/el512_helpers.h
T
mangavalkandClaude Opus 5.5 963590cdfe Add ESPHome display component; ring-buffer driver shared with PlatformIO
Driver (components/el512/el512_panel.*, ESP-IDF only):
- DMA ring of 8 x 4 lines (17 KB) refilled from the I2S EOF interrupt
  (IRAM) instead of a 142 KB full-frame stream; ~107 KB heap freed.
- 256-entry lookup table encodes 8 pixels per step.
- Back buffer + displayed buffer, swapped at the start of a frame.
- PlatformIO builds it via lib_extra_dirs (EL512Core); lib/EL512 keeps
  the Adafruit GFX front end.

ESPHome external component (platform: el512):
- DisplayBuffer with optional ENABLE / _LOWPWR pins, set_enabled(),
  set_low_power(), helper functions for page lambdas.
- still_alive_page: plays the Portal end-credits show, then returns to
  the previous page.

Still Alive engine moved to components/el512/still_alive.* with a built-in
5x7 font (from Adafruit GFX, BSD); the PlatformIO demo uses the same code.
The importer writes components/el512/still_alive_data.h and stops at 200 s
(--until-ms), matching the hand-trimmed data.

esphome/el512-panel.yaml: 8 pages (clock, forecast, agenda, dashboard,
now playing, Plex, solar, Still Alive), page select, rotation, display
and low-power switches, message text. home_assistant/packages/el512_panel.yaml:
helper template sensors for forecast, agenda, now playing and Plex streams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 20:20:13 +02:00

109 lines
3.4 KiB
C++

// Small helpers for page lambdas (available as el512::... in YAML lambdas).
#pragma once
#include <cstdio>
#include <string>
#include <vector>
#include "esphome/components/display/display.h"
namespace esphome::el512 {
// Split the "a|b|c;d|e|f" lists made by the Home Assistant package.
inline std::vector<std::string> split(const std::string &s, char sep) {
std::vector<std::string> out;
if (s.empty())
return out;
size_t start = 0;
while (true) {
const size_t end = s.find(sep, start);
out.push_back(s.substr(start, end == std::string::npos ? std::string::npos : end - start));
if (end == std::string::npos)
return out;
start = end + 1;
}
}
// Field i of a "|"-separated entry, or "" if missing.
inline std::string field(const std::string &entry, size_t i) {
const auto parts = split(entry, '|');
return i < parts.size() ? parts[i] : std::string();
}
// Material Design Icons glyph (UTF-8) for a Home Assistant weather condition.
inline const char *weather_icon(const std::string &c) {
if (c == "clear-night") return "\U000F0594";
if (c == "cloudy") return "\U000F0590";
if (c == "fog") return "\U000F0591";
if (c == "hail") return "\U000F0592";
if (c == "lightning") return "\U000F0593";
if (c == "lightning-rainy") return "\U000F067E";
if (c == "partlycloudy") return "\U000F0595";
if (c == "pouring") return "\U000F0596";
if (c == "rainy") return "\U000F0597";
if (c == "snowy") return "\U000F0598";
if (c == "snowy-rainy") return "\U000F067F";
if (c == "sunny") return "\U000F0599";
if (c == "windy") return "\U000F059D";
if (c == "windy-variant") return "\U000F059E";
return "\U000F05D6"; // exceptional / unknown
}
// Readable weather condition ("partlycloudy" -> "Partly cloudy").
inline std::string weather_text(const std::string &c) {
if (c == "clear-night") return "Clear";
if (c == "partlycloudy") return "Partly cloudy";
if (c == "lightning-rainy") return "Thunderstorm";
if (c == "snowy-rainy") return "Sleet";
if (c == "windy-variant") return "Windy";
if (c == "exceptional") return "Exceptional";
if (c.empty()) return "";
std::string s = c;
s[0] = (char) toupper(s[0]);
return s;
}
// Cut text with "..." so it fits in max_width pixels in the given font.
inline std::string fit(display::BaseFont *font, const std::string &text, int max_width) {
int w, x_off, baseline, h;
font->measure(text.c_str(), &w, &x_off, &baseline, &h);
if (w <= max_width)
return text;
std::string s = text;
while (!s.empty()) {
// Drop a whole UTF-8 character.
do {
s.pop_back();
} while (!s.empty() && (s.back() & 0xC0) == 0x80);
const std::string t = s + "...";
font->measure(t.c_str(), &w, &x_off, &baseline, &h);
if (w <= max_width)
return t;
}
return "...";
}
// 245 -> "4:05", 3725 -> "1:02:05".
inline std::string mmss(int seconds) {
if (seconds < 0)
seconds = 0;
char buf[16];
if (seconds >= 3600)
snprintf(buf, sizeof(buf), "%d:%02d:%02d", seconds / 3600, seconds / 60 % 60, seconds % 60);
else
snprintf(buf, sizeof(buf), "%d:%02d", seconds / 60, seconds % 60);
return buf;
}
// 1250 -> "1250 W", 4200 -> "4.20 kW".
inline std::string watts(float w) {
char buf[16];
if (w >= 1000 || w <= -1000)
snprintf(buf, sizeof(buf), "%.2f kW", w / 1000.0f);
else
snprintf(buf, sizeof(buf), "%.0f W", w);
return buf;
}
} // namespace esphome::el512