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

As a tribute to this great project herewith my version to inspire others.

4 Likes

I am not sure about easier examples, but I suggest you to do few elements at a time and figure out little by little. When it’s ok you add another part of code

Nise project. Have orderd the components and started working on my sensors in HA, but I’m struggeling to validate my configuration.

In configuration.yaml i got: sensor: !include_dir_list sensors/

and in folder sensors i got: Weatherman_motion.yaml

The content of the file is:

platform: template
sensors:
  binary_sensor:
  name: "Weatherman Motion Detected"
  unique_id: "dfa78de7-d761-425f-9731-86f1af332eac"
  device_class: "occupancy"
  delay_off: 1min
  state: >-
      {% if states('binary_sensor.motion_sensor_home_security_motion_detection_3') == 'on' or states('binary_sensor.lumi_lumi_sensor_magnet_73cfb702_on_off') == 'on' %}
        on
      {% else -%}
        off
      {% endif -%}

The template is working, but something is wrong in my format?

When i push test configuration it just keep spinning.

Kind of frusterating that i can not figure out the error…

Same structure and “error” on the weather template sensors…

if it’s a template, shouldn’t it be something like:

template: !include configurations/general/template.yaml

and then in the file something like:

  - sensor:
    - name: potenza_istantanea_totale_PT_PI
      unit_of_measurement: 'W'
      state: '{{ states("sensor.shelly_shem_bcff4dfcd7bf_1_current_consumption") |float + states("sensor.shelly_shem_bcff4dfce165_1_current_consumption") | float }}'

I have 2 of these working, and since the update of ESPHome to properly support the V7,5altV2, the quality has improved a lot, no more greasy pictures.

But I DO have a kind of “burn in”-effect. Of course, this does not exactly exist on an ePaper, but you know what I mean.

Any way to really clean the screen?

This is my config:

display:
  - platform: waveshare_epaper
    cs_pin: GPIO15
    dc_pin: GPIO27
    busy_pin: GPIO25
    reset_pin: GPIO26
    reset_duration: 2ms
    model: 7.50inV2alt
    update_interval: 60s

what is the effect? Can you share a picture?
Have you tried with
auto_clear_enabled: True
?


Didnt know about autoclear and will give it a try.

Autoclear does not do anything for me (isn’t it defaulted to “true” if not set?).
So I tried adding a routine that fills the screen black and then white for a minute every 24 hours. Below is my code, but it doesn’t work as expected; I am getting just a white screen after reboot and the code never seems to continue. What am I doing wrong?

display:
  lambda: |-
    if (millis() % 86400000 < 120000) {
      // Within the first 2 minutes of the day
      epaper_display->fill(esphome::Color(0));  
      epaper_display->display();  // Display the black screen
      delay(60000);  // 1 minute delay in milliseconds
      epaper_display->fill(esphome::Color(1));  
      epaper_display->display();  
      delay(60000);  
    } else {
   .....Rest of my normal display routine....

I had to specify it otherwise it didn’t properly clear my screen
My code is the follwing and I don’t have your problem

display:
  - platform: waveshare_epaper
    cs_pin: GPIO15
    dc_pin: GPIO27
    busy_pin: GPIO25
    reset_pin: GPIO26
    model: 7.50inV2alt
    update_interval: 1200s
    rotation: 90°
    auto_clear_enabled: True
    lambda: |-

The only difference is the
reset_duration: 2ms
that I haven’t set

1 Like

The new ESPHome 2023.12.0 - 20th December 2023 seems to break the shutdown, restart och refresh functions. I can´t find what causes this, anyone has any ideea?

# Creates buttons in Home Assistant to
button:
  - platform: shutdown
    name: "Weatherman - Shutdown"
  - platform: restart
    name: "Weatherman - Restart"
  - platform: template
    name: "Weatherman - Refresh Screen"
    entity_category: config
    on_press:
      - script.execute: update_screen

image

Chceck if ids were not renamed. Some of mine were renamed by adding underscore and number to the ids, so the old ones were not active.

I’m not sure at what point ESPhome broke this as I haven’t had to update the display in many, many months (it’s actually still running on the old FW) but now it’s so far out of date I copied the ESPhome data to a new file so that I can avoid breaking things while testing to compile.

However I am stuck, I do not understand why it will not compile. Below is the error and my ESPhome yaml, any help would be appreciated!

INFO ESPHome 2023.12.1
INFO Reading configuration /config/esphome/frontdoor-epaper1.yaml...
INFO Detected timezone 'Canada/Eastern'
WARNING GPIO15 is a strapping PIN and should only be used for I/O with care.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
INFO Generating C++ source...
Traceback (most recent call last):
  File "/usr/local/bin/esphome", line 33, in <module>
    sys.exit(load_entry_point('esphome', 'console_scripts', 'esphome')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 1041, in main
    return run_esphome(sys.argv)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 1028, in run_esphome
    rc = POST_CONFIG_ACTIONS[args.command](args, config)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 407, in command_compile
    exit_code = write_cpp(config)
                ^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 192, in write_cpp
    generate_cpp_contents(config)
  File "/esphome/esphome/__main__.py", line 204, in generate_cpp_contents
    CORE.flush_tasks()
  File "/esphome/esphome/core/__init__.py", line 679, in flush_tasks
    self.event_loop.flush_tasks()
  File "/esphome/esphome/coroutine.py", line 246, in flush_tasks
    next(task.iterator)
  File "/esphome/esphome/__main__.py", line 184, in wrapped
    await coro(conf)
  File "/esphome/esphome/coroutine.py", line 80, in coro
    ret = yield from _flatten_generator(gen)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/coroutine.py", line 132, in _flatten_generator
    to_send = yield from val.__await__()
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/components/display/__init__.py", line 119, in register_display
    await cg.register_component(var, config)
  File "/esphome/esphome/cpp_helpers.py", line 56, in register_component
    raise ValueError(
ValueError: Component ID einkdisplay was not declared to inherit from Component, or was registered twice. Please create a bug report with your configuration.

sorry had to split my post and slightly shorten the code at the bottom (too long):

ESPhome yaml:

# 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: "frontdoor-epaper-1"
  on_boot:
      priority: 200.0
      then:
        - component.update: einkdisplay
        - 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

external_components:
  - source:
      type: git
      url: https://github.com/atomicmike/esphome.git
      ref: waveshare-color-2023.10
    components: [ waveshare_epaper ]

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: !secret base64_key

ota:
  password: !secret esphome_ota_pass

wifi:
  ssid: !secret iot_wifi_ssid
  password: !secret iot_wifi_pass
   
button:
  - platform: shutdown
    name: "Front Door eInk1 - Shutdown"
  - platform: restart
    name: "Front Door eInk1 - Restart"
  - platform: template
    name: "Front Door eInk1 - Refresh Screen"
    entity_category: config
    on_press:
      - script.execute: update_screen


# Check if motion is detected or front door opened
binary_sensor:
  - platform: homeassistant
    entity_id: binary_sensor.weatherman_motion_detected
    id: motion_detected

# 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'
  - 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: einkdisplay
      - lambda: 'id(recorded_display_refresh) += 1;'
      - lambda: 'id(display_last_update).publish_state(id(homeassistant_time).now().timestamp);'
      

# 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:
                - if:
                    condition:
                      binary_sensor.is_on: motion_detected
                    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."

#Include sun
sun:
  latitude: 47.68441276121833
  longitude: -71.51268632342236

# 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



sensor:

  # Create sensors for monitoring Weatherman remotely.
  - platform: template
    name: "Front Door eInk1 - Display Last Update"
    device_class: timestamp
    entity_category: "diagnostic"
    id: display_last_update
    
  - platform: template
    name: "Front Door eInk1 - 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: "Front Door eInk1 - WiFi Signal Strength"
    id: wifisignal
    unit_of_measurement: "dBm"
    entity_category: "diagnostic"
    update_interval: 60s

  # Temperature - My Weather station
  - platform: homeassistant
    entity_id: weather.batcave_owp
    attribute: temperature
    id: weather_temperature
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'    
    
  # Humidity - My Weather station
  - platform: homeassistant
    entity_id: weather.batcave_owp
    attribute: humidity
    id: weather_humidity
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Air pressure - My Weather station
  - platform: homeassistant
    entity_id: weather.batcave_owp
    attribute: pressure
    id: weather_pressure
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Wind Bearing - from Yr.no (built-in HA weather privider)
  - platform: homeassistant
    entity_id: weather.forecast_home
    attribute: wind_bearing
    id: wind_bearing
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

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

  # Weatherman entities
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_0
    id: weather_temperature_0
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_1
    id: weather_temperature_1
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_2
    id: weather_temperature_2
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_3
    id: weather_temperature_3
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Strong Wind
  - platform: homeassistant
    entity_id: sensor.wind_warning
    id: wind_warning
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Wind Speed 
  - platform: homeassistant
    entity_id: weather.batcave_owp
    attribute: wind_speed
    id: wind_speed
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # UV Index
  - platform: homeassistant
    entity_id: sensor.current_uv_index
    id: uv_index
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'


text_sensor:
  # Pressure Trend
  - platform: homeassistant
    entity_id: binary_sensor.atmospheric_pressure
    id: pressure_trend
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Temp Trend
  - platform: homeassistant
    entity_id: binary_sensor.outside_temperature
    id: temp_trend
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Weather State
  - platform: homeassistant
    entity_id: sensor.batcave_owp_condition
    id: weather_state
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Frost Risk
  - platform: homeassistant
    entity_id: sensor.weather_frostrisk
    id: frost_risk
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # # Weatherman entities
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_now
    id: weather_condition_now
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_0
    id: weather_condition_0
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_0
    id: weather_timestamp_0
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_1
    id: weather_condition_1
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_1
    id: weather_timestamp_1
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_2
    id: weather_condition_2
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_2
    id: weather_timestamp_2
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_3
    id: weather_condition_3
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_3
    id: weather_timestamp_3
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'



  # 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.batcave_owp_visibility
    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_RED
    red: 100%
    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: einkdisplay
    cs_pin: GPIO15
    dc_pin: GPIO27
    busy_pin: 
      number: GPIO25
      inverted: True
    reset_pin: GPIO26
    reset_duration: 3ms
    model: 7.50inv2b
    update_interval: never
    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"},


That was the case, thanks! They hade created a new entity with _2 in the end

The images are loaded from this heading
// --WEATHER STATE ICON

You just need the make sure the entity matches your accuweather ID.

There are icons from memory that load here that you must have.

1 Like

Here is my take on e-ink dashboard for weekly calendar/forecast/events.
Wanted to use the big screen of Inkplate10 for most of static HA daily data.

2 Likes

Been wanting to try building this for so long and eventually got the hardware for Christmas. Been playing with it today, got things working but currently the text colour is black with the background a kind of grey (off white) colour. Is there any way to force it to have black background and white text?

I’ve played with different code under the ‘color:’ tag but no luck. Screen it a Waveshare 800×480, 7.5inch black and white.

My yaml looks like this:

# 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: esp32-frame
  friendly_name: ESP32 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:

# Enable Webserver
web_server:
  port: 80
  include_internal: True

# Enable Home Assistant API
api:
  encryption:
    key: "r4ecKKcwJefUSaz45RD0Ztv2JEVitpXS2nmZhIKvY84="

ota:


button:
  - platform: shutdown
    name: "Weatherman - Shutdown"
  - platform: restart
    name: "Weatherman - Restart"
  - platform: template
    name: "Weatherman - Refresh Screen"
    entity_category: config
    on_press:
      - script.execute: update_screen
      
      
# 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'
  - 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);'
      

# 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:
                - if:
                    condition:
                      binary_sensor.is_on: motion_detected
                    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."


# Wifi information
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    # Set this to the IP of the ESP
    static_ip: 192.168.60.200
    # Set this to the IP address of the router. Often ends with .1
    gateway: 192.168.60.1
    # The subnet of the network. 255.255.255.0 works for most home networks.
    subnet: 255.255.255.0


  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-1C8A6C"
    password: "ujvis6zPKIhT"

# 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: ['B', 'D', 'S', 'Y', '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']


  # 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
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_medium
    size: 36
    glyphs: *mdi-weather-glyphs


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


# Check if motion is detected in the living room.
binary_sensor:
  - platform: homeassistant
    entity_id: binary_sensor.motion_sensor_bathroom_sink_hue_occupancy
    id: motion_detected


sensor:
  # Create sensors for monitoring Weatherman remotely.
  - platform: template
    name: "Weatherman - Display Last Update"
    device_class: timestamp
    entity_category: "diagnostic"
    id: display_last_update
    
  - platform: template
    name: "Weatherman - Recorded Display Refresh"
    accuracy_decimals: 0
    unit_of_measurement: "Refreshes"
    state_class: "total_increasing"
    entity_category: "diagnostic"
    lambda: 'return id(recorded_display_refresh);'

  - platform: homeassistant
    entity_id: weather.kings_langley_hourly
    attribute: temperature
    id: weather_temperature
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_0
    id: weather_temperature_0
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_1
    id: weather_temperature_1
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_2
    id: weather_temperature_2
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_temperature_3
    id: weather_temperature_3
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'


text_sensor:
  - platform: homeassistant
    entity_id: weather.kings_langley_hourly
    id: weather_state
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_now
    id: weather_condition_now
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_0
    id: weather_condition_0
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_0
    id: weather_timestamp_0
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_1
    id: weather_condition_1
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_1
    id: weather_timestamp_1
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_2
    id: weather_condition_2
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_2
    id: weather_timestamp_2
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_condition_3
    id: weather_condition_3
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: weather_timestamp_3
    id: weather_timestamp_3
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: train_status
    id: train_status
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: train_status_manhattan
    id: train_status_manhattan
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    entity_id: sensor.weatherman_data
    attribute: train_status_canarsie
    id: train_status_canarsie
    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%
  - id: color_text
    red: 100%
    green: 100%
    blue: 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.50inV2alt
    update_interval: never
    rotation: 90°
    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 {

        // Weather Section
        // it.image(0, 88, id(title_weather)); 
        it.printf(240, 84, id(font_title), color_text, TextAlign::TOP_CENTER, "WEATHER");

        it.printf(100, 158, id(font_mdi_large), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str());

        it.printf(300, 158, id(font_large_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature).state);

        it.printf(105, 282, id(font_small_book), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_0).state.c_str());
        it.printf(105, 306, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_0).state.c_str()].c_str());
        it.printf(105, 354, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_0).state);

        it.printf(195, 282, id(font_small_book), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_1).state.c_str());
        it.printf(195, 306, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_1).state.c_str()].c_str());
        it.printf(195, 354, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_1).state);

        it.printf(285, 282, id(font_small_book), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_2).state.c_str());
        it.printf(285, 306, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_2).state.c_str()].c_str());
        it.printf(285, 354, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_2).state);

        it.printf(375, 282, id(font_small_book), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_3).state.c_str());
        it.printf(375, 306, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_3).state.c_str()].c_str());
        it.printf(375, 354, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_3).state);

        it.printf(240, 400, id(font_title), color_text, TextAlign::TOP_CENTER, "BIRTHDAYS");


        // 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);
      }

captive_portal:

Try this:

color:
  - id: color_text
    red: 0%
    green: 0%
    blue: 0%
    white: 50%
  - id: color_bg
    red: 0%
    green: 0%
    blue: 0%
    white: 0%

I’m new to this e-paper thing, but added this section so hope I’m doing it correctly :slight_smile: (about the inverted busy pin)

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.50inV2*
  • update_interval: never*

Hello, I am also in the process of creating a picture frame. After big problems with the weather integration (hourly forecast) I finally managed to get it running.

Since the time in the clock jump code is output with e.g. 7PM I would like to convert this to 19:00
I’m not sure if I have to work with timestamp_custom or if I should specify something else? Thank you