Use ESPHome with e-ink Displays to blend in with your home decor!

That also looks nice! would you mind sharing the .yaml (including HA templates?)

1 Like

Not a problem, the code might need some clean-up though, but it works. The only issue I have is that I cannot compile the yaml if I am using secrets. ESPHome returns error, just change whatever needs to be changed to suit your needs. Oh, you would need to edit “weatherman” config to swap entitity names with yours.

# WEATHERMAN DASHBOARD
# For Home Assistant and ESPHome
# Designed by Madelena Mak 2022 - https://mmak.es

# Cue "Blame it on the Weatherman" by B*Witched!
esphome:
  name: "weatherman"

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

wifi:
  ssid: "CHANGEME"
  password: "CHANGEME"
  
  reboot_timeout: 300s
  
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-901078"
    password: "WHATEVER"
   

# Include time
time:
  - platform: homeassistant
    id: esptime

#Include sun
sun:
  latitude: CHANGEME
  longitude: CHANGEME

# Include custom fonts
font:
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_small_book
    size: 18
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_large_bold
    size: 108
    glyphs: [' ', '°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C',]
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_time
    size: 90
    glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':']
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_weekday
    size: 30
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_day
    size: 65
    glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_month
    size: 40
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_medium_bold
    size: 40
    #glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'M', 'I', 'N']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_small_bold
    size: 18
    # glyphs: ['°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', 'M', 'I', 'N']

  # 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: 96
    glyphs: &mdi-weather-glyphs
      - "\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
      - "\U000F058E" # mdi-water-percent
      - "\U000F04C5" # mdi-spedometer
      - "\U000F0F29" # mdi-snowflake-alert
      - "\U000F15FA" # mdi-windsock
      - "\U000F19B3" # mdi-arrow-down-thin
      - "\U000F19B2" # mdi-arrow-up-thin
      - "\U000F19B4" # mdi-arrow-top-right-thin
      - "\U000F19B7" # mdi-arrow-bottom-right-thin
      - "\U000F00A5" # mdi-binoculars
      - "\U000F018C" # mdi-compass-outline
      - "\U000F05A9" # mdi-wifi
      - "\U000F0928" # mdi-wifi-strength-4
      - "\U000F0925" # mdi-wifi-strength-3 
      - "\U000F0922" # mdi-wifi-strength-2
      - "\U000F091F" # mdi-wifi-strength-1
      - "\U000F092B" # mdi-wifi-strength-alert-outline
      - "\U000F0079" # mdi-battery
      - "\U000F0082" # mdi-battery-90
      - "\U000F0081" # mdi-battery-80
      - "\U000F0080" # mdi-battery-70
      - "\U000F007F" # mdi-battery-60
      - "\U000F007E" # mdi-battery-50
      - "\U000F007D" # mdi-battery-40
      - "\U000F007C" # mdi-battery-30
      - "\U000F007B" # mdi-battery-20
      - "\U000F007A" # mdi-battery-10
      - "\U000F10CD" # mdi-battery-alert-variant-outline
      - "\U000F0E03" # mdi-thermometer-chevron-up
      - "\U000F0E02" # mdi-thermometer-chevron-down
      - "\U000F1A09" # mdi-triangle-small-down
      - "\U000F1A0A" # mdi-triangle-small-up
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_medlarge
    size: 60
    glyphs:
      - "\U000F058E" # mdi-water-percent
      - "\U000F04C5" # mdi-spedometer
      - "\U000F0F29" # mdi-snowflake-alert
      - "\U000F15FA" # mdi-windsock
      - "\U000F0F61" # mdi-moon-first-quarter
      - "\U000F0F62" # mdi-moon-full
      - "\U000F0F63" # mdi-moon-last-quarter
      - "\U000F0F64" # mdi-moon-new
      - "\U000F0F65" # mdi-moon-waning-crescent
      - "\U000F0F66" # mdi-moon-waning-gibbous
      - "\U000F0F67" # mdi-moon-waxing-crescent
      - "\U000F0F68" # mdi-moon-waxing-gibbous
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_medium
    size: 36
    glyphs: *mdi-weather-glyphs


# Include Custom Titles
image:
  - file: "images/weatherman-title-weather.png"
    id: title_weather
    type: BINARY


# Call Weather sensors from HA
sensor:
  # Temperature - My Weather station
  - platform: homeassistant
    entity_id: weather.my_weather_station
    attribute: temperature
    id: weather_temperature
    
  # Humidity - My Weather station
  - platform: homeassistant
    entity_id: weather.my_weather_station
    attribute: humidity
    id: weather_humidity

  # Air pressure - My Weather station
  - platform: homeassistant
    entity_id: weather.my_weather_station
    attribute: pressure
    id: weather_pressure

  # Wind Bearing - from Yr.no (built-in HA weather privider)
  - platform: homeassistant
    entity_id: weather.home
    attribute: wind_bearing
    id: wind_bearing

  # Battery % for Aqara sensor
  - platform: homeassistant
    entity_id: sensor.zigbeeweather_battery
    id: battery_percent
  # Battery Voltage for Aqara sensor
  - platform: homeassistant
    entity_id: sensor.zigbeeweather_battery
    attribute: voltage
    id: battery_voltage

  # Weatherman entities
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_0
    id: weather_temperature_0
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_1
    id: weather_temperature_1
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_2
    id: weather_temperature_2
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_3
    id: weather_temperature_3

  # Strong Wind
  - platform: homeassistant
    entity_id: sensor.wind_warning
    id: wind_warning
  # Wind Speed 
  - platform: homeassistant
    entity_id: weather.my_weather_station
    attribute: wind_speed
    id: wind_speed
  # UV Index
  - platform: homeassistant
    entity_id: sensor.dundonald_uv_index_3_hourly
    id: uv_index
  # WiFi Signal     
  - platform: wifi_signal
    name: "WiFi Signal Sensor"
    id: wifisignal
    update_interval: 20s


text_sensor:
  # Pressure Trend
  - platform: homeassistant
    entity_id: binary_sensor.atmospheric_pressure
    id: pressure_trend
  # Temp Trend
  - platform: homeassistant
    entity_id: binary_sensor.outside_temperature
    id: temp_trend
  # Weather State
  - platform: homeassistant
    entity_id: weather.my_weather_station
    id: weather_state
  # Frost Risk
  - platform: homeassistant
    entity_id: sensor.weather_frostrisk
    id: frost_risk
  # # Weatherman entities
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_now
    id: weather_condition_now
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_0
    id: weather_condition_0
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_0
    id: weather_timestamp_0
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_1
    id: weather_condition_1
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_1
    id: weather_timestamp_1
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_2
    id: weather_condition_2
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_2
    id: weather_timestamp_2
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_3
    id: weather_condition_3
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_3
    id: weather_timestamp_3
  # Sunrise
  - platform: sun
    type: sunrise
    id: sun_sunrise
    format: "%H:%M"
  # Sunset
  - platform: sun
    type: sunset
    id: sun_sunset
    format: "%H:%M"
  # Visibility range from Met Office
  - platform: homeassistant
    entity_id: sensor.dundonald_visibility_distance_daily
    id: visibility_range
  # Moon phase
  - platform: homeassistant
    entity_id: sensor.moon
    id: moon_phase
  - platform: homeassistant
    entity_id: sensor.moon_rise
    id: moon_rise
  - platform: homeassistant
    entity_id: sensor.moon_set
    id: moon_set


