mirror of
https://github.com/michivonah/esphome-eink-dashboard.git
synced 2025-12-22 21:16:28 +01:00
rename yaml file
This commit is contained in:
parent
5f1553a265
commit
57e0656763
1 changed files with 0 additions and 0 deletions
484
weatherman.yaml
484
weatherman.yaml
|
|
@ -1,484 +0,0 @@
|
|||
# E-Ink Frame/Dashboard
|
||||
# Michi von Ah - 2025
|
||||
# GitHub: https://github.com/michivonah/esphome-eink-dashboard
|
||||
# Inspired by (based on) https://github.com/Madelena/esphome-weatherman-dashboard/blob/main/weatherman.yaml
|
||||
|
||||
esphome:
|
||||
name: e-ink-frame
|
||||
friendly_name: E-Ink_Frame
|
||||
on_boot:
|
||||
priority: 200.0
|
||||
then:
|
||||
- component.update: eink_display
|
||||
- wait_until:
|
||||
condition:
|
||||
lambda: 'return id(data_updated) == true;'
|
||||
# Wait a bit longer so all the items are received
|
||||
- delay: 5s
|
||||
- logger.log: "Initial sensor data received: Refreshing display..."
|
||||
- lambda: 'id(initial_data_received) = true;'
|
||||
- script.execute: update_screen
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
# Enable Home Assistant API
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_key_e-ink-frame
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_key_e-ink-frame
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
|
||||
|
||||
|
||||
# Include custom fonts
|
||||
font:
|
||||
- file: 'fonts/Roboto-Medium.ttf'
|
||||
id: font_small_book
|
||||
size: 18
|
||||
- file: 'fonts/Roboto-Bold.ttf'
|
||||
id: font_large_bold
|
||||
size: 82
|
||||
glyphs: [' ', '-', '°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', ',', '.']
|
||||
- file: 'fonts/Roboto-Bold.ttf'
|
||||
id: font_title
|
||||
size: 54
|
||||
glyphs: ['W', 'E', 'A', 'T', 'H', 'R', 'L', 'I', 'N', 'M', 'U', 'S', 'C', ' ']
|
||||
- file: 'fonts/Roboto-Bold.ttf'
|
||||
id: font_medium_bold
|
||||
size: 30
|
||||
glyphsets:
|
||||
- GF_Latin_Core
|
||||
- file: 'fonts/Roboto-Bold.ttf'
|
||||
id: font_small_bold
|
||||
size: 18
|
||||
glyphsets:
|
||||
- GF_Latin_Core
|
||||
|
||||
|
||||
# Include Material Design Icons font
|
||||
# Thanks to https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16
|
||||
- file: 'fonts/materialdesignicons-webfont.ttf'
|
||||
id: font_mdi_large
|
||||
size: 78
|
||||
glyphs: &mdi-icons
|
||||
- "\U000F0590" # mdi-weather-cloudy
|
||||
- "\U000F0F2F" # mdi-weather-cloudy-alert
|
||||
- "\U000F0E6E" # mdi-weather-cloudy-arrow-right
|
||||
- "\U000F0591" # mdi-weather-fog
|
||||
- "\U000F0592" # mdi-weather-hail
|
||||
- "\U000F0F30" # mdi-weather-hazy
|
||||
- "\U000F0898" # mdi-weather-hurricane
|
||||
- "\U000F0593" # mdi-weather-lightning
|
||||
- "\U000F067E" # mdi-weather-lightning-rainy
|
||||
- "\U000F0594" # mdi-weather-night
|
||||
- "\U000F0F31" # mdi-weather-night-partly-cloudy
|
||||
- "\U000F0595" # mdi-weather-partly-cloudy
|
||||
- "\U000F0F32" # mdi-weather-partly-lightning
|
||||
- "\U000F0F33" # mdi-weather-partly-rainy
|
||||
- "\U000F0F34" # mdi-weather-partly-snowy
|
||||
- "\U000F0F35" # mdi-weather-partly-snowy-rainy
|
||||
- "\U000F0596" # mdi-weather-pouring
|
||||
- "\U000F0597" # mdi-weather-rainy
|
||||
- "\U000F0598" # mdi-weather-snowy
|
||||
- "\U000F0F36" # mdi-weather-snowy-heavy
|
||||
- "\U000F067F" # mdi-weather-snowy-rainy
|
||||
- "\U000F0599" # mdi-weather-sunny
|
||||
- "\U000F0F37" # mdi-weather-sunny-alert
|
||||
- "\U000F14E4" # mdi-weather-sunny-off
|
||||
- "\U000F059A" # mdi-weather-sunset
|
||||
- "\U000F059B" # mdi-weather-sunset-down
|
||||
- "\U000F059C" # mdi-weather-sunset-up
|
||||
- "\U000F0F38" # mdi-weather-tornado
|
||||
- "\U000F059D" # mdi-weather-windy
|
||||
- "\U000F059E" # mdi-weather-windy-variant
|
||||
- "\U000F029A" # mdi:gauge
|
||||
- "\U000F09A1" # mdi:shower-head
|
||||
- "\U000F040A" # mdi:play
|
||||
- "\U000F03E4" # mdi:pause
|
||||
- "\U000F0F54" # mdi:home-thermometer
|
||||
- "\U000F1A35" # mdi:cloud-percent
|
||||
- file: 'fonts/materialdesignicons-webfont.ttf'
|
||||
id: font_mdi_medium
|
||||
size: 42
|
||||
glyphs: *mdi-icons
|
||||
|
||||
|
||||
|
||||
# Button definitions
|
||||
button:
|
||||
- platform: shutdown
|
||||
name: "Shutdown"
|
||||
- platform: restart
|
||||
name: "Restart"
|
||||
- platform: template
|
||||
name: "Refresh Screen"
|
||||
entity_category: config
|
||||
on_press:
|
||||
- script.execute: update_screen
|
||||
|
||||
globals:
|
||||
- id: data_updated
|
||||
type: bool
|
||||
restore_value: no
|
||||
initial_value: 'false'
|
||||
- id: initial_data_received
|
||||
type: bool
|
||||
restore_value: no
|
||||
initial_value: 'false'
|
||||
- id: recorded_display_refresh
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: '0'
|
||||
|
||||
# Script for updating screen - Refresh display and publish refresh count and time. (Thanks @paviro!)
|
||||
script:
|
||||
- id: update_screen
|
||||
then:
|
||||
- lambda: 'id(data_updated) = false;'
|
||||
- component.update: eink_display
|
||||
- lambda: 'id(recorded_display_refresh) += 1;'
|
||||
- lambda: 'id(display_last_update).publish_state(id(homeassistant_time).now().timestamp);'
|
||||
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: homeassistant_time
|
||||
timezone: Europe/Zurich
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /1
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(data_updated) == true;'
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
all:
|
||||
- binary_sensor.is_on: presence_detected
|
||||
- lambda: 'return id(presence_lux).state > 5;'
|
||||
then:
|
||||
- logger.log: "Sensor data updated and activity in home detected: Refreshing display..."
|
||||
- script.execute: update_screen
|
||||
else:
|
||||
- logger.log: "Sensor data updated but no activity in home - skipping display refresh."
|
||||
else:
|
||||
- logger.log: "No sensors updated - skipping display refresh."
|
||||
|
||||
|
||||
# Check if motion is detected in the living room.
|
||||
binary_sensor:
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-PRESENCE-SENSOR>
|
||||
id: presence_detected
|
||||
|
||||
|
||||
sensor:
|
||||
# Create sensors for monitoring Weatherman remotely.
|
||||
- platform: template
|
||||
name: "Display Last Update"
|
||||
device_class: timestamp
|
||||
entity_category: "diagnostic"
|
||||
id: display_last_update
|
||||
|
||||
- platform: template
|
||||
name: "Recorded Display Refresh"
|
||||
accuracy_decimals: 0
|
||||
unit_of_measurement: "Refreshes"
|
||||
state_class: "total_increasing"
|
||||
entity_category: "diagnostic"
|
||||
lambda: 'return id(recorded_display_refresh);'
|
||||
|
||||
- platform: wifi_signal
|
||||
name: "WiFi Signal Strength"
|
||||
id: wifisignal
|
||||
unit_of_measurement: "dBm"
|
||||
entity_category: "diagnostic"
|
||||
update_interval: 60s
|
||||
|
||||
# LUX value used for deciding if display should be updated (only if it can be read)
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-LUX-SENSOR>
|
||||
id: presence_lux
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: weather.<YOUR-WEATHER>
|
||||
attribute: temperature
|
||||
id: weather_temperature
|
||||
on_value:
|
||||
then:
|
||||
- lambda: 'id(data_updated) = true;'
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-RAIN-SENSOR>
|
||||
id: weather_rain
|
||||
on_value:
|
||||
then:
|
||||
- lambda: 'id(data_updated) = true;'
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-IAQ-SENSOR>
|
||||
id: indoor_iaq
|
||||
on_value:
|
||||
then:
|
||||
- lambda: 'id(data_updated) = true;'
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-TEMP-SENSOR>
|
||||
id: indoor_temperature
|
||||
on_value:
|
||||
then:
|
||||
- lambda: 'id(data_updated) = true;'
|
||||
|
||||
text_sensor:
|
||||
- platform: homeassistant
|
||||
entity_id: weather.<YOUR-WEATHER>
|
||||
id: weather_state
|
||||
on_value:
|
||||
then:
|
||||
- lambda: 'id(data_updated) = true;'
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-SPEAKER>
|
||||
id: media_player_sonos_state
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-SPEAKER>
|
||||
attribute: media_title
|
||||
id: media_player_sonos_song
|
||||
on_value:
|
||||
then:
|
||||
- lambda: 'id(data_updated) = true;'
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-SPEAKER>
|
||||
attribute: media_artist
|
||||
id: media_player_sonos_artist
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-SPOTIFY>
|
||||
id: media_player_spotify_state
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-SPOTIFY>
|
||||
attribute: media_title
|
||||
id: media_player_spotify_song
|
||||
on_value:
|
||||
then:
|
||||
- lambda: 'id(data_updated) = true;'
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <YOUR-SPOTIFY>
|
||||
attribute: media_artist
|
||||
id: media_player_spotify_artist
|
||||
|
||||
- platform: homeassistant
|
||||
entity_id: <INPUT-DATETIME-ENTITY>
|
||||
id: shower_time
|
||||
|
||||
|
||||
# Define colors
|
||||
# This design is white on black so this is necessary.
|
||||
color:
|
||||
- id: color_bg
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
white: 100%
|
||||
- id: color_text
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
white: 0%
|
||||
|
||||
|
||||
# Pins for Waveshare ePaper ESP Board
|
||||
spi:
|
||||
clk_pin: GPIO13
|
||||
mosi_pin: GPIO14
|
||||
|
||||
# Now render everything on the ePaper screen.
|
||||
display:
|
||||
- platform: waveshare_epaper
|
||||
id: eink_display
|
||||
cs_pin: GPIO15
|
||||
dc_pin: GPIO27
|
||||
busy_pin:
|
||||
number: GPIO25
|
||||
inverted: True
|
||||
reset_pin: GPIO26
|
||||
reset_duration: 2ms
|
||||
model: 7.50inV2p
|
||||
update_interval: never
|
||||
full_update_every: 30
|
||||
rotation: 270°
|
||||
lambda: |-
|
||||
// Map weather states to MDI characters.
|
||||
std::map<std::string, std::string> weather_icon_map
|
||||
{
|
||||
{"cloudy", "\U000F0590"},
|
||||
{"cloudy-alert", "\U000F0F2F"},
|
||||
{"cloudy-arrow-right", "\U000F0E6E"},
|
||||
{"fog", "\U000F0591"},
|
||||
{"hail", "\U000F0592"},
|
||||
{"hazy", "\U000F0F30"},
|
||||
{"hurricane", "\U000F0898"},
|
||||
{"lightning", "\U000F0593"},
|
||||
{"lightning-rainy", "\U000F067E"},
|
||||
{"night", "\U000F0594"},
|
||||
{"night-partly-cloudy", "\U000F0F31"},
|
||||
{"partlycloudy", "\U000F0595"},
|
||||
{"partly-lightning", "\U000F0F32"},
|
||||
{"partly-rainy", "\U000F0F33"},
|
||||
{"partly-snowy", "\U000F0F34"},
|
||||
{"partly-snowy-rainy", "\U000F0F35"},
|
||||
{"pouring", "\U000F0596"},
|
||||
{"rainy", "\U000F0597"},
|
||||
{"snowy", "\U000F0598"},
|
||||
{"snowy-heavy", "\U000F0F36"},
|
||||
{"snowy-rainy", "\U000F067F"},
|
||||
{"sunny", "\U000F0599"},
|
||||
{"sunny-alert", "\U000F0F37"},
|
||||
{"sunny-off", "\U000F14E4"},
|
||||
{"sunset", "\U000F059A"},
|
||||
{"sunset-down", "\U000F059B"},
|
||||
{"sunset-up", "\U000F059C"},
|
||||
{"tornado", "\U000F0F38"},
|
||||
{"windy", "\U000F059D"},
|
||||
{"windy-variant", "\U000F059E"},
|
||||
};
|
||||
|
||||
// Fill background.
|
||||
it.fill(color_bg);
|
||||
|
||||
// Show loading screen before data is received.
|
||||
if (id(initial_data_received) == false) {
|
||||
it.printf(240, 390, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "WAITING FOR DATA...");
|
||||
} else {
|
||||
// GENERAL PARAMS
|
||||
uint base_x = 240;
|
||||
uint base_y = 84;
|
||||
|
||||
// Weather section
|
||||
it.printf(base_x, base_y, id(font_title), color_text, TextAlign::TOP_CENTER, "WEATHER");
|
||||
|
||||
it.printf(114, 178, id(font_mdi_large), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_state).state.c_str()].c_str());
|
||||
|
||||
it.printf(168, 168, id(font_large_bold), color_text, "%2.1f°C", id(weather_temperature).state);
|
||||
|
||||
// Music section
|
||||
uint music_base_x = base_x;
|
||||
uint music_base_y = 296;
|
||||
uint music_content_offset_y = 80;
|
||||
uint music_linebreak_y = 50;
|
||||
|
||||
it.printf(music_base_x, music_base_y, id(font_title), color_text, TextAlign::TOP_CENTER, "MUSIC");
|
||||
|
||||
String sonos_state = id(media_player_sonos_state).state.c_str();
|
||||
String spotify_state = id(media_player_spotify_state).state.c_str();
|
||||
String song = "";
|
||||
String artist = "";
|
||||
bool isPlaying = false;
|
||||
|
||||
if (sonos_state == "playing") {
|
||||
isPlaying = true;
|
||||
song = id(media_player_sonos_song).state.c_str();
|
||||
artist = id(media_player_sonos_artist).state.c_str();
|
||||
}
|
||||
else if (spotify_state == "playing"){
|
||||
isPlaying = true;
|
||||
song = id(media_player_spotify_song).state.c_str();
|
||||
artist = id(media_player_spotify_artist).state.c_str();
|
||||
}
|
||||
else{
|
||||
isPlaying = false;
|
||||
}
|
||||
|
||||
// show current song if music is playing
|
||||
if (isPlaying){
|
||||
if (song.length() > 35) {
|
||||
it.printf(music_base_x, music_base_y + music_content_offset_y, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", song.c_str());
|
||||
} else {
|
||||
it.printf(music_base_x, music_base_y + music_content_offset_y, id(font_medium_bold), color_text, TextAlign::TOP_CENTER, "%s", song.c_str());
|
||||
}
|
||||
|
||||
if (artist.length() > 35) {
|
||||
it.printf(music_base_x, music_base_y + music_content_offset_y + music_linebreak_y, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "by %s", artist.c_str());
|
||||
} else {
|
||||
it.printf(music_base_x, music_base_y + music_content_offset_y + music_linebreak_y, id(font_medium_bold), color_text, TextAlign::TOP_CENTER, "by %s", artist.c_str());
|
||||
}
|
||||
}
|
||||
else{
|
||||
it.printf(music_base_x, music_base_y + music_content_offset_y, id(font_medium_bold), color_text, TextAlign::TOP_CENTER, "No music playing");
|
||||
}
|
||||
|
||||
// Multiple infos with icon
|
||||
// General settings
|
||||
uint multi_info_base_x = 78;
|
||||
uint text_offset_x = 48;
|
||||
uint tab_offset_x = 172;
|
||||
uint multi_info_base_y = music_base_y + 220;
|
||||
uint text_offset_y = 4;
|
||||
uint linebreak_offset_y = 60;
|
||||
|
||||
// IAQ value
|
||||
it.printf(multi_info_base_x, multi_info_base_y, id(font_mdi_medium), color_text, "\U000F029A");
|
||||
it.printf(multi_info_base_x + text_offset_x, multi_info_base_y + text_offset_y, id(font_medium_bold), color_text, "%.0f IAQ", id(indoor_iaq).state);
|
||||
|
||||
// Indoor temperature
|
||||
it.printf(multi_info_base_x, multi_info_base_y + linebreak_offset_y, id(font_mdi_medium), color_text, "\U000F0F54");
|
||||
it.printf(multi_info_base_x + text_offset_x, multi_info_base_y + linebreak_offset_y + text_offset_y, id(font_medium_bold), color_text, "%.1f°C", id(indoor_temperature).state);
|
||||
|
||||
// Last shower timestamp
|
||||
it.printf(multi_info_base_x + tab_offset_x, multi_info_base_y, id(font_mdi_medium), color_text, "\U000F09A1");
|
||||
String shower_datetime_str = id(shower_time).state.c_str();
|
||||
int year = atoi(shower_datetime_str.c_str());
|
||||
int month = atoi(shower_datetime_str.c_str() + 5);
|
||||
int day = atoi(shower_datetime_str.c_str() + 8);
|
||||
int hour = atoi(shower_datetime_str.c_str() + 11);
|
||||
int minute = atoi(shower_datetime_str.c_str() + 14);
|
||||
|
||||
// calculate day (calculation by gpt-4.1-mini)
|
||||
int y = year;
|
||||
int m = month;
|
||||
int d = day;
|
||||
if (m < 3) {
|
||||
m += 12;
|
||||
y -= 1;
|
||||
}
|
||||
int K = y % 100;
|
||||
int J = y / 100;
|
||||
int f = d + int((13*(m + 1))/5) + K + int(K/4) + int(J/4) + 5*J;
|
||||
int weekday = ((f + 5) % 7) + 1;
|
||||
const char* weekdays_de[] = {
|
||||
"Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"
|
||||
};
|
||||
const char* wday_name = (weekday >=1 && weekday <=7) ? weekdays_de[weekday - 1] : "?";
|
||||
|
||||
it.printf(multi_info_base_x + tab_offset_x + text_offset_x, multi_info_base_y + text_offset_y, id(font_medium_bold), color_text, "%s, %02d:%02d", wday_name, hour, minute);
|
||||
|
||||
// Rain amount (weather)
|
||||
it.printf(multi_info_base_x + tab_offset_x, multi_info_base_y + linebreak_offset_y, id(font_mdi_medium), color_text, "\U000F1A35");
|
||||
it.printf(multi_info_base_x + tab_offset_x + text_offset_x, multi_info_base_y + linebreak_offset_y + text_offset_y, id(font_medium_bold), color_text, "%.1fmm", id(weather_rain).state);
|
||||
|
||||
// Refresh Timestamp
|
||||
// Code by EnsconcE from https://community.home-assistant.io/t/esphome-show-time/348903
|
||||
char str[17];
|
||||
time_t currTime = id(homeassistant_time).now().timestamp;
|
||||
strftime(str, sizeof(str), "%H:%M", localtime(&currTime));
|
||||
it.printf(240, 710, id(font_small_book), color_text, TextAlign::TOP_CENTER, "REFRESHED AT %s", str);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue