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

It’s a bit hard to say without seeing your configuration, but it seems like the ESP32 isn’t properly fetching data from Home Assistant. A good starting point for troubleshooting would be to remove any if / else conditions in the display: section in your ESPHome YAML configuration that control what’s displayed on the e-paper screen. This way, you can test if the screen is able to display any data from the sensors at all. If it works without the conditions, the issue might be in the logic or how the data is being fetched from Home Assistant.

Also, double-check that the sensors in Home Assistant are providing updated values and that the ESPHome device is properly connected to your network and able to communicate with Home Assistant.

Feel free to share your YAML configuration if you’d like more detailed help!

DFRobot sell:

  • Firebeetle 2 ESP32-E (4mb and 16mb variants)
  • Firebeetle 2 ESP32-C6 (4mb)

For the Firebeetle 2 ESP32-C6, I got ESPHome working with the following board definition in the yaml:

esp32:
  board: esp32-c6-devkitc-1
  flash_size: 4MB
  variant: esp32c6
  framework:
    type: esp-idf
    version: "5.3.1"
    platform_version: 6.9.0
    sdkconfig_options:
      CONFIG_OPENTHREAD_ENABLED: n
      CONFIG_ENABLE_WIFI_STATION: y
      CONFIG_USE_MINIMAL_MDNS: y
      CONFIG_ESPTOOLPY_FLASHSIZE_4MB: y

Finally got this working, based on inspirations above and elsewhere:

Here’s is a portable, battery-powered 2.9" ePaper display of the current Bitcoin price from Coingecko, powered by ESPHome.

This is an ESPHome configuration for a Firebeetle2 ESP32-C6 with a Waveshare 2.9" black and white ePaper display. The configuration tells the Firebeetle2 to periodically wake up, update the display with sensor data, then go back to deep sleep. My device is awake about 8 seconds per hour, which I’m hoping will mean long battery life.

My YAML config is here:

Bitcoin tracking ePaper goes for £250 on Etsy, so I’m pleased with this!

Open to any improvements/critiques, or questions.

Hi,

Does anyone manage to write text and draw objects in gray-scale with this model?

My display definition is:

display:
  - platform: waveshare_epaper
    id: eink_display
    cs_pin: GPIO15
    dc_pin: GPIO27
    reset_pin: GPIO26
    busy_pin: 
      number: GPIO25
      inverted: true
    model: 7.50inV2alt
    reset_duration: 2ms
    update_interval: never
    rotation: 270°

Here is where I am so far:

Regards

3 Likes

I like your setup, is it possible to share the home assistant weather code as well?

Thanks for the repository. Here’s my implementation. Mainly focused around solar & energy.

11 Likes

How did you get the F1 info for the screen?

Originally, I used the Ergast API to fetch F1 data, but since it has been deprecated, I moved everything to a Google Sheet. I use Google Script to read the data as a REST sensor in Home Assistant. It’s not the most elegant solution, but it works.

There’s a new project called jolpica-f that aims to replace Ergast, but I haven’t had the chance to look into it yet.

Nice one, do you want to share the code?

I believe the new API is the way to go. My current solution isn’t the easiest to share since it’s specifically tied to my Google account and a manual spreadsheet.

I’ll take a look at the new API soon and see if I can migrate to it.

That’s really awesome. I’d love to see the code too if you’re happy to share.

sure :slight_smile:

There’s commented out bits left over from development. I’ve left those in, might be useful.

esphome:
  name: e-ink-display-one
  friendly_name: E Ink Display One
  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

#deep_sleep:
#  run_duration: 10s
#  sleep_duration: 50s

# Enable logging
logger:
  baud_rate: 0

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxx"

ota:
  - platform: esphome
    password: "xxxxxxx"

wifi:
  power_save_mode: LIGHT
  fast_connect: true
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 192.168.10.67
    gateway: 192.168.10.1
    subnet: 255.255.255.0
    dns1: 192.168.10.1

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "E-Ink-Display-One"
    password: "xxxxx"


captive_portal:


#switch:
#  - platform: gpio
#    pin: GPIO32 #display power enable pin
#    id: display_power
#    internal: true  

button:
  - platform: shutdown
    name: "E Ink - Shutdown"
  - platform: restart
    name: "E Ink - Restart"
  - platform: template
    name: "E Ink - Refresh Screen"
    entity_category: config
    on_press:
      - script.execute: update_screen
  - platform: template
    name: "E Ink - Clear Screen"
    entity_category: config
    on_press:
      - script.execute: clear_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: data_screen_off
    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;'
      - lambda: 'id(data_screen_off) = false;'
      - component.update: eink_display
      - lambda: 'id(recorded_display_refresh) += 1;'
      #- lambda: 'id(display_last_update).publish_state(id(homeassistant_time).now().timestamp);'
  - id: clear_screen
    then:
      - lambda: 'id(data_updated) = false;'
      - lambda: 'id(data_screen_off) = true;'
      - component.update: eink_display
      - lambda: 'id(recorded_display_refresh) += 1;'
      #- lambda: 'id(display_last_update).publish_state(id(homeassistant_time).now().timestamp);'





time:
  - platform: homeassistant
    id: homeassistant_time
    timezone: Europe/London
    on_time:
      # Every 1 minutes
      - seconds: 0
        minutes: /2
        then:
          - if:
              condition:
                lambda: 'return id(data_screen_off) == false;'
              then:
                - script.execute: update_screen

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true