# Define colors
# This design is white on black so this is necessary.
color:
  - id: color_black
    red: 0%
    green: 0%
    blue: 0%
    white: 50%
  - id: color_white
    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
    cs_pin: GPIO15
    dc_pin: GPIO27
    busy_pin: GPIO25
    reset_pin: GPIO26
    model: 7.50inV2
    update_interval: 60s
    rotation: 90°
    auto_clear_enabled: false
    lambda: |-
      // Map weather states to MDI characters.
      std::map<std::string, std::string> visibility_short
        {
          {"20-40", "30"},
          {"20-30", "25"},
          {"10-20", "15"},
        };
      std::map<std::string, std::string> moon_icon_map
        {
          {"first_quarter", "\U000F0F61"},
          {"full_moon", "\U000F0F62"},
          {"last_quarter", "\U000F0F63"},
          {"new_moon", "\U000F0F64"},
          {"waning_crescent", "\U000F0F65"},
          {"waning_gibbous", "\U000F0F66"},
          {"waning_crescent", "\U000F0F67"},
          {"waxing_gibbous", "\U000F0F68"},
          
        };
      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 in black.
      it.fill(color_white);

      // ----------------------------------------------------------------------------------- Print full weekday name
      it.strftime(340, 95, id(font_weekday), TextAlign::TOP_RIGHT, "%A", id(esptime).now());
      // ----------------------------------------------------------------------------------- Print time in HH:MM format 335
      it.strftime(340, 200, id(font_time), TextAlign::BASELINE_RIGHT, "%H:%M", id(esptime).now());
      // TEST it.printf(315, 200, id(font_time), TextAlign::BASELINE_RIGHT, "88:88");
      
      // ----------------------------------------------------------------------------------- Print day of the month
      it.strftime(440, 95, id(font_day), TextAlign::TOP_RIGHT, "%d", id(esptime).now());
      // ----------------------------------------------------------------------------------- Print abbreviated month name
      it.strftime(440, 200, id(font_month), TextAlign::BASELINE_RIGHT, "%b", id(esptime).now());
      // TEST it.printf(440, 200, id(font_month), TextAlign::BASELINE_RIGHT, "AAA");

      // ----------------------------------------------------------------------------------- WiFi
      if(id(wifisignal).has_state ()) {
        if (id(wifisignal).state >= -50) {
            // Excellent # mdi-wifi-strength-4 "\U000F0928" 255, 230
            it.printf(85, 487, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "\U000F0928");
        } else if (id(wifisignal).state  >= -60) {
            //Good # mdi-wifi-strength-3 "\U000F0925"
            it.printf(85, 487, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "\U000F0925");
        } else if (id(wifisignal).state  >= -67) {
            //Fair # mdi-wifi-strength-2 "\U000F0922"
            it.printf(85, 487, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "\U000F0922");
        } else if (id(wifisignal).state  >= -70) {
            //Weak # mdi-wifi-strength-1 "\U000F091F"
            it.printf(85, 487, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "\U000F091F");
        } else {
            //Unlikely working signal # mdi-wifi-strength-alert-outline "\U000F092B"
            it.printf(85, 487, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "\U000F092B");
        }
      }
      // ----------------------------------------------------------------------------------- LOW BATTERY
        if(id(battery_voltage).has_state ()) {
            if(id(battery_voltage).state <= 2700) {
                // Replace battery, less than 10% 420, 558 / 87
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F10CD");
            } else if (id(battery_percent).state == 100) {
                // "\U000F0079" # mdi-battery
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F0079");
            } else if (id(battery_percent).state >= 90) {
                // "\U000F0082" # mdi-battery-90
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F0082");
            } else if (id(battery_percent).state >= 80) {
                // "\U000F0081" # mdi-battery-80
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F0081");
            } else if (id(battery_percent).state >= 70) {
                // "\U000F0080" # mdi-battery-70
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F0080");
            } else if (id(battery_percent).state >= 60) {
                // "\U000F007F" # mdi-battery-60
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F007F");
            } else if (id(battery_percent).state >= 50) {
                // "\U000F007E" # mdi-battery-50
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F007E");
            } else if (id(battery_percent).state >= 40) {
                // "\U000F007D" # mdi-battery-40
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F007D");
            } else if (id(battery_percent).state >= 30) {
                // "\U000F007C" # mdi-battery-30
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F007C");
            } else if (id(battery_percent).state >= 20) {
                // "\U000F007B" # mdi-battery-20
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F007B");
            } else if (id(battery_percent).state >= 10) {
                // "\U000F007A" # mdi-battery-10
                it.printf(85, 567, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F007A");
            }
        }
      // ----------------------------------------------------------------------------------- Print sunrise
        if(id(sun_sunrise).has_state ()) {
            it.printf(100, 235, id(font_mdi_medium), color_black, TextAlign::CENTER_RIGHT, "\U000F059C");
            it.printf(105, 235, id(font_small_bold), color_black, TextAlign::CENTER_LEFT, "%s", id(sun_sunrise).state.c_str());
        }      
      // ----------------------------------------------------------------------------------- Print sunset
        if(id(sun_sunset).has_state ()) {
            it.printf(455, 235, id(font_mdi_medium), color_black, TextAlign::CENTER_RIGHT, "\U000F059B");
            it.printf(415, 235, id(font_small_bold), color_black, TextAlign::CENTER_RIGHT, "%s", id(sun_sunset).state.c_str());
        }
      // ----------------------------------------------------------------------------------- Print moonrise
        if(id(moon_rise).has_state ()) {
            it.printf(155, 260, id(font_mdi_medium), color_black, TextAlign::CENTER_RIGHT, "\U000F1A0A");
            it.printf(155, 260, id(font_small_bold), color_black, TextAlign::CENTER_LEFT, "%s", id(moon_rise).state.c_str());
        }      
      // ----------------------------------------------------------------------------------- Print moonset
        if(id(moon_set).has_state ()) {
            it.printf(400, 260, id(font_mdi_medium), color_black, TextAlign::CENTER_RIGHT, "\U000F1A09");
            it.printf(365, 260, id(font_small_bold), color_black, TextAlign::CENTER_RIGHT, "%s", id(moon_set).state.c_str());
        }
      // Weather Section ------------------------------------------------------------------- PLACE NAME
        // TEST it.image(20, 338, id(title_weather));
        it.print(265, 280, id(font_weekday), color_black, TextAlign::TOP_CENTER, "DUNDONALD");

      // ----------------------------------------------------------------------------------- WEATHER STATE ICON --------------- (110, 358)
        it.printf(245, 335, id(font_mdi_large), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str());
      // TEST it.printf(245, 340, id(font_mdi_large), color_black, TextAlign::TOP_CENTER, "\U000F0F35");
      
      // ----------------------------------------------------------------------------------- MOON PHASE ICON ------------------
        if(id(moon_phase).has_state ()) {
            it.printf(265, 243, id(font_mdi_medlarge), color_black, TextAlign::CENTER, "%s", moon_icon_map[id(moon_phase).state.c_str()].c_str());
        }     
      // ----------------------------------------------------------------------------------- HUMIDITY
        if(id(weather_humidity).has_state ()) {
            it.printf(100, 352, id(font_mdi_medium), color_black, TextAlign::BASELINE_RIGHT, "\U000F058E");
            it.printf(145, 350, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "%s", "%");
            it.printf(140, 350, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "%2.0f", id(weather_humidity).state);
        }      
      // ----------------------------------------------------------------------------------- VISIBILITY
        if(id(visibility_range).has_state ()) {
            it.printf(100, 400, id(font_mdi_medium), color_black, TextAlign::BASELINE_RIGHT, "\U000F00A5");
            it.printf(140, 400, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "%s", visibility_short[id(visibility_range).state.c_str()].c_str());
            it.printf(145, 400, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "km");
        }
      // ----------------------------------------------------------------------------------- UV INDEX
        if(id(uv_index).has_state ()) {
            it.printf(100, 454, id(font_mdi_medium), color_black, TextAlign::BASELINE_RIGHT, "\U000F0F37");
            it.printf(140, 450, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "%2.0f", id(uv_index).state);
            it.printf(145, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "UVI");
        }
      // ----------------------------------------------------------------------------------- PRESSURE
        if(id(weather_pressure).has_state ()) {
            // pressure trend
            if(id(pressure_trend).state == "on") {
                it.printf(325, 353, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F19B4");
            } else if(id(pressure_trend).state == "off") {
                it.printf(325, 352, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F19B7");
            }
            it.printf(410, 350, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "%2.0f", id(weather_pressure).state);
            it.printf(415, 350, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "hPa");
        }
        // TEST it.printf(410, 340, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "1888");
      // ----------------------------------------------------------------------------------- WIND
        if(id(wind_speed).has_state ()) {
            it.printf(330, 400, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F15FA");
            it.printf(410, 400, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "%2.0f", id(wind_speed).state);
            it.printf(415, 400, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "kmh");
            if(id(wind_warning).state > 20) {
                it.printf(415, 485, id(font_mdi_medium), color_black, TextAlign::TOP_LEFT, "\U000F15FA");
            }
        }
        // TEST it.printf(420, 390, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "188");
      // ----------------------------------------------------------------------------------- WIND DIRECTION & BEARING
        if(id(wind_bearing).has_state ()) {
            it.printf(330, 454, id(font_mdi_medium), color_black, TextAlign::BASELINE_CENTER, "\U000F018C");
            it.printf(410, 450, id(font_weekday), color_black, TextAlign::BASELINE_RIGHT, "%2.0f", id(wind_bearing).state);
            if (id(wind_bearing).state > 337.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "N");
            } else if(id(wind_bearing). state > 292.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "NW");
            } else if(id(wind_bearing). state > 247.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "W");
            } else if(id(wind_bearing). state > 202.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "SW");
            } else if(id(wind_bearing). state > 157.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "S");
            } else if(id(wind_bearing). state > 112.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "SE");
            } else if(id(wind_bearing). state > 67.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "E");
            } else if(id(wind_bearing). state > 22.5) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "NE");
            } else if(id(wind_bearing). state >= 0) {
                it.printf(415, 450, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "N");
            }
        }
      // ----------------------------------------------------------------------------------- Frost Warning
        if(id(frost_risk).has_state ()) {
            if(id(frost_risk).state != "no_risk") {
                it.printf(415, 565, id(font_mdi_medium), color_black, TextAlign::BASELINE_LEFT, "\U000F0F29");
            }
        }
      // ----------------------------------------------------------------------------------- TEMPERATURE ---------------------- (310, 458)
        if(id(weather_temperature).has_state ()) {
            it.printf(410, 480, id(font_large_bold), color_black, TextAlign::TOP_RIGHT, "%2.0f°C", id(weather_temperature).state);
            // TEST it.printf(410, 480, id(font_large_bold), color_black, TextAlign::TOP_RIGHT, "%s", "88°C");
        }
        if(id(temp_trend).has_state ()) {
            if(id(temp_trend).state == "on") {
                it.printf(123, 527, id(font_mdi_medium), color_black, TextAlign::CENTER, "\U000F0E03");
            } else if(id(temp_trend).state == "off") {
                it.printf(123, 527, id(font_mdi_medium), color_black, TextAlign::CENTER, "\U000F0E02");
            }
        }
      
        if(id(weather_temperature_0).has_state ()) {
            it.printf(90, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_0).state.c_str());
            it.printf(90, 626, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_0).state.c_str()].c_str());
            it.printf(90, 674, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_0).state);
        }
        
        if(id(weather_temperature_1).has_state ()) {
            it.printf(200, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_1).state.c_str());
            it.printf(200, 626, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_1).state.c_str()].c_str());
            it.printf(200, 674, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_1).state);
        }
        
        if(id(weather_temperature_2).has_state ()) {
            it.printf(315, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_2).state.c_str());
            it.printf(315, 626, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_2).state.c_str()].c_str());
            it.printf(315, 674, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_2).state);
        }
        
        if(id(weather_temperature_3).has_state ()) {
            it.printf(425, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_3).state.c_str());
            it.printf(425, 626, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_3).state.c_str()].c_str());
            it.printf(425, 674, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_3).state);
        }
      
captive_portal:


Template entities:

  - sensor:
      - name: "Moon Rise"
        icon: "mdi:triangle-small-up"
        state: >-
          {% set x = states('sensor.astroweather_moon_next_rising') %}
          
          {{ x[11:16] }}
  - sensor:
      - name: "Moon Set"
        icon: "mdi:triangle-small-down"
        state: >-
          {% set x = states('sensor.astroweather_moon_next_setting') %}
          
          {{ x[11:16] }}

The above comes from Astroweater integration available to install in HACS:

Wind warning is based on my custom weather entity, by changing [1] to other number you can chose how soon you will see the warning.

  - sensor:
      - name: "Wind Warning"
        icon: "mdi:weather-windy"
        unit_of_measurement: "km/h"
        state: "{{state_attr('weather.my_weather_station', 'forecast')[1]['wind_speed']}}"

UV index sensor comes with MET Office integration. I also need to change source for the wind speed . I just noticed Met Office provides separate sensors in its integration, but if I remember correctly these are disabled by default. You can easily be change the identity name in the code once activated.

Temp trend:

  - platform: trend
    sensors:
      outside_temperature:
        entity_id: sensor.zigbeeweather_temperature
        friendly_name: "Temperature Trend"
        max_samples: 60
        sample_duration: 10800

