Adding esphome display
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
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);
|
||||
Reference in New Issue
Block a user