spi:
  clk_pin: GPIO13
  mosi_pin: GPIO14

image:
   - file: "images/EInkBG.png"
     id: background_image
     type: BINARY

sensor:
  - platform: bmp280_i2c
    address: 0x77
    update_interval: 60s
    temperature:
      id: bmp280_temp
      name: "BMP temp"
      oversampling: 2x
    pressure:
      id: bmp280_pressure
      name: "BMP Pressure"
      filters:
        - offset: 14.0

  - platform: aht10
    variant: AHT20
    address: 0x38
    update_interval: 60s
    temperature:
      name: "AHT20 temp"
      id: aht20_temp
      accuracy_decimals: 1
      filters:
      - filter_out: 0.0
      - median:
          window_size: 3
          send_every: 3
          send_first_at: 1
    humidity:
      name: "AHT20 humidity"
      id: aht20_humidity
      accuracy_decimals: 1
      filters:
      - filter_out: 0.0
      - median:
          window_size: 3
          send_every: 3
          send_first_at: 1

  - platform: homeassistant
    id: pv_power
    entity_id: sensor.pv_power
    #on_value:
    #  then:
    #    - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    id: battery_state_of_charge
    entity_id: sensor.battery_state_of_charge
  - platform: homeassistant
    id: load_power
    entity_id: sensor.load_power
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
  - platform: homeassistant
    id: grid_power
    entity_id: sensor.grid_power
  - platform: homeassistant
    id: battery_power
    entity_id: sensor.battery_power
  - platform: template
    id: battery_charge_percent
    lambda: |-
      if (id(battery_power).state != 0){
        float percentage = (100.0 / 6000.0) * float(id(battery_power).state);
        if (percentage >= -100 && percentage <= 100){
          return percentage;
        } else {
          return 0;
        }
      } else {
        return 0;
      }
    accuracy_decimals: 1
    update_interval: 30s
  - platform: homeassistant
    id: pv_energy
    entity_id: sensor.pv_energy
  - platform: homeassistant
    id: grid_energy_out
    entity_id: sensor.grid_energy_out
  - platform: homeassistant
    id: pv_energy_daily
    entity_id: sensor.pv_energy_daily
  - platform: homeassistant
    id: grid_energy_in_daily
    entity_id: sensor.grid_energy_in_daily
  - platform: homeassistant
    id: grid_energy_out_daily
    entity_id: sensor.grid_energy_out_daily
  - platform: homeassistant
    id: battery_energy_in_daily
    entity_id: sensor.battery_energy_in_daily
  - platform: homeassistant
    id: battery_energy_out_daily
    entity_id: sensor.battery_energy_out_daily_2
  - platform: homeassistant
    id: load_energy_daily
    entity_id: sensor.load_energy_daily

    


  - platform: homeassistant
    id: weather_tempertature
    entity_id: weather.blueshift
    attribute: temperature
  - platform: homeassistant
    id: weather_humidity
    entity_id: weather.blueshift
    attribute: humidity
  - platform: homeassistant
    id: weather_uv_index
    entity_id: weather.blueshift
    attribute: uv_index
  - platform: homeassistant
    id: weather_wind_speed
    entity_id: weather.blueshift
    attribute: wind_speed
  - platform: homeassistant
    id: weather_wind_bearing
    entity_id: weather.blueshift
    attribute: wind_bearing



#graph:
  # Show bare-minimum auto-ranged graph
#  - id: battery_state_of_charge_chart
#    sensor: battery_state_of_charge
#    duration: 2h
#    width: 210
#    height: 160

text_sensor:
  - platform: template
    id: battery_charge_icon
    lambda: |-
      if ((int)id(battery_state_of_charge).state >= 70 ){
        return id(battery_charge_icon).state = "battery-high";
      } else if ((int)id(battery_state_of_charge).state >= 40) {
        return id(battery_charge_icon).state = "battery-medium";
      } else if ((int)id(battery_state_of_charge).state > 10) {
        return id(battery_charge_icon).state = "battery-low";
      } else {
        return id(battery_charge_icon).state = "battery-outline";
      }
  - platform: homeassistant
    id: weather_blueshift
    entity_id: weather.blueshift       
  - platform: homeassistant
    id: sun_next_rising
    entity_id: sensor.sun_next_rising
  - platform: homeassistant
    id: sun_next_setting
    entity_id: sensor.sun_next_setting
  - platform: template
    id: weather_wind_bearing_icon
    lambda: |-
      float rotation = 8.0 / 360.0;
      int direction = (int)floor(0.5 + (rotation * id(weather_wind_bearing).state));
      if (direction == 0){
        return id(weather_wind_bearing_icon).state = "arrow-up";
      }
      if (direction == 1){
        return id(weather_wind_bearing_icon).state = "arrow-top-right";
      }
      if (direction == 2){
        return id(weather_wind_bearing_icon).state = "arrow-right";
      }
      if (direction == 3){
        return id(weather_wind_bearing_icon).state = "arrow-bottom-right";
      }
      if (direction == 4){
        return id(weather_wind_bearing_icon).state = "arrow-down";
      }
      if (direction == 5){
        return id(weather_wind_bearing_icon).state = "arrow-bottom-left";
      }
      if (direction == 6){
        return id(weather_wind_bearing_icon).state = "arrow-left";
      }
      if (direction == 7){
        return id(weather_wind_bearing_icon).state = "arrow-top-left";
      }
      return id(weather_wind_bearing_icon).state = "arrow-up";