Pressure trend:

  - platform: trend
    sensors:
      atmospheric_pressure:
        entity_id: sensor.zigbeeweather_pressure
        friendly_name: "Pressure Trend"
        max_samples: 60
        sample_duration: 21600

Lastly, frost risk is based on your current temperature, frost point and absolute humidit, quite reliable. Easily available through Thermal Comfort custom integration (HACS):

If I missed something, let me know. Hope it works without any issues.

9 Likes

This is awesome, I actually picked up the hardware months ago and am just now getting round to doing the build.

I’m pretty new to this level of ESPHome, What I want to do is get calendars lists to display. essentially to do lists. But I’m not getting anything showing up yet.

Is this enough to just get the titles of two of the lists to display?

I get no errors in the logs, but im not even getting

WAITING FOR DATA

Should the screen flash or anything on boot?

globals:
  - id: data_updated
    type: bool
    restore_value: no
    initial_value: 'false'
  - id: initial_data_received
    type: bool
    restore_value: no
    initial_value: 'false'

# Include custom fonts
font:
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_small_book
    size: 18
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_large_bold
    size: 108
    glyphs: [' ', '°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_title
    size: 54
    glyphs: ['W', 'E', 'A', 'T', 'H', 'R', 'L', 'I', 'N', ' ']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_medium_bold
    size: 30
    # glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'M', 'I', 'N']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_small_bold
    size: 18
    # glyphs: ['°', '0', '1', +'2', '3', '4', '5', '6', '7', '8', '9', 'C', 'M', 'I', 'N']


# Check whether the display needs to be refreshed every minute,
# based on whether new data is received or motion is detected. (Thanks @paviro!)
time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      - seconds: 0
        minutes: /1
        then:
          - if:
              condition:
                lambda: 'return id(data_updated) == true;'
              then:
                - lambda: 'id(initial_data_received) = true;'
                - if:
                    condition:
                      binary_sensor.is_on: motion_detected
                    then:
                      - logger.log: "Sensor data updated and activity in home detected: Refreshing display..."
                - component.update: eink_display
                - lambda: 'id(data_updated) = false;'
              else:
                      - logger.log: "Sensor data updated but no activity in home - skipping display refresh."



# Check if motion is detected in the bathroom.
binary_sensor:
  - platform: homeassistant
    entity_id: binary_sensor.bathroom_motion_sensor
    id: motion_detected    

# Call calender sensors from HA.
sensor:
  - platform: homeassistant
    entity_id: calendar.home_assistant_tasks
    id: home_assistant_tasks
    on_value:
      then:
         - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: calendar.alexa_to_do_list
    id: alexa_to_do_list
    on_value:
      then:
         - lambda: 'id(data_updated) = true;'
    
  - platform: homeassistant
    entity_id: calendar.alexa_shopping_list
    id: alexa_shopping_list
    on_value:
      then:
         - lambda: 'id(data_updated) = true;'

# Define colors
# This design is white on black so this is necessary.
color:
  - id: color_bg
    red: 0%
    green: 0%
    blue: 0%
    white: 0%
  - id: color_text
    red: 0%
    green: 0%
    blue: 0%
    white: 100%


# 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: GPIO25
    reset_pin: GPIO26
    reset_duration: 2ms
    model: 7.50inV2
    update_interval: never
    rotation: 90°
    lambda: |-


      // 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 {

        // To Do List
        it.printf(240, 84, id(font_title), color_text, TextAlign::TOP_CENTER, "To Do");


        // Shopping List Section
        it.printf(240, 408, id(font_title), color_text, TextAlign::TOP_CENTER, "Shopping List");




      }


  

captive_portal:

1 Like

So been trying to tobbleshoot this and so far nothing.

ESP32 appears to be working.

Does the display flash when it gets powered?
Does ESPHome log writes to the display? (I get no info in the logs)

I also stripped everything out of the yaml

font:
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_small_book
    size: 18

  - file: "gfonts://Roboto"
    id: roboto
    size: 20

# 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: GPIO25
    reset_pin: GPIO26
    reset_duration: 2ms
    model: 7.50inV2
    update_interval: never
    rotation: 90°
    lambda: |-
      it.print(0, 0, id(roboto), "Hello World!");

Still nothing, can anyone see anything wrong here?

Thanks

Changed the update intival,

[13:09:32][D][esp32.preferences:113]: Saving 1 preferences to flash...
[13:09:32][D][esp32.preferences:142]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed

So is this working as far as the ESP32 is concerned?

EDIT;

OK, I think I got it working. Was the display select on the board itself :slight_smile:

