infrapuzzle/esphome/esdisplay_kitchen.yaml

272 lines
7.7 KiB
YAML

esphome:
name: esdisplay-kitchen
comment: "e-Paper display in the kitchen connected to Home Assistant"
platformio_options:
lib_deps:
- bblanchon/ArduinoJson@7.4.2
includes:
- prometheus_client.h
globals:
- id: prometheus_connected
type: bool
restore_value: no
initial_value: 'true'
esp32:
board: nodemcu-32s
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API (auto-discovery)
api:
password: ""
# Over-The-Air updates
ota:
platform: esphome
wifi:
ssid: !env_var WIFI_SSID
password: !env_var WIFI_PASSWORD
# Enable fallback hotspot in case wifi fails
ap:
ssid: "Kitchen Display Fallback"
password: "fallbackpassword"
captive_portal:
# Time synchronization to get current time on display
time:
- platform: sntp
id: sntp_time
timezone: "Europe/Berlin"
# Define local and imported sensors
sensor:
- platform: homeassistant
id: ha_outdoor_temp
entity_id: sensor.regensburg_regensburg_temperatur
internal: true
- platform: homeassistant
id: ha_kitchen_temp
entity_id: sensor.home_kuche_wandthermostat_temperature
internal: true
# Local template sensors scraped from Prometheus
- platform: template
id: local_cellar_temp
name: "Kitchen Display Cellar Temperature Indoor"
unit_of_measurement: "°C"
accuracy_decimals: 1
- platform: template
id: local_cellar_humi
name: "Kitchen Display Cellar Humidity Indoor"
unit_of_measurement: "%"
accuracy_decimals: 0
- platform: template
id: local_cellar_dp
name: "Kitchen Display Cellar Dewpoint Indoor"
unit_of_measurement: "°C"
accuracy_decimals: 1
- platform: template
id: local_cellar_out_temp
name: "Kitchen Display Cellar Temperature Outdoor"
unit_of_measurement: "°C"
accuracy_decimals: 1
- platform: template
id: local_cellar_out_humi
name: "Kitchen Display Cellar Humidity Outdoor"
unit_of_measurement: "%"
accuracy_decimals: 0
- platform: template
id: local_cellar_out_dp
name: "Kitchen Display Cellar Dewpoint Outdoor"
unit_of_measurement: "°C"
accuracy_decimals: 1
- platform: template
id: local_fan_status
name: "Kitchen Display Cellar Fan Status"
accuracy_decimals: 0
- platform: template
id: local_mold_critical
name: "Kitchen Display Cellar Mold Critical"
accuracy_decimals: 0
- platform: template
id: local_fan_would_activate
name: "Kitchen Display Cellar Fan Activation Criteria"
accuracy_decimals: 0
- platform: template
id: local_mold_threshold
name: "Kitchen Display Cellar Mold Threshold"
unit_of_measurement: "%"
accuracy_decimals: 0
- platform: template
id: local_shelly_online
name: "Kitchen Display Cellar Shelly Online"
accuracy_decimals: 0
# Interval to scrape metrics from Prometheus every 30 seconds
interval:
- interval: 30s
then:
- lambda: |-
fetch_prometheus();
text_sensor:
- platform: homeassistant
id: ha_message
entity_id: input_text.epaper_message
internal: true
- platform: wifi_info
ip_address:
name: "IP Address"
id: wifi_ip
# Font configuration (uses Google Fonts Roboto)
font:
- file: "gfonts://Roboto"
id: font_title
size: 16
- file: "gfonts://Roboto"
id: font_body
size: 12
# SPI bus configuration for e-Paper display
spi:
clk_pin: GPIO18
mosi_pin: GPIO23
display:
- platform: waveshare_epaper
id: epaper_display
model: 2.13inv3
cs_pin: GPIO5
dc_pin: GPIO17
reset_pin: GPIO16
busy_pin: GPIO4
rotation: 90
update_interval: 60s
lambda: |-
// Fill background with white
it.fill(COLOR_OFF);
// Check if Prometheus is reachable
if (!id(prometheus_connected)) {
it.print(125, 20, id(font_title), TextAlign::CENTER, "PROMETHEUS ERROR");
it.print(125, 55, id(font_body), TextAlign::CENTER, "Cannot reach server");
it.print(125, 75, id(font_body), TextAlign::CENTER, "Check network or auth");
// Show time at the bottom if synced
it.line(5, 96, 245, 96);
char time_str[30];
auto time_now = id(sntp_time).now();
if (time_now.is_valid()) {
it.strftime(5, 100, id(font_body), "Time: %a %H:%M", time_now);
} else {
it.print(5, 100, id(font_body), "Time: --:--");
}
return;
}
// --- SECTION 1: HEADER ---
it.print(5, 2, id(font_title), "Cellar Fan (TAUPI)");
// Time and weekday on the right
auto time_now = id(sntp_time).now();
if (time_now.is_valid()) {
it.strftime(245, 2, id(font_body), TextAlign::TOP_RIGHT, "%a %H:%M", time_now);
} else {
it.print(245, 2, id(font_body), TextAlign::TOP_RIGHT, "Time: --:--");
}
// Draw header divider
it.line(5, 18, 245, 18);
// --- SECTION 2: CLIMATE CELLS ---
// Vertical separator
it.line(122, 22, 122, 82);
// CELLAR INDOOR (Left)
it.print(5, 22, id(font_body), "CELLAR INDOOR");
if (!isnan(id(local_cellar_temp).state)) {
it.printf(5, 36, id(font_body), "Temp: %.1f C", id(local_cellar_temp).state);
} else {
it.print(5, 36, id(font_body), "Temp: -- C");
}
if (!isnan(id(local_cellar_humi).state)) {
it.printf(5, 49, id(font_body), "Humi: %.0f%%", id(local_cellar_humi).state);
} else {
it.print(5, 49, id(font_body), "Humi: --%%");
}
if (!isnan(id(local_cellar_dp).state)) {
it.printf(5, 62, id(font_body), "Dewp: %.1f C", id(local_cellar_dp).state);
} else {
it.print(5, 62, id(font_body), "Dewp: -- C");
}
if (!isnan(id(local_mold_threshold).state)) {
it.printf(5, 75, id(font_body), "Limit: %.0f%%", id(local_mold_threshold).state);
} else {
it.print(5, 75, id(font_body), "Limit: --%%");
}
// CELLAR OUTDOOR (Right)
it.print(128, 22, id(font_body), "CELLAR OUTDOOR");
if (!isnan(id(local_cellar_out_temp).state)) {
it.printf(128, 36, id(font_body), "Temp: %.1f C", id(local_cellar_out_temp).state);
} else {
it.print(128, 36, id(font_body), "Temp: -- C");
}
if (!isnan(id(local_cellar_out_humi).state)) {
it.printf(128, 49, id(font_body), "Humi: %.0f%%", id(local_cellar_out_humi).state);
} else {
it.print(128, 49, id(font_body), "Humi: --%%");
}
if (!isnan(id(local_cellar_out_dp).state)) {
it.printf(128, 62, id(font_body), "Dewp: %.1f C", id(local_cellar_out_dp).state);
} else {
it.print(128, 62, id(font_body), "Dewp: -- C");
}
// --- SECTION 3: STATUS BAR (Bottom) ---
it.line(5, 86, 245, 86);
// Fan status
const char* fan_state = "OFF";
if (!isnan(id(local_fan_status).state) && id(local_fan_status).state > 0.5) {
fan_state = "ON";
}
it.printf(5, 90, id(font_body), "Fan: %s", fan_state);
// Mold status
const char* mold_state = "SAFE";
if (!isnan(id(local_mold_critical).state) && id(local_mold_critical).state > 0.5) {
mold_state = "CRITICAL";
}
it.printf(128, 90, id(font_body), "Mold: %s", mold_state);
// Criteria status
const char* crit_state = "NO";
if (!isnan(id(local_fan_would_activate).state) && id(local_fan_would_activate).state > 0.5) {
crit_state = "YES";
}
it.printf(5, 104, id(font_body), "Criteria: %s", crit_state);
// Shelly Status
const char* shelly_state = "OFFLINE";
if (!isnan(id(local_shelly_online).state) && id(local_shelly_online).state > 0.5) {
shelly_state = "ONLINE";
}
it.printf(128, 104, id(font_body), "Shelly: %s", shelly_state);