#interval:
#  - interval: 1s
#    then: 
#      - lambda: |
#            ESP_LOGI("log", "1%s", id(weather_blueshift_base).state);
#            ESP_LOGI("log", "2%s", id(weather_blueshift_base).state.c_str());
#            ESP_LOGI("log", "3%s", id(weather_blueshift).state);
#            ESP_LOGI("log", "4%s", id(weather_blueshift).state.c_str());


#id(sun_next_rising).publish_state(id(homeassistant_time).now().timestamp)
#days 3 letters
    #glyphs: ['J','a','n','F','e','b','M','r','A','p','y','u','l','g','S','O','c','t','N','o','v','D']

font:
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_bold_120
    size: 120
    glyphs: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':']
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_book_64
    size: 64
    glyphs: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_bold_44
    size: 44
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_bold_34
    size: 34
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_bold_30
    size: 30
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_book_30
    size: 30
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_book_34
    size: 34

# 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: 80
    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_130
    size: 130
    glyphs: &mdi-battery-glyphs
      - "\U000F12A3" # mdi-battery-high
      - "\U000F12A2" # mdi-battery-medium
      - "\U000F12A1" # mdi-battery-low
      - "\U000F008E" # mdi-battery-outline
      - "\U000F125E" # mdi-battery-off-outline
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_30
    size: 30
    glyphs: &mdi-weathersun-glyphs
      - "\U000F059B" # mdi-weather-sunset-down
      - "\U000F059C" # mdi-weather-sunset-up
      - "\U000F050F" # mdi-thermometer
      - "\U000F058E" # mdi-water-percent
      - "\U000F05A8" # mdi-white-balance-sunny
      - "\U000F15FA" # mdi-windsock
      - "\U000F005D" # mdi-arrow-up
      - "\U000F005C" # mdi-arrow-top-right
      - "\U000F0054" # mdi-arrow-right
      - "\U000F0043" # mdi-arrow-bottom-right
      - "\U000F0045" # mdi-arrow-down
      - "\U000F0042" # mdi-arrow-bottom-left
      - "\U000F004D" # mdi-arrow-left
      - "\U000F005B" # mdi-arrow-top-left
      - "\U000F029A" # mdi-gauge
      - "\U000F12A3" # mdi-battery-high
      - "\U000F12A2" # mdi-battery-medium
      - "\U000F12A1" # mdi-battery-low
      - "\U000F008E" # mdi-battery-outline
      - "\U000F125E" # mdi-battery-off-outline
      



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
    #full_update_every: 10
    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"},
          {"clear-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"},
          {"battery-high", "\U000F12A3"},
          {"battery-medium", "\U000F12A2"},
          {"battery-low", "\U000F12A1"},
          {"battery-outline", "\U000F008E"},
          {"battery-off-outline", "\U000F125E"},
          {"thermometer", "\U000F050F"},
          {"water-percent", "\U000F058E"},
          {"white-balance-sunny", "\U000F05A8"},
          {"windsock", "\U000F15FA"},
          {"arrow-up", "\U000F005D"},
          {"arrow-top-right", "\U000F005C"},
          {"arrow-right", "\U000F0054"},
          {"arrow-bottom-right", "\U000F0043"},
          {"arrow-down", "\U000F0045"},
          {"arrow-bottom-left", "\U000F0042"},
          {"arrow-left", "\U000F004D"},
          {"arrow-top-left", "\U000F005B"},
          {"gauge", "\U000F029A"},
        };
      // Inverted
      //auto color_black = Color(255, 255, 255);
      //auto color_white = Color(0, 0, 0);
      // Regular
      auto color_black = Color(0, 0, 0);
      auto color_white = Color(255, 255, 255);

      // Fill background.
      it.fill(color_black);

      if (id(data_screen_off) == true) {

      } else if (id(initial_data_received) == false) {
        it.printf(240, 390, id(font_bold_34), color_white, TextAlign::TOP_CENTER, "WAITING FOR DATA...");
      } else {
        // TMP alignment grid
        //it.rectangle(20,20,440,760, color_white); 

        // Background
        it.image(0, 0, id(background_image));

        // Sunrise Day Sunset
        //it.printf(19, 144, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["sunset-up"].c_str());
        it.printf(56, 26, id(font_book_30), color_white, TextAlign::TOP_LEFT , "%s", id(sun_next_rising).state.substr(11,5).c_str());
        //it.strftime(240, 144, id(font_book_30), color_white, TextAlign::TOP_CENTER , "%A", id(homeassistant_time).now());
        it.printf(424, 26, id(font_book_30), color_white, TextAlign::TOP_RIGHT , "%s", id(sun_next_setting).state.substr(11,5).c_str());
        //it.printf(462, 144, id(font_mdi_30), color_white, TextAlign::TOP_RIGHT, "%s", weather_icon_map["sunset-down"].c_str());

        // Time Section
        it.strftime(240, 58, id(font_bold_120), color_white, TextAlign::TOP_CENTER , "%H:%M", id(homeassistant_time).now());
        it.strftime(240, 162, id(font_book_30), color_white, TextAlign::TOP_CENTER , "%a %e %b %Y", id(homeassistant_time).now());
        //it.strftime(460, 16, id(font_book_64), color_white, TextAlign::TOP_RIGHT , "%d", id(homeassistant_time).now());
        //it.strftime(460, 77, id(font_bold_34), color_white, TextAlign::TOP_RIGHT , "%b", id(homeassistant_time).now());

        

        //it.line(10, 190, 470, 190);

        // PV Section

        // PV / Grid / Load
        
        it.printf(240, 223, id(font_mdi_130), color_white, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(battery_charge_icon).state.c_str()].c_str());
        it.printf(422, 534, id(font_mdi_30), color_black, TextAlign::TOP_LEFT, "%s", weather_icon_map[id(battery_charge_icon).state.c_str()].c_str());
       
        it.printf(240, 348, id(font_bold_44), color_white, TextAlign::TOP_CENTER , "%d%", (int)(id(battery_state_of_charge).state + 0.5));
        if(id(battery_charge_percent).state != 0){
          it.printf(240, 391, id(font_book_30), color_white, TextAlign::TOP_CENTER , "%d%%", (int)(id(battery_charge_percent).state + 0.5));
        }

        //it.graph(235, 220, id(battery_state_of_charge_chart));

        //it.print(85, 392, id(font_book_34), color_white, TextAlign::TOP_CENTER , "PV");
        //it.print(240, 392, id(font_book_34), color_white, TextAlign::TOP_CENTER , "Grid");
        //it.print(395, 392, id(font_book_34), color_white, TextAlign::TOP_CENTER , "Load");

        it.printf(106, 259, id(font_bold_44), color_black, TextAlign::TOP_CENTER , "%dw",(int)(id(pv_power).state + 0.5));
        it.printf(106, 472, id(font_bold_44), color_black, TextAlign::TOP_CENTER , "%dw",(int)(id(grid_power).state + 0.5));
        it.printf(374, 259, id(font_bold_44), color_black, TextAlign::TOP_CENTER , "%dw",(int)(id(load_power).state + 0.5));
        it.printf(374, 472, id(font_bold_44), color_black, TextAlign::TOP_CENTER , "%dw",(int)(id(battery_power).state + 0.5));

        it.printf(125, 312, id(font_bold_30), color_black, TextAlign::TOP_CENTER , "%.1f",id(pv_energy_daily).state);
        it.printf(125, 396, id(font_bold_30), color_black, TextAlign::TOP_CENTER , "%.1f",id(grid_energy_in_daily).state);
        it.printf(125, 433, id(font_bold_30), color_black, TextAlign::TOP_CENTER , "%.1f",id(grid_energy_out_daily).state);

        it.printf(355, 312, id(font_bold_30), color_black, TextAlign::TOP_CENTER , "%.1f",id(load_energy_daily).state);
        it.printf(355, 396, id(font_bold_30), color_black, TextAlign::TOP_CENTER , "%.1f",id(battery_energy_out_daily).state);
        it.printf(355, 433, id(font_bold_30), color_black, TextAlign::TOP_CENTER , "%.1f",id(battery_energy_in_daily).state);


        //it.line(10, 524, 470, 524);

        // Weather
        it.printf(240, 432, id(font_mdi_large), color_white, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_blueshift).state.c_str()].c_str());
        it.printf(240, 517, id(font_bold_30), color_white, TextAlign::TOP_CENTER, "%2.1fC", id(weather_tempertature).state);
        

        it.printf(69, 593, id(font_bold_30), color_white, TextAlign::TOP_LEFT, "%d UVI", (int)id(weather_uv_index).state);
        it.printf(69, 633, id(font_bold_30), color_white, TextAlign::TOP_LEFT, "%d%%", (int)id(weather_humidity).state);
        it.printf(29, 674, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map[id(weather_wind_bearing_icon).state.c_str()].c_str());
        it.printf(69, 674, id(font_bold_30), color_white, TextAlign::TOP_LEFT, "%dkm", (int)(id(weather_wind_speed).state + 0.5));

        it.printf(411, 593, id(font_bold_30), color_white, TextAlign::TOP_RIGHT, "%2.1fC", id(aht20_temp).state);
        it.printf(411, 633, id(font_bold_30), color_white, TextAlign::TOP_RIGHT, "%d%%", (int)(id(aht20_humidity).state + 0.5));
        it.printf(411, 674, id(font_bold_30), color_white, TextAlign::TOP_RIGHT, "%dhPa", (int)(id(bmp280_pressure).state + 0.5));

        //it.printf(20, 696, id(font_book_30), color_white, TextAlign::TOP_LEFT, "id: %s", id(weather_blueshift).state.c_str());
        //it.printf(150, 555, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["thermometer"].c_str());
        //it.printf(150, 595, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["water-percent"].c_str());
        //it.printf(300, 555, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["white-balance-sunny"].c_str());
        //it.printf(300, 595, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["windsock"].c_str());
        //it.printf(150, 655, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["thermometer"].c_str());
        //it.printf(150, 695, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["water-percent"].c_str());
        //it.printf(300, 655, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["gauge"].c_str());
        //it.printf(300, 695, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map["windsock"].c_str());
        //it.printf(335, 696, id(font_book_30), color_white, TextAlign::TOP_LEFT, "%dkm", (int)(id(weather_wind_speed).state + 0.5));
        //it.printf(420, 695, id(font_mdi_30), color_white, TextAlign::TOP_LEFT, "%s", weather_icon_map[id(weather_wind_bearing_icon).state.c_str()].c_str());

        
        
    

        //it.rectangle(0,0,480,800, color_white); 
        //it.rectangle(4,4,472,792, color_white); 
        //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_smallest_book), color_white, TextAlign::TOP_CENTER, "REFRESHED AT %s", str);
      }

      

      

           
      
      