Ok, so have gotten somewhere.

But the display is really bad. I seen people kind of allude to quality issues. But how did you fix it?

After turning it on, got this

Even though I thought it would be black text on white

Restarted again and now its sitting here

If anyone can give me any advise, would really appreciate it :slight_smile:

# Global variables for detecting if the display needs to be refreshed. (Thanks @paviro!)
globals:
  - id: data_updated
    type: bool
    restore_value: no
    initial_value: 'false'
  - id: initial_data_received
    type: bool
    restore_value: no
    initial_value: 'false'

# Include custom fonts
font:
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_small_book
    size: 18
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_large_bold
    size: 108
    #glyphs: [' ', '°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_title
    size: 54
    #glyphs: ['W', 'E', 'A', 'T', 'H', 'R', 'L', 'I', 'N', ' ']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_medium_bold
    size: 30
    # glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'M', 'I', 'N']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_small_bold
    size: 18
    # glyphs: ['°', '0', '1', +'2', '3', '4', '5', '6', '7', '8', '9', 'C', 'M', 'I', 'N']


# Check whether the display needs to be refreshed every minute,
# based on whether new data is received or motion is detected. (Thanks @paviro!)
time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      - seconds: 0
        minutes: /1
        then:
          - if:
              condition:
                lambda: 'return id(data_updated) == true;'
              then:
                - lambda: 'id(initial_data_received) = true;'
                - if:
                    condition:
                      binary_sensor.is_on: motion_detected
                    then:
                      - logger.log: "Sensor data updated and activity in home detected: Refreshing display..."
                - component.update: eink_display
                - lambda: 'id(data_updated) = false;'
              else:
                      - logger.log: "Sensor data updated but no activity in home - skipping display refresh."




# Check if motion is detected in the bathroom.
binary_sensor:
  - platform: homeassistant
    entity_id: binary_sensor.bathroom_motion_sensor
    id: motion_detected    

# Call calender sensors from HA.
sensor:
  - platform: homeassistant
    entity_id: calendar.home_assistant_tasks
    id: home_assistant_tasks
    on_value:
      then:
         - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: calendar.alexa_to_do_list
    id: alexa_to_do_list
    on_value:
      then:
         - lambda: 'id(data_updated) = true;'
    
  - platform: homeassistant
    entity_id: calendar.alexa_shopping_list
    id: alexa_shopping_list
    on_value:
      then:
         - lambda: 'id(data_updated) = true;'

# Define colors
# This design is white on black so this is necessary.
color:
  - id: color_black
    red: 0%
    green: 0%
    blue: 0%
    white: 50%
  - id: color_white
    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: GPIO25
    reset_pin: GPIO26
    reset_duration: 2ms
    model: 7.50inV2
    update_interval: 1h
    rotation: 90°
    lambda: |-


      // 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_title), color_black, TextAlign::TOP_CENTER, "WAITING FOR DATA...");
      } else {

        // To Do List
        it.printf(240, 84, id(font_title), color_black, TextAlign::TOP_CENTER, "To Do");


        // Shopping List Section
        it.printf(240, 408, id(font_title), color_black, TextAlign::TOP_CENTER, "Shopping List");




      }


  

captive_portal:

I had something similar and discovered I had a three colour panel and after some googling found that someone had added support via an external component (YMMV)

1 Like

Thanks for that, I knew I had the three colour one. I just didn’t know it wasn’t supported LOL

Do you think it safe? *he asks stranger on the internet" HAHA

But really thank you, had no idea that would be an issue :slight_smile:

HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
Dependency Graph
|-- AsyncTCP-esphome @ 1.2.2
|-- WiFi @ 1.0
|-- FS @ 1.0
|-- Update @ 1.0
|-- ESPAsyncWebServer-esphome @ 2.1.0
|   |-- AsyncTCP-esphome @ 1.2.2
|-- DNSServer @ 1.1.0
|-- ESPmDNS @ 1.0
|-- noise-c @ 0.1.4
|   |-- libsodium @ 1.10018.1
|-- SPI @ 1.0
Compiling /data/to-do/.pioenvs/to-do/src/main.cpp.o
src/main.cpp: In function 'void setup()':
src/main.cpp:3702:63: error: invalid new-expression of abstract class type 'esphome::waveshare_epaper::WaveshareEPaper7P5InV2'
   eink_display = new waveshare_epaper::WaveshareEPaper7P5InV2();
                                                               ^
In file included from src/esphome.h:50:0,
                 from src/main.cpp:3:
