Na klar, das geht schon.
Hier mal ein Bild von meinem Display
und hier nun der YAML Code dazu einige stellen sind einige Passagen sind mit Platzhaltern versehen die musst du dann noch für deine Sachen bearbeiten.
esphome:
name: e-ink-display
friendly_name: E-Ink Display
on_boot:
- priority: 600
then:
- output.turn_on: batterie_enable
- delay: 500ms
- component.update: batterie_spannung
- component.update: batterie_prozent
- priority: -100
then:
- wait_until:
condition:
lambda: ‘return id(ha_time).now().is_valid();’
timeout: 20s
- lambda: |-
auto now = id(ha_time).now();
id(ist_nacht) = (now.is_valid() && (now.hour >= 22 || now.hour < 5));
ESP_LOGI(“boot”, “Nacht? %d | Stunde: %d”, id(ist_nacht), now.hour);
- if:
condition:
lambda: ‘return id(ist_nacht);’
then:
- wait_until:
condition:
lambda: ‘return !id(ha_bild_url).state.empty();’
timeout: 15s
- if:
condition:
lambda: ‘return !id(ha_bild_url).state.empty();’
then:
- online_image.set_url:
id: nachtbild
url: !lambda ‘return id(ha_bild_url).state;’
- component.update: nachtbild
else:
- logger.log: “Keine URL erhalten - schlafe”
- script.execute: schlafen_planen
else:
- component.update: epaper_display
- script.execute: schlafen_planen
esp32:
variant: esp32s3
framework:
type: esp-idf
psram:
mode: octal
speed: 80MHz
logger:
api:
encryption:
key: “DEIN_API_KEY”
ota:
platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: E-Ink Display Fallback Hotspot
password: “DEIN_FALLBACK_PASSWORT”
captive_portal:
http_request:
verify_ssl: false
timeout: 20s
watchdog_timeout: 25s
deep_sleep:
id: ds
sleep_duration: 60min
time:
platform: homeassistant
id: ha_time
output:
platform: gpio
pin: GPIO21
id: batterie_enable
switch:
platform: template
name: “Display wachhalten”
id: sw_wachhalten
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
globals:
id: letzte_temp
type: float
restore_value: yes
initial_value: “0.0”
id: letzter_zustand
type: std::string
restore_value: yes
initial_value: ‘“…”’
id: g_wetter_extra
type: std::string
restore_value: yes
initial_value: ‘“”’
id: g_termine
type: std::string
restore_value: yes
initial_value: ‘“”’
id: g_sprit
type: std::string
restore_value: yes
initial_value: ‘“”’
id: ist_nacht
type: bool
restore_value: no
initial_value: “false”
online_image:
id: nachtbild
url: “http://DEINE_HA_IP:8123/local/bilder/platzhalter.png”
format: PNG
type: RGB565
resize: 800x480
buffer_size: 65536
on_download_finished:
component.update: epaper_display
script.execute: schlafen_planen
on_error:
logger.log: “FEHLER: Bild konnte NICHT geladen werden”
script.execute: schlafen_planen
script:
id: schlafen_planen
mode: restart
then:
delay: 45s
if:
condition:
lambda: ‘return !id(sw_wachhalten).state;’
then:
- deep_sleep.enter: ds
text_sensor:
platform: homeassistant
id: wetter_zustand
entity_id: weather.DEIN_WETTER
on_value:
then:
- lambda: |-
if (x != “unknown” && x != “unavailable” && x != “”) {
id(letzter_zustand) = x;
}
platform: homeassistant
id: ha_bild_url
entity_id: input_text.display_nachtbild_url
platform: homeassistant
id: ha_sprit
entity_id: input_text.display_sprit
on_value:
then:
- lambda: ‘if (!x.empty()) id(g_sprit) = x;’
platform: homeassistant
id: ha_wetter_extra
entity_id: input_text.display_wetter_extra
on_value:
then:
- lambda: ‘if (!x.empty()) id(g_wetter_extra) = x;’
platform: homeassistant
id: ha_termine_woche
entity_id: input_text.display_termine_woche
on_value:
then:
- lambda: ‘if (!x.empty()) id(g_termine) = x;’
sensor:
platform: homeassistant
id: wetter_temp
entity_id: weather.DEIN_WETTER
attribute: temperature
on_value:
then:
- lambda: |-
if (!isnan(x)) {
id(letzte_temp) = x;
}
platform: adc
pin: GPIO1
name: “Akku Spannung”
id: batterie_spannung
update_interval: 60s
attenuation: 12db
filters:
multiply: 2.0
on_value:
then:
component.update: batterie_prozent
platform: template
name: “Akku”
id: batterie_prozent
unit_of_measurement: “%”
device_class: battery
state_class: measurement
update_interval: never
lambda: ‘return id(batterie_spannung).state;’
filters:
calibrate_linear:
4.15 → 100.0
3.96 → 90.0
3.91 → 80.0
3.85 → 70.0
3.80 → 60.0
3.75 → 50.0
3.68 → 40.0
3.58 → 30.0
3.49 → 20.0
3.41 → 10.0
3.30 → 5.0
3.27 → 0.0
clamp:
min_value: 0
max_value: 100
font:
file: “gfonts://Inter@700”
id: font_gross
size: 40
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "
file: “gfonts://Inter@700”
id: font_riesig
size: 64
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "
file: “gfonts://Inter@500”
id: font_normal
size: 28
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "
file: “gfonts://Inter@600”
id: font_titel
size: 24
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "
file: “fonts/MaterialDesignIconsDesktop.ttf”
id: font_wettericon
size: 90
glyphs: [
“\U000F0599”, “\U000F0594”, “\U000F0590”, “\U000F0595”,
“\U000F0597”, “\U000F0596”, “\U000F0593”, “\U000F067E”,
“\U000F0598”, “\U000F067F”, “\U000F0591”, “\U000F0592”,
“\U000F059D”, “\U000F0F31”,
]
color:
id: col_black
red: 0%
green: 0%
blue: 0%
id: col_red
red: 100%
green: 0%
blue: 0%
id: col_blue
red: 0%
green: 0%
blue: 100%
spi:
clk_pin: GPIO7
mosi_pin: GPIO9
display:
platform: epaper_spi
model: Seeed-reTerminal-E1002
id: epaper_display
update_interval: never
lambda: |-
auto now = id(ha_time).now();
// ======== NACHT-MODUS: Bild aus HA ========
if (id(ist_nacht)) {
it.image(0, 0, id(nachtbild));
return;
}
// ======== ÜBERSICHT ========
it.fill(Color::WHITE);
static const char *wochentage
= {“Sonntag”,“Montag”,“Dienstag”,“Mittwoch”,“Donnerstag”,“Freitag”,“Samstag”};
static const char *monate
= {“Januar”,“Februar”,“März”,“April”,“Mai”,“Juni”,“Juli”,“August”,“September”,“Oktober”,“November”,“Dezember”};
if (now.is_valid()) {
// Wochentag selbst berechnen (Sakamoto) - unabhängig von day_of_week
int jy = now.year;
int jm = now.month;
int jd = now.day_of_month;
static const int tt
= {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
if (jm < 3) jy -= 1;
int wt = (jy + jy/4 - jy/100 + jy/400 + tt[jm-1] + jd) % 7;
it.printf(20, 15, id(font_gross), id(col_black),
"%s, %d. %s", wochentage[wt], now.day_of_month, monate[now.month - 1]);
it.printf(600, 15, id(font_gross), id(col_black), TextAlign::TOP_RIGHT,
"%02d:%02d", now.hour, now.minute);
}
if (!isnan(id(batterie_prozent).state)) {
auto akku_farbe = id(batterie_prozent).state < 20 ? id(col_red) : id(col_black);
it.printf(780, 15, id(font_gross), akku_farbe, TextAlign::TOP_RIGHT,
“%.0f%%”, id(batterie_prozent).state);
}
it.filled_rectangle(20, 70, 760, 3, id(col_black));
std::string z = id(letzter_zustand);
std::string z_de = “Unbekannt”;
const char* icon = “\U000F0F31”;
if (z == “sunny”) { z_de = “Sonnig”; icon = “\U000F0599”; }
else if (z == “clear-night”) { z_de = “Klar”; icon = “\U000F0594”; }
else if (z == “cloudy”) { z_de = “Bewölkt”; icon = “\U000F0590”; }
else if (z == “partlycloudy”) { z_de = “Teils bewölkt”; icon = “\U000F0595”; }
else if (z == “rainy”) { z_de = “Regen”; icon = “\U000F0597”; }
else if (z == “pouring”) { z_de = “Starkregen”; icon = “\U000F0596”; }
else if (z == “lightning”) { z_de = “Gewitter”; icon = “\U000F0593”; }
else if (z == “lightning-rainy”){ z_de = “Gewitter, Regen”; icon = “\U000F067E”; }
else if (z == “snowy”) { z_de = “Schnee”; icon = “\U000F0598”; }
else if (z == “snowy-rainy”) { z_de = “Schneeregen”; icon = “\U000F067F”; }
else if (z == “fog”) { z_de = “Nebel”; icon = “\U000F0591”; }
else if (z == “hail”) { z_de = “Hagel”; icon = “\U000F0592”; }
else if (z == “windy”) { z_de = “Windig”; icon = “\U000F059D”; }
else if (z == “windy-variant”) { z_de = “Windig”; icon = “\U000F059D”; }
else if (z == “exceptional”) { z_de = “Außergewöhnlich”; icon = “\U000F0F31”; }
it.printf(30, 80, id(font_wettericon), id(col_red), “%s”, icon);
it.printf(165, 78, id(font_riesig), id(col_black), “%.1f°C”, id(letzte_temp));
it.printf(165, 150, id(font_normal), id(col_black), “%s”, z_de.c_str());
{
std::string w = id(g_wetter_extra);
int wy = 80;
size_t ws = 0;
while (ws <= w.length()) {
size_t nl = w.find(‘\n’, ws);
std::string zeile = (nl == std::string::npos) ? w.substr(ws) : w.substr(ws, nl - ws);
if (!zeile.empty()) {
it.printf(480, wy, id(font_normal), id(col_black), “%s”, zeile.c_str());
wy += 38;
}
if (nl == std::string::npos) break;
ws = nl + 1;
}
}
it.filled_rectangle(20, 188, 760, 3, id(col_black));
it.printf(20, 196, id(font_titel), id(col_blue), “Termine”);
{
std::string t2 = id(g_termine);
int y2 = 226;
size_t start = 0;
while (start <= t2.length()) {
size_t nl = t2.find(‘\n’, start);
std::string zeile = (nl == std::string::npos) ? t2.substr(start) : t2.substr(start, nl - start);
if (!zeile.empty()) {
it.printf(20, y2, id(font_normal), id(col_black), “%s”, zeile.c_str());
y2 += 33;
}
if (nl == std::string::npos) break;
start = nl + 1;
}
}
it.filled_rectangle(20, 396, 760, 3, id(col_black));
it.printf(20, 402, id(font_titel), id(col_blue), “Sprit (Super) - Top 3”);
it.printf(20, 434, id(font_normal), id(col_black), “%s”, id(g_sprit).c_str());