2 Likes

This is my display to monitor my greenhouse

6 Likes

Hey! I love this project! A bit of a rocky. Wondering if it would work with:

@madelena :heart::metal:

Looks great! What hardware did you order?

Hi, I would be interested to know which pin you connected to the ESP32 for power and what you put as resistors. In short, I would be interested to know the power scheme you used! Thanks

display link

driver board link

code is below

substitutions:
  name: barn_dashboard
  friendly_name: Barn Dashboard
  room_name: Barn
  gpio_spi_clk_pin: GPIO13
  gpio_spi_mosi_pin: GPIO14
  gpio_cs_pin: GPIO15
  gpio_dc_pin: GPIO27
  gpio_reset_pin: GPIO26
  gpio_busy_pin: GPIO25

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.6.0
  name_add_mac_suffix: false
  on_boot:
    then:
      - switch.turn_on: display_power
      - script.execute: update_screen
  on_shutdown:
    then:
      - switch.turn_off: display_power

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

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

ota:
  - platform: esphome
    password: ""

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 192.168.1.182
    gateway: 192.168.1.1
    subnet: 255.255.255.0


  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Barn-Dashboard Fallback Hotspot"
    password: "Q9LbdglSFG4r"

captive_portal:

# Enable Web server.
web_server:
  port: 80


font:

  - file: 'fonts/Roboto-Medium.ttf'
    id: roboto_med_28
    size: 30
  - file: 'fonts/Roboto-Regular.ttf'
    id: footer_font
    size: 12
    glyphs:
      ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
       '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
       'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
       'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
       'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/','º','µ','³']

  - file: 'fonts/Roboto-Regular.ttf'
    id: font_medium
    size: 20
    glyphs:
      ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
       '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
       'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
       'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
       'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/','º','µ','³']
       
  - file: 'fonts/Roboto-Medium.ttf'
    id: font_small
    size: 14
    glyphs:
      ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
       '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
       'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
       'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
       'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/','º','µ','³']
  
  - file: 'fonts/Roboto-Bold.ttf'
    id: font_large
    size: 24
  
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id:  mdi_small
    size: 28
    glyphs: [
      # Wifi
      '󰤫', # mdi-wifi-strength-alert-outline
      '󰤟', # mdi-wifi-strength-1
      '󰤢', # mdi-wifi-strength-2
      '󰤥', # mdi-wifi-strength-3
      '󰤨', # mdi-wifi-strength-4
      ]

  - file: 'fonts/materialdesignicons-webfont.ttf'
    id:  mdi_wifi
    size: 20
    glyphs: [
      # Wifi
      '󰤫', # mdi-wifi-strength-alert-outline
      '󰤟', # mdi-wifi-strength-1
      '󰤢', # mdi-wifi-strength-2
      '󰤥', # mdi-wifi-strength-3
      '󰤨'  # mdi-wifi-strength-4

      ]

  - file: 'fonts/materialdesignicons-webfont.ttf'
    id:  mdi_med
    size: 30
    glyphs: [
      # Wifi
      '󰤫', # mdi-wifi-strength-alert-outline
      '󰤟', # mdi-wifi-strength-1
      '󰤢', # mdi-wifi-strength-2
      '󰤥', # mdi-wifi-strength-3
      '󰤨', # mdi-wifi-strength-4
      '󰀦' # mdi-alert
      ]

  # 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: 90
    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-clear-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: 30
    glyphs: *mdi-weather-glyphs

time:
  - platform: homeassistant
    id: homeassistant_time
    on_time_sync:
      - component.update: uptime_timestamp

text_sensor:
  - platform: version
    name: ${friendly_name} ESPHome version

  - platform: homeassistant
    id: weather_now
    entity_id: sensor.pirateweather_hourly_summary

binary_sensor:
  - platform: status
    name: ${friendly_name} Status
    id: barn_dash_status
  - platform: homeassistant
    name: "OTA Mode"
    id: otamode
    entity_id: input_boolean.display_ota_mode   

switch:
  - platform: restart
    name: $(friendly_name) Restart
  - platform: gpio
    pin: GPIO32 #display power enable pin
    id: display_power
    internal: true  

button:
  - platform: restart
    name: "Dashboard - Restart"
  - platform: template
    name: "Dashboard - Refresh Screen"
    entity_category: config
    on_press:
      - script.execute: update_screen

sensor:
  - platform: uptime
    name: $(friendly_name) Uptime Sec
    id: uptime_sec
    internal: true

  - platform: homeassistant
    entity_id: sensor.shelf_temp 
    id: shelf_temp 

  - platform: homeassistant
    entity_id: sensor.shelf_humidity
    id: shelf_humidity
  
  - platform: homeassistant
    entity_id: sensor.outdoor_humidity 
    id: outdoor_humidity 

  - platform: homeassistant
    entity_id: sensor.outdoor_temperature
    id: outdoor_temperature
  
  - platform: homeassistant
    entity_id: sensor.greenhouse_temp
    id: temperature_greenhouse
  
  - platform: homeassistant
    entity_id: sensor.greenhouse_humidity
    id: greenhouse_humid
  
  - platform: homeassistant
    entity_id: sensor.barn_temp
    id: barn_temperature
  
  - platform: homeassistant
    entity_id: sensor.barn_humidity
    id: barn_humidity
    
  - platform: homeassistant
    entity_id: sensor.bed_temp_1
    id: bed_temp_1
    
  - platform: homeassistant
    entity_id: sensor.bed_temp_2
    id: bed_temp_2
  
  - platform: homeassistant
    entity_id: sensor.bed_temp_3
    id: bed_temp_3

  - platform: homeassistant
    entity_id: sensor.bed_temp_4
    id: bed_temp_4

  - platform: homeassistant
    entity_id: sensor.soil_moisture_4_in_2
    id: soil_moisture_4_percent

  - platform: homeassistant
    entity_id: sensor.soil_moisture_8_pc
    id: moisture_soil_8_pct

  - platform: homeassistant
    entity_id: sensor.soil_moisture_4_in
    id: soil_moisture_4_in

  - platform: homeassistant
    entity_id: sensor.soil_moisture_8_in
    id: in_8_moisture

  - platform: homeassistant
    entity_id: sensor.soil_moisture_12_in
    id: in_12_moisture

  - platform: homeassistant
    entity_id: sensor.soil_moisture_16_in
    id: in_16_moisture 

  - platform: homeassistant
    entity_id: sensor.today_s_rainfall
    id: rainfall_today

  - platform: homeassistant
    entity_id: sensor.rainfall_last_7_days
    id: rainfall_7_day

  - platform: homeassistant
    entity_id: sensor.monthly_rainfall
    id: rainfall_monthly

  - platform: homeassistant
    entity_id: sensor.yearly_rainfall
    id: rainfall_yearly

  - platform: homeassistant
    entity_id: sensor.solar_energy
    id: solar_energy

  - platform: wifi_signal
    name: "WiFi Signal Sensor"
    id: wifisignal
    update_interval: 60s
    
  - platform: template
    id: uptime_timestamp
    name: $(friendly_name) Uptime
    device_class: "timestamp"
    accuracy_decimals: 0
    update_interval: never
    lambda: |-
      static float timestamp = (
        id(homeassistant_time).utcnow().timestamp - id(uptime_sec).state
      );
      return timestamp;

graph:
  
  - id: temperature_graph
    duration: 48h
    x_grid: 30min
    y_grid: 5.0     # degF/div
    width: 500
    height: 115
    traces:
      - sensor: temperature_greenhouse
        line_type: SOLID
        line_thickness: 4
      - sensor: barn_temperature
        line_type: DASHED
        line_thickness: 4
        continuous: true
      - sensor: outdoor_temperature
        line_type: DOTTED
        line_thickness: 4
      
script:
  - id: update_screen
    then:
      - component.update: screen
spi:
  clk_pin: $gpio_spi_clk_pin
  mosi_pin: $gpio_spi_mosi_pin
  id: epaper_display

display:
  - platform: waveshare_epaper
    cs_pin: $gpio_cs_pin
    busy_pin: $gpio_busy_pin
    reset_pin: $gpio_reset_pin
    dc_pin: $gpio_dc_pin
    #model: 7.50in-bc
    #model: 7.50inV2
    # model: 7.50in
    #model: 7.50in-bV2
    model: 7.50inV2alt
    update_interval: 5min
    reset_duration: 2ms
    id: screen
    lambda: |-
      int offsetY = 140;

      // Map weather states to MDI characters.
      std::map<std::string, std::string> weather_icon_map
        {
          {"Cloudy", "\U000F0590"},
          {"cloudy", "\U000F0590"},
          {"cloudy-alert", "\U000F0F2F"},
          {"cloudy-arrow-right", "\U000F0E6E"},
          {"Fog", "\U000F0591"},
          {"Hail", "\U000F0592"},
          {"Hazy", "\U000F0F30"},
          {"hurricane", "\U000F0898"},
          {"Lightning", "\U000F0593"},
          {"lightning-rainy", "\U000F067E"},
          {"Clear Night", "\U000F0594"},
          {"Night Partly Cloudy", "U000F0F31"},
          {"Partly Cloudy", "\U000F0595"},
          {"partly-lightning", "\U000F0F32"},
          {"partly-rainy", "\U000F0F33"},
          {"partly-snowy", "\U000F0F34"},
          {"partly-snowy-rainy", "\U000F0F35"},
          {"Pouring", "\U000F0596"},
          {"Rain", "\U000F0597"},
          {"Snow", "\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"},
        };

      char unit[] = "cbn";
      
      float temp_outdoor = id(outdoor_temperature).state;
      float humid_outdoor = id(outdoor_humidity).state;
      float energy_solar = id(solar_energy).state;
      float temp_barn = id(barn_temperature).state;
      float greenhouse_temp = id(temperature_greenhouse).state;
      float humid_greenhouse = id(greenhouse_humid).state;
      float humid_barn = id(barn_humidity).state;
      float temp_bed_1 = id(bed_temp_1).state;
      float temp_bed_2 = id(bed_temp_2).state;
      float temp_bed_3 = id(bed_temp_3).state;
      float temp_bed_4 = id(bed_temp_4).state;
      float soil_4in_percent = id(soil_moisture_4_percent).state;
      float soil_percent_8in = id(moisture_soil_8_pct).state;
      int soil_4in_cnb = id(soil_moisture_4_in).state;
      int soil_8in_cnb = id(in_8_moisture).state;
      int soil_12in_cnb = id(in_12_moisture).state;
      int soil_16in_cnb = id(in_16_moisture).state;
      float todays_rainfall =id(rainfall_today).state;
      float day_7_rainfall = id(rainfall_7_day).state;
      float monthly_rainfall = id(rainfall_monthly).state;
      float yearly_rainfall = id(rainfall_yearly).state; 
      float soil_12in_pct = id(in_12_moisture).state;
      float soil_16in_pct = id(in_16_moisture).state;
      float temp_shelf = id(shelf_temp).state;
      float humidity_shelf = id(shelf_humidity).state;

      /* Date and Time */
      it.strftime(400, 10, id(font_medium), TextAlign::CENTER_HORIZONTAL, "%A %B %d, %Y", id(homeassistant_time).now());
      it.strftime(400, 30, id(font_large), TextAlign::CENTER_HORIZONTAL, "%H:%M:%S", id(homeassistant_time).now());

      /* Current Weather */
        it.printf(400, 60, id(font_small), TextAlign::CENTER_HORIZONTAL, "Currently: %s", id(weather_now).state.c_str()); 
        it.printf(400, 85, id(font_mdi_large), TextAlign::CENTER_HORIZONTAL, "%s", weather_icon_map[id(weather_now).state.c_str()].c_str());

      /* Outdoor */
      
      it.print(10, 50, id(font_large), "Outdoors & Barn");
      it.rectangle(10,80,250,80);
      it.print(15, 105, id(font_small), TextAlign::BASELINE_LEFT , "Outdoor Temperature:");
      it.print(15, 125, id(font_small), TextAlign::BASELINE_LEFT , "Outdoor Humidity:");
      it.print(15, 145, id(font_small), TextAlign::BASELINE_LEFT , "Solar Energy:");
      it.printf(240, 105, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", temp_outdoor);
      it.printf(240, 125, id(font_small), TextAlign::BASELINE_RIGHT , "%.1f% %", humid_outdoor);
      it.printf(240, 145, id(font_small), TextAlign::BASELINE_RIGHT , "%.1f% w/m2", energy_solar);
      
      /* Greenhouse and Barn */
      it.print(10, 35 + offsetY, id(font_large), "Greenhouse/Barn");
      it.rectangle(10, 65 + offsetY,250,255);
      it.print(15, 68 + offsetY, id(font_small), "Greenhouse");
      it.printf(240, 95 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", greenhouse_temp);
      it.printf(240, 115 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%.1f% %", humid_greenhouse);
      it.print(15, 120 + offsetY, id(font_small), "Barn");
      it.printf(240, 140 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", temp_barn);
      it.printf(240, 160 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%.1f% %", humid_barn);
      it.print(15, 170 + offsetY, id(font_small), "Greenhouse Bed Temps");
      it.printf(240, 190 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", temp_bed_1);
      it.printf(240, 210 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", temp_bed_2);
      it.printf(240, 230 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", temp_bed_3);
      it.printf(240, 250 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", temp_bed_4);
      it.print(15, 255 + offsetY, id(font_small), "Greenhouse in a Greenhouse");
      it.printf(240, 285 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f ºF", temp_shelf);
      it.printf(240, 305 + offsetY, id(font_small), TextAlign::BASELINE_RIGHT , "%.1f% %", humidity_shelf);

      /* Rain Data */
      it.print(280, 175, id(font_large), "Rain Data");
      it.rectangle(280,205,230,100);
      it.print(285, 225, id(font_small), TextAlign::BASELINE_LEFT , "Rain Today");
      it.print(285, 245, id(font_small), TextAlign::BASELINE_LEFT , "7 Day Rain");
      it.print(285, 265, id(font_small), TextAlign::BASELINE_LEFT , "Monthly Rain");
      it.print(285, 285, id(font_small), TextAlign::BASELINE_LEFT , "Yearly Rain");
      it.printf(500, 225, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f in", todays_rainfall);
      it.printf(500, 245, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f in", day_7_rainfall);
      it.printf(500, 265, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f in", monthly_rainfall);
      it.printf(500, 285, id(font_small), TextAlign::BASELINE_RIGHT , "%2.1f in", yearly_rainfall);

      /* Lavender Bed */
      it.print(595, 50, id(font_large), "Lavender Bed");
      it.rectangle(595,80,180,225); 
      it.print(600, 85, id(font_small), "Percent:");  
      it.printf(765, 115, id(font_small), TextAlign::BASELINE_RIGHT , "%.1f% %", soil_4in_percent);
      it.printf(765, 135, id(font_small), TextAlign::BASELINE_RIGHT , "%.1f% %", soil_percent_8in);
      it.printf(765, 155, id(font_small), TextAlign::BASELINE_RIGHT, "%.1f% %", (255-soil_12in_pct)/255 * 100 );
      it.printf(765, 175, id(font_small), TextAlign::BASELINE_RIGHT, "%.1f% %", (255-soil_16in_pct)/255 * 100 );
      it.print(603, 115, id(font_small), TextAlign::BASELINE_LEFT , "4 inches");
      it.print(603, 135, id(font_small), TextAlign::BASELINE_LEFT , "8 inches");
      it.print(603, 155, id(font_small), TextAlign::BASELINE_LEFT , "12 inches");  
      it.print(603, 175, id(font_small), TextAlign::BASELINE_LEFT , "16 inches");    

      it.print(600, 185, id(font_small), "Centibars:");
      it.printf(740, 215, id(font_small), TextAlign::BASELINE_RIGHT , "%d%", soil_4in_cnb);
      it.printf(740, 235, id(font_small), TextAlign::BASELINE_RIGHT , "%d%", soil_8in_cnb);
      it.printf(740, 255, id(font_small), TextAlign::BASELINE_RIGHT , "%d%", soil_12in_cnb);
      it.printf(740, 275, id(font_small), TextAlign::BASELINE_RIGHT , "%d%", soil_16in_cnb);
      it.print(603, 215, id(font_small), TextAlign::BASELINE_LEFT , "4 inches");
      it.print(603, 235, id(font_small), TextAlign::BASELINE_LEFT , "8 inches");
      it.print(603, 255, id(font_small), TextAlign::BASELINE_LEFT , "12 inches");  
      it.print(603, 275, id(font_small), TextAlign::BASELINE_LEFT , "16 inches"); 
      it.print(765, 215, id(font_small), TextAlign::BASELINE_RIGHT , unit);
      it.print(765, 235, id(font_small), TextAlign::BASELINE_RIGHT , unit);
      it.print(765, 255, id(font_small), TextAlign::BASELINE_RIGHT , unit);
      it.print(765, 275, id(font_small), TextAlign::BASELINE_RIGHT , unit);

      /* Temperature Graph */
      it.print(280, 310, id(font_large), "Temperature Graph");
      it.graph(280, 335, id(temperature_graph));
      
      /* FOOTER */
      it.strftime(380, 460, id(footer_font), TextAlign::CENTER_HORIZONTAL , "Updated at %d/%b/%Y %H:%M", id(homeassistant_time).now());
      
      /* WiFi Signal Strenght */
      if(id(wifisignal).has_state()) {
        int x = 478;
        int y = 472;
        if (id(wifisignal).state >= -50) {
            //Excellent
            it.print(x, y, id(mdi_wifi), TextAlign::BASELINE_CENTER, "󰤨");
            ESP_LOGI("WiFi", "Exellent");
        } else if (id(wifisignal).state  >= -60) {
            //Good
            it.print(x, y, id(mdi_wifi), TextAlign::BASELINE_CENTER, "󰤥");
            ESP_LOGI("WiFi", "Good");
        } else if (id(wifisignal).state  >= -75) {
            //Fair
            it.print(x, y, id(mdi_wifi), TextAlign::BASELINE_CENTER, "󰤢");
            ESP_LOGI("WiFi", "Fair");
        } else if (id(wifisignal).state  >= -100) {
            //Weak
            it.print(x, y, id(mdi_wifi), TextAlign::BASELINE_CENTER, "󰤟");
            ESP_LOGI("WiFi", "Weak");
        } else {
            //Unlikely working signal
            it.print(x, y, id(mdi_wifi), TextAlign::BASELINE_CENTER, "󰤫");
            ESP_LOGI("WiFi", "Unlikely");
        }
      }

hey! its been a while and your code seems unavailable.
Would you mind putting it somewhere? :slight_smile:

Finally, I had the time, @Johan71, to dive into the Formula 1 API. I’ve updated my code to pull data directly from the API, you can check it out here

3 Likes

Hi all !
I am currently using the Google Calendar integration to display my Google agenda in Home Assistant, and more specifically to show the next event on my e-paper display using the following code:

- platform: template
    sensors:
      calendrier_evenement:
        value_template: "{{ states.calendar.agenda_perso.attributes.message | replace('\xc2', '') | replace('\xa0', ' ') | trim }}"
      calendrier_message:
        value_template: "{{ states.calendar.agenda_perso.attributes.description | replace('\xc2', '') | replace('\xa0', ' ') | trim }}"
      calendrier_debut:
        value_template: >-
          {% set mois_fr = {
              'January': 'Janvier',
              'February': 'Février',
              'March': 'Mars',
              'April': 'Avril',
              'May': 'Mai',
              'June': 'Juin',
              'July': 'Juillet',
              'August': 'Août',
              'September': 'Septembre',
              'October': 'Octobre',
              'November': 'Novembre',
              'December': 'Décembre'
          } %}
          {% set jours_fr = {
              0: 'Lundi',
              1: 'Mardi',
              2: 'Mercredi',
              3: 'Jeudi',
              4: 'Vendredi',
              5: 'Samedi',
              6: 'Dimanche'
          } %}
          {% set date_start = strptime(states.calendar.agenda_perso.attributes.start_time, "%Y-%m-%d %H:%M:%S") %}
          {{ jours_fr[date_start.weekday()] + " " + date_start.strftime(" %d") + " " + mois_fr[date_start.strftime("%B")] + " " + date_start.strftime("%Y à %H:%M") }}

This allows me to have 3 entities:

  • sensor.calendrier_debut: the date of the next event in the format: “Jeudi 03 Avril 2025 à 14:00”
  • sensor.calendrier_evenement: the name of the event
  • sensor.calendrier_message: displays the event’s description, if available

I would like to improve this setup to display not just the next event, but the next 3 upcoming events. Is this possible?

Thanks in advance for your help!