src/esphome/components/waveshare_epaper/waveshare_epaper.h:312:7: note:   because the following virtual functions are pure within 'esphome::waveshare_epaper::WaveshareEPaper7P5InV2':
 class WaveshareEPaper7P5InV2 : public WaveshareEPaper {
       ^
In file included from src/esphome.h:20:0,
                 from src/main.cpp:3:
src/esphome/components/display/display_buffer.h:373:23: note: 	virtual esphome::display::DisplayType esphome::display::DisplayBuffer::get_display_type()
   virtual DisplayType get_display_type() = 0;
                       ^
*** [/data/to-do/.pioenvs/to-do/src/main.cpp.o] Error 1
========================== [FAILED] Took 5.83 seconds ==========================

Didnt like it, will open a FR with ESPHome :slight_smile:

EDIT

Actually, I have the two colour I think. Lucky I checked

Waveshare 7.5inch E-Paper (B) E-Ink Raw Display 800×480 Pixels Red/Black/White Three-Color with SPI Interface Without PCB >170°Wide Viewing Angle Paper-Like Displaying Without Electricity

I just remember seeing “three colour” lol

7.50in B-type (red-black-white) I asked in one the related github issues if its supported. See if someone responds :slight_smile:

1 Like

How did you set it? A and ON? I have the same problem I think

I’m at work now, so cant check. But initially I did have it in the wrong position. It kinda works now. But its not great. Perhaps because the one I have has 2 colors in it.

Hi, I’m using part of your code to make a board, but I found some points where I get errors (maybe it’s me that I’m not the best at coding).

On the moon time and phase, I defined the sensor templates in HA, but when I compile the ESP32 I get these errors:

/config/eink-frame-1.yaml:356:110: error: request for member 'c_str' in 'moon_rise->esphome::homeassistant::HomeassistantSensor::<anonymous>.esphome::sensor::Sensor::state', which is of non-class type 'float'
             it.printf(155, 260, id(font_small_bold), color_black, TextAlign::CENTER_LEFT, "%s", id(moon_rise).state.c_str());
                                                                                                              ^~~~~
/config/eink-frame-1.yaml:361:110: error: request for member 'c_str' in 'moon_set->esphome::homeassistant::HomeassistantSensor::<anonymous>.esphome::sensor::Sensor::state', which is of non-class type 'float'
             it.printf(365, 260, id(font_small_bold), color_black, TextAlign::CENTER_RIGHT, "%s", id(moon_set).state.c_str());
                                                                                                              ^~~~~
/config/eink-frame-1.yaml:365:122: error: request for member 'c_str' in 'moon_phase->esphome::homeassistant::HomeassistantSensor::<anonymous>.esphome::sensor::Sensor::state', which is of non-class type 'float'
             it.printf(265, 243, id(font_mdi_medlarge), color_black, TextAlign::CENTER, "%s", moon_icon_map[id(moon_phase).state.c_str()].c_str());
                                                                                                                          ^~~~~

Also the moon phase is identified with a percentage, how does it maps to

      std::map<std::string, std::string> moon_icon_map

since there are just names? Where is defined the correspondence?

Regarding the weather, what is sensor.weatherman_data source?

  # # Weatherman entities
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_now
    id: weather_condition_now

Thank you for any help you can give :grinning:

1 Like

Hi, unfortunately, my display died prematurely over Christmas, but I will try to help you as best I can, and as simply as possible (I am no expert either :wink: )

it.printf(155, 260, id(font_small_bold), color_black, TextAlign::CENTER_LEFT, "%s", id(moon_rise).state.c_str());
it.printf(365, 260, id(font_small_bold), color_black, TextAlign::CENTER_RIGHT, "%s", id(moon_set).state.c_str());
it.printf(265, 243, id(font_mdi_medlarge), color_black, TextAlign::CENTER, "%s", moon_icon_map[id(moon_phase).state.c_str()].c_str());

First, double check in Home Assistant in Dev tools if you have these sensors and if you can see their states:

sensor.moon
sensor.moon_rise
sensor.moon_set

If there is a problem with any of them, go to Settings / Devices / Integrations and check if you have Moon integration listed for the moon phase, and AstroWeather for the moon rise and moon set. Moon is a native integration, while AstroWeather can be installed via HACS, for which I provided a link previously.

The first sensor reports only the moon phase as a string (text) and not as a percentage. It seems to me you might be using some other sensor for this, hence the issue (possibly moon phase sensor from AstroWeather (???)).

Sensor ‘weatherman_data’ groups information from other weather / sensor entities into one sensor for the ease of use.

The code below is from the original project, I only modified it slightly to use the data from my own weather station and the forecast from Yr.no.

# Bundle up all the data to send over to Weatherman.
  - sensor:
      - name: "Weatherman Data"
        state: "OK"
        attributes:
          weather_condition_now: >
            {% set cond_now = states('weather.my_weather_station') %}
            {% if states('sun.sun') == 'below_horizon' %}
                {% if cond_now == 'sunny' %} night {% elif cond_now == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond_now }} {% endif %}
            {% else %}
                {{ cond_now }}
            {% endif %}
            
          weather_condition_0: >
            {% set cond0 = state_attr('weather.home_hourly', 'forecast')[0].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond0_time = as_timestamp(state_attr('weather.home_hourly', 'forecast')[0].datetime) %}
            {% if cond0_time > next_setting and cond0_time < next_rising %}
                {% if cond0 == 'sunny' %} night {% elif cond0 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond0 }} {% endif %}
            {% else %}
                {{ cond0 }}
            {% endif %}
          weather_temperature_0: >
            {{ state_attr('weather.home_hourly', 'forecast')[0].temperature | round }}
          weather_timestamp_0: >
            {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[0].datetime) | timestamp_custom('%I') | int }} {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[0].datetime) | timestamp_custom('%p') }}
            
          weather_condition_1: >
            {% set cond1 = state_attr('weather.home_hourly', 'forecast')[1].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond1_time = as_timestamp(state_attr('weather.home_hourly', 'forecast')[1].datetime) %}
            {% if cond1_time > next_setting and cond1_time < next_rising %}
                {% if cond1 == 'sunny' %} night {% elif cond1 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond1 }} {% endif %}
            {% else %}
                {{ cond1 }}
            {% endif %}
          weather_temperature_1: >
            {{ state_attr('weather.home_hourly', 'forecast')[1].temperature | round }}
          weather_timestamp_1: >
            {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[1].datetime) | timestamp_custom('%I') | int }} {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[1].datetime) | timestamp_custom('%p') }}
            
          weather_condition_2: >
            {% set cond2 = state_attr('weather.home_hourly', 'forecast')[2].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond2_time = as_timestamp(state_attr('weather.home_hourly', 'forecast')[2].datetime) %}
            {% if cond2_time > next_setting and cond2_time < next_rising %}
                {% if cond2 == 'sunny' %} night {% elif cond2 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond2 }} {% endif %}
            {% else %}
                {{ cond2 }}
            {% endif %}
          weather_temperature_2: >
            {{ state_attr('weather.home_hourly', 'forecast')[2].temperature | round }}
          weather_timestamp_2: >
            {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[2].datetime) | timestamp_custom('%I') | int }} {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[2].datetime) | timestamp_custom('%p') }}
            
          weather_condition_3: >
            {% set cond3 = state_attr('weather.home_hourly', 'forecast')[3].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond3_time = as_timestamp(state_attr('weather.home_hourly', 'forecast')[3].datetime) %}
            {% if cond3_time > next_setting and cond3_time < next_rising %}
                {% if cond3 == 'sunny' %} night {% elif cond3 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond3 }} {% endif %}
            {% else %}
                {{ cond3 }}
            {% endif %}
          weather_temperature_3: >
            {{ state_attr('weather.home_hourly', 'forecast')[3].temperature | round }}
          weather_timestamp_3: >
            {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[3].datetime) | timestamp_custom('%I') | int }} {{ as_timestamp(state_attr('weather.home_hourly', 'forecast')[3].datetime) | timestamp_custom('%p') }}

Hope this all helps.

1 Like

Thank you for your reply. I fixed some things missing (moon sensor), but I still get errors on the same spot like if the return is not of correct type.
I checked in HA and I can see correctly the entities and attributes

/config/eink-frame-1.yaml:308:133: error: request for member 'c_str' in 'weather_condition_now->esphome::homeassistant::HomeassistantSensor::<anonymous>.esphome::sensor::Sensor::state', which is of non-class type 'float'
         it.printf(245, 335, id(font_mdi_large), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str());
                                                                                                                                     ^~~~~

Are you aware of any documentation on how to construct these instructions?

Thanks

Hmm…

it.printf(245, 335, id(font_mdi_large), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str());

The code above only displays the weather state icon, which is assigned in the code below. I would check formatting i.e. missing quotation here:

      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"},
        };

The only documentation I used was the one from ESPHome and from the original project, plus a lot of trial and error until I figured it out.

Hi,
You said “connect the e-Paper screen to the driver board” does the screen come with the cable or does the driver board come with the cable or do I have to buy the cable seperatly ?

Amazing project !

Mine arrived with it.

Yes, you need to buy the driver board (and sometimes known as a HAT) in order for the Raspberry Pi to control the e-ink screen.

Btw, I don’t know if anyone had tried before, does the e-ink screen of one brand work with the driver HAT of another? I’d love to hear it from the e-ink screen experts here.

Hey folks,

I got some big updates for the project with the help of our community. Thanks @paviro, @pehses, and @danito for your contributions!

New Features

Intelligent Screen Refreshing

To reduce the frequency of screen refreshes and to prolong the life of the e-ink screen, the screen will now only refresh when motion (or any template criteria) sensor binary_sensor.weatherman_motion_detected is on. I have it set up so that it wakes up when there are any motions from my sensors in the living room.

Last Refreshed Timestamp

The last update timestamp can be displayed on the screen itself. No more wondering when the screen was last refreshed!

Remote Control and Monitoring

The screen can now be controlled and monitored remotely. The screen can be either refreshed manually, restarted, or safely shut down through buttons in your HA. The number of screen refreshes in its lifetime as well as its last update timestamp and wifi signal strength can also be monitored.

Other changes:

  • Negative temperatures can now be displayed. Perfect for winter.
  • A nice loading screen is shown before any data is received.
  • Titles are now in text rather than bitmaps. Much easier to change it to anything you like.
6 Likes

Out of interest, how many refreshes can these eink displays handle?

I’d like to use them to show my wife’s glucose levels from her Dexcom so she doesn’t need to keep looking at her phone.

The issue with this though is the refresh rate, as information like that needs to be kept up to date in a realtime bases (I think the Dexcom integration actually updates every few minutes)

1 Like

I want to thank everyone for sharing their examples, code and pictures. Finished my setup today.

9 Likes