Seeed Studio reTerminal E1002 Color ePaper Display - Home Assistant Dashboard

I got a reTerminal E1002 and thought I’d share my dashboard and ESPHome code. I also have a XIAO 7.5" ePaper Display and was able to use a lot of the code from that project.

The E100x displays have a much better build (metal case) with programmable buttons, MicroSD card support, leds and buzzer. It also has battery status which was my biggest issue with the XIAO 7.5".

The biggest con to the E1002 (color version) is that the refresh takes a long time (sometimes 30 secs) because it looks like it flashes every color for every element. It flashes like crazy while doing the refresh. I don’t have and numbers yet, but it looks like the battery life is less that the B&W epaper. I’ll update this topic when I have a rough number of days.

I’m still messing with the colors. Haven’t settled on what works best yet.

Pictures:


You will need the Puppet Addon.

ESPHome Builder YAML:

esphome:
  name: reterminal-e1002
  friendly_name: ColorEinkDisplay
  on_boot:
    - priority: 600
      then:
        - output.turn_on: bsp_sd_enable
        - output.turn_on: bsp_battery_enable
        - delay: 200ms
        - component.update: battery_voltage
        - component.update: battery_level
    - priority: -100 
      then:
        - logger.log: "*** Device woke up from deep sleep ***"
        - light.turn_on: onboard_led
        - delay: 1s
        - logger.log: "*** Starting application ***"      

esp32:
  board: esp32-s3-devkitc-1
  framework:
#    type: arduino
    type: esp-idf

# Enable logging
logger:
  hardware_uart: UART0

# Enable Home Assistant API
api:
  encryption:
    key: "<Your API Key>"
  on_client_connected:
    - logger.log:
        format: "*** Client %s connected to API with IP %s ***"
        args: ["client_info.c_str()", "client_address.c_str()"]

ota:
  - platform: esphome
    password: "<Your OTA PW>"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  min_auth_mode: WPA2
  on_connect:
    then:
      - lambda: |-
          id(wifi_status) = 1;
      - logger.log: "*** WiFi Connected Successfully! ***"
      - delay: 1s
      - component.update: dashboard_image          
  on_disconnect:
    then:
      - lambda: |-
          id(wifi_status) = 0;
      - logger.log: "*** WiFi Disconnected ***"
  ap:
    ssid: "ColoreInkFallbackHotspot"
    password: "<Your Fallback Hotspot PW>"

psram:
  mode: octal
  speed: 80MHz


captive_portal:

globals:
  - id: sleep_counter
    type: int
    restore_value: yes  # Use RTC storage to maintain counter during sleep
    initial_value: '0'  
  - id: battery_glyph
    type: std::string
    restore_value: no
    initial_value: "\"\\U000F0079\""   # default full battery
  - id: wifi_status
    type: int
    restore_value: no
    initial_value: "0"
  - id: recorded_display_refresh
    type: int
    restore_value: yes
    initial_value: '0' 

# Deep-sleep, wake by GPIO3
deep_sleep:
  id: deep_sleep_1
  run_duration: 120s  # Device wake up and run 120s. This should not run for 120s because of other code
  sleep_duration: 30min  # deep sleep for 30m
  wakeup_pin: GPIO3          # Green button
  wakeup_pin_mode: INVERT_WAKEUP

# SPI bus for display
spi:
  clk_pin: GPIO7
  mosi_pin: GPIO9
# I2C bus for temperature and humidity sensor
i2c:
  scl: GPIO20
  sda: GPIO19

http_request:
  verify_ssl: false
  timeout: 20s
  watchdog_timeout: 25s

online_image:
  - id: dashboard_image
    format: PNG
    type: RGB565
    buffer_size: 65536
    url: http://192.168.1.101:10000/dashboard-robin/display-color?viewport=800x480&lang=en
#    update_interval: 20s.  # Not needed now
    on_download_finished:
      - component.update: epaper_display
      - delay: 45s # Time to allow display to refresh
      - deep_sleep.enter: deep_sleep_1
    on_error: 
      - delay: 30s
      - deep_sleep.enter: deep_sleep_1

display:
  - platform: epaper_spi
    id: epaper_display
    model: Seeed-reTerminal-E1002
    update_interval: never
    lambda: |-
      it.image(0, 0, id(dashboard_image));

# Home Assistant time
time:
  - platform: homeassistant
    id: ha_time

sensor:
  - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
    update_interval: 60s
    name: "WiFi Signal dB"
    id: wifi_signal_db
    entity_category: "diagnostic"
  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    id: wifi_signal_percent
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"
  - platform: uptime
    update_interval: 60s    
    name: Uptime
  - platform: internal_temperature
    update_interval: 60s    
    name: "Internal Temperature"
  - platform: template
    update_interval: 60s      
    name: "Display Last Update"
    device_class: timestamp
    entity_category: "diagnostic"
    id: display_last_update
    lambda: 'return id(ha_time).now().timestamp;'
  - platform: template
    name: "Display Refresh Count"
    accuracy_decimals: 0
    unit_of_measurement: "Refreshes"
    state_class: "total_increasing"
    entity_category: "diagnostic"
    lambda: 'return id(recorded_display_refresh) += 1;'  
  - platform: sht4x
    update_interval: 60s      
    temperature:
      name: "Temperature"
      id: temp_sensor
    humidity:
      name: "Relative Humidity"
      id: hum_sensor
  - platform: adc
    update_interval: 60s
    pin: GPIO1
    name: "Battery Voltage"
    id: battery_voltage
    attenuation: 12db
    filters:
      - multiply: 2.0
  - platform: template
    update_interval: 60s
    name: "Battery Level"
    id: battery_level
    unit_of_measurement: "%"
    icon: "mdi:battery"
    device_class: battery
    state_class: measurement
    lambda: 'return id(battery_voltage).state;'
    on_value:
      then:
        - lambda: |-
            int pct = int(x);
            if (pct <= 10)      id(battery_glyph) = "\U000F007A";
            else if (pct <= 20) id(battery_glyph) = "\U000F007B";
            else if (pct <= 30) id(battery_glyph) = "\U000F007C";
            else if (pct <= 40) id(battery_glyph) = "\U000F007D";
            else if (pct <= 50) id(battery_glyph) = "\U000F007E";
            else if (pct <= 60) id(battery_glyph) = "\U000F007F";
            else if (pct <= 70) id(battery_glyph) = "\U000F0080";
            else if (pct <= 80) id(battery_glyph) = "\U000F0081";
            else if (pct <= 90) id(battery_glyph) = "\U000F0082";
            else                id(battery_glyph) = "\U000F0079";
    filters:
      - calibrate_linear:
          - 4.15 -> 100.0
          - 3.96 -> 90.0
          - 3.91 -> 80.0
          - 3.85 -> 70.0
          - 3.80 -> 60.0
          - 3.75 -> 50.0
          - 3.68 -> 40.0
          - 3.58 -> 30.0
          - 3.49 -> 20.0
          - 3.41 -> 10.0
          - 3.30 -> 5.0
          - 3.27 -> 0.0
      - clamp:
          min_value: 0
          max_value: 100

# Button configuration
binary_sensor:
  
#  - platform: gpio
#    pin:
#      number: GPIO3         # Green button, commented out because I'm using it for wakeup
#      mode: INPUT_PULLUP
#      inverted: true
#    id: button_1
#    name: "Green Button"
#    on_press:
#      then:
#        - logger.log: "*** Green Button (GPIO3) Pressed ***"
        
  - platform: gpio
    pin:
      number: GPIO4          # Right white button
      mode: INPUT_PULLUP
      inverted: true
    id: button_2
    name: "Right Button"
    on_press:
      then:
        - logger.log: "*** Right Button (GPIO4) Pressed ***"
        
  - platform: gpio
    pin:
      number: GPIO5           # Left white button
      mode: INPUT_PULLUP
      inverted: true
    id: button_3
    name: "Left Button"
    on_press:
      then:
        - logger.log: "*** Left Button (GPIO5) Pressed ***"

output:
  - platform: gpio
    pin: GPIO6
    id: bsp_led
    inverted: true
  - platform: gpio
    pin: GPIO16
    id: bsp_sd_enable
  - platform: gpio
    pin: GPIO21
    id: bsp_battery_enable

# Onboard LED
light:
  - platform: binary
    name: "Onboard LED"
    output: bsp_led
    id: onboard_led

Home Assistant Dashboard YAML:

type: masonry
path: display-color
title: Display-Color
cards:
  - type: vertical-stack
    cards:
      - show_current: true
        show_forecast: true
        type: weather-forecast
        entity: weather.kpajamis12
        forecast_type: daily
        secondary_info_attribute: humidity
        card_mod:
          style: |
            :host {
              --weather-icon-cloud-front-color: white;
              --weather-icon-cloud-back-color: black;
              --weather-icon-sun-color: yellow;
              --weather-icon-rain-color: blue;
              --weather-icon-moon-color: yellow;
            }  
            ha-card {
              border: 2px solid black;
              padding: 5px !important;
              background: red;
            }
            .name-state .name {
              color: blue;
              font-size: 16px;
            }
            .name-state .state {
              color: yellow;
              font-weight: bold;
            }
            .temp-attribute .temp {
              color: yellow;
              font-weight: bold;
              font-size: 30px !important;
            }
            .temp-attribute .temp span {
              color: yellow;
              font-size: 20px !important;
            }
            .temp-attribute .attribute {
              color: white;
              font-size: 16px;
            }
            .forecast .temp {
              color: yellow;
              font-weight: bold;
              font-size: 14px;
            }
            .forecast .templow {
              color: white;
              font-weight: bold;
              font-size: 14px;      
            }
            .forecast div {
              font-weight: bold;
              color: white;
            }
            ha-card div.forecast div.temp {
              font-size: 14pt;
            } 
      - type: horizontal-stack
        cards:
          - graph: line
            type: sensor
            detail: 2
            entity: sensor.home_thermostat_air_temperature
            name: Inside
            icon: mdi:home-thermometer
            hours_to_show: 12
            card_mod:
              style: |
                .header .icon {
                  color: white;
                }
                ha-card {
                  border: 2px solid black;
                  --primary-text-color: yellow;
                  font-weight: bold;
                  --secondary-text-color: white;
                  background: green;
                  --accent-color: white;
                  --ha-font-size-3xl: 46px;
                }        
          - graph: line
            type: sensor
            detail: 2
            name: Outside
            icon: mdi:sun-thermometer-outline
            hours_to_show: 12
            entity: sensor.back_deck_temp_temperature
            card_mod:
              style: |
                .header .icon {
                  color: white;
                }  
                ha-card {
                  border: 2px solid black;
                  --primary-text-color: yellow;
                  font-weight: bold;
                  --secondary-text-color: white;
                  background: green;
                  --accent-color: white;
                  --ha-font-size-3xl: 46px;
                }        
      - type: horizontal-stack
        cards:
          - graph: none
            type: sensor
            detail: 2
            icon: ""
            hours_to_show: 12
            entity: sensor.back_deck_temp_humidity
            name: Humidity
            card_mod:
              style: |
                .header .icon {
                  color: white;
                }
                ha-card {
                  border: 2px solid black;
                  --primary-text-color: white;
                  font-weight: bold;
                  --secondary-text-color: white;
                  background: blue;
                  --accent-color: white;
                }        
          - graph: none
            type: sensor
            detail: 2
            entity: sensor.topwindspeedhr
            name: Wind
            icon: mdi:weather-windy
            hours_to_show: 12
            card_mod:
              style: |
                .header .icon {
                  color: white;
                }
                ha-card {
                  border: 2px solid black;
                  --primary-text-color: white;
                  font-weight: bold;
                  --secondary-text-color: white;
                  background: blue;
                  --accent-color: white;
                }      
          - graph: none
            type: sensor
            detail: 2
            name: Rain
            icon: ""
            hours_to_show: 24
            entity: sensor.kpajamis12_precipitation_today
            card_mod:
              style: |
                .header .icon {
                  color: white;
                }
                ha-card {
                  border: 2px solid black;
                  --secondary-text-color: white;
                  --primary-text-color: white;
                  font-weight: bold;
                  background: blue;
                  --accent-color: white;
                }             
  - type: vertical-stack
    cards:
      - type: custom:mushroom-chips-card
        chips:
          - type: entity
            entity: sensor.date_time
            icon_color: yellow
            card_mod:
              style: |
                ha-card {
                  --chip-background: green;
                  --chip-height: 20px;
                  --chip-border-radius: 12px;
                  --chip-icon-size: 12px;
                  --chip-font-size: 12px;
                  --text-color: yellow;
                  border: 2px solid black !important;
                } 
          - type: spacer
          - type: entity
            entity: sensor.coloreinkdisplay_wifi_signal_percent
            icon_color: yellow
            card_mod:
              style: |
                ha-card {
                  --chip-background: green;
                  --chip-height: 20px;
                  --chip-border-radius: 12px;
                  --chip-icon-size: 12px;
                  --chip-font-size: 12px;
                  --text-color: yellow;
                  border: 2px solid black !important;
                }   
          - type: entity
            entity: sensor.coloreinkdisplay_battery_level
            icon_color: yellow
            card_mod:
              style: |
                ha-card {
                  --chip-background: green;
                  --chip-height: 20px;
                  --chip-border-radius: 12px;
                  --chip-icon-size: 12px;
                  --chip-font-size: 12px;
                  --text-color: yellow;
                  border: 2px solid black !important;
                } 
        alignment: center
      - entities:
          - entity: calendar.birthdays
            show_time: true
            color: white
            accent_color: white
          - entity: calendar.philadelphia_phillies
            show_time: true
            color: white
            accent_color: white
          - entity: calendar.carolina_panthers
            show_time: true
            color: white
            accent_color: white
          - entity: calendar.holidays_in_united_states
            show_time: true
            split_multiday_events: false
            color: white
            accent_color: white
          - entity: calendar.north_carolina_tar_heels_men_s_basketball
            show_time: true
            color: white
            accent_color: white
          - entity: calendar.philadelphia_eagles
            show_time: true
            color: white
            accent_color: white
          - entity: calendar.gmail_com
            show_time: true
            color: white
            accent_color: white
        days_to_show: 7
        show_empty_days: true
        filter_duplicates: true
        title: ""
        title_color: ""
        background_color: black
        accent_color: white
        day_spacing: 4px
        event_spacing: 2px
        day_separator_width: 1px
        day_separator_color: yellow
        today_indicator: dot
        today_indicator_color: green
        weekday_color: white
        day_color: white
        month_color: white
        weekend_weekday_color: yellow
        weekend_day_color: yellow
        weekend_month_color: yellow
        today_weekday_color: ""
        today_day_color: ""
        today_month_color: ""
        show_past_events: true
        event_font_size: 20px
        event_color: white
        empty_day_color: white
        time_font_size: 14px
        time_color: white
        show_location: false
        weather:
          position: none
          date:
            show_conditions: true
            show_high_temp: true
            show_low_temp: false
            icon_size: 14px
            font_size: 12px
            color: white
          event:
            show_conditions: true
            show_temp: true
            icon_size: 14px
            font_size: 12px
            color: white
          entity: weather.kpajamis12
        type: custom:calendar-card-pro
        height: 400px
        card_mod:
          style: |
            ha-card {
              border: 2px solid black;
              font-weight: bold !important;
            } 
            .day-table.today .event-title {
              font-weight: bold !important;
            }
            .day-table.tomorrow .event-title {
              font-weight: bold !important;
            }
            .day-table.future-day .event-title {
              font-weight: bold !important;
            }
            div.time {
              font-weight: bold !important;
            }
badges: []

John

10 Likes

Looks fantastic. Nice piece of inspiration.
I just started playing with the National weather forecast for NL (Royal Dutch Meteo institute), but their cloud and rain picto’s don’t convert well through Puppet. I think I can learn a lot from yours. Thanks for sharing the codes and images.

P.s. what is kpajamis12?
P.s.2: Are you using an SD card in your E1002?

1 Like

kpajamis12 is the ID for the weather station in my backyard. I send data to WeatherUnderground and that’s the ID for it.

I’m not using a SD Card. I’m not sure what I’d do with it because I’m just using the E1002 as a simple weather and calendar display. Are you going to to use it?

1 Like

Hmm - I wanted to use this for a “now playing” for either Music Assistant or Emby… BUT after seeing your note about the 30 seconds to refresh/flickering - it might not be a great idea. Any chance you can record the refresh, as it’s happening? Gotta see if I can find anyone else that did it.

Edit:
Cancel that - went down yt rabbit hole - doesn’t look great for media. six color display… if you don’t stick to those primary colors, it looks grainy.

still crazy for general dashboard.

1 Like

You can get it to work pretty nice, just need to get the dithering algorithm right. The flickering is kind of annoying but eink should be for fairly static slow changing screens anyway. Below are my dashboards for this display.

1 Like

I don’t think it would work well for a Now Playing display that needs to update frequently. It looks like the device sets each previous color to each of the other colors while updating (I’m guessing to reset the pixels). If I get a chance I’ll record it because it does limit what the display can be used for. I’m updating every 30mins, so it’s fine.

I stuck to the primary colors to avoid dithering. Your display looks good though!

Hi,

Take a look at my project GitHub - koosoli/ESPHomeDesigner: A visual drag-and-drop editor for ESPHome displays (E-Ink, OLED, LCD, Touch), running as a Home Assistant integration or a standalone web app.

This was originally called reTerminal designer and it will make your life so much easier :wink:


4 Likes

Really nicely done! I’ve managed to get Puppet working, and a dashboard showing up on it, but need to do a lot of work on exactly what is showing, fixing colors, layout, etc. I’m going to grab Calendar Card Pro which looks like a better version of what I was doing.

Here’s a couple of videos of the display updating (flashing). It seems like the length is related to how many colors you use. First one uses all colors. Second one is B&W.

Color

B&W

And yes, I need to clean the screen. :grin:

Can anyone offer advice on how to get the relatively crisp rendering y’all seem to have? I’m using Puppet to take a snapshot of a dashboard, and I’m getting really pixelated/missing colors all over the place. My guess is that I need to use more “pure” colors for everything (card-mod to force that?), or maybe do some color-matching or dithering in Puppet config, but before I go deep on playing with that, wanted to check if I’m missing something else.

Here’s what I have currently

Using true colors (Black, White, Red, Blue, Yellow, Green) will definitely help. If you use lighter or off colors it might not pick the right color to use. Also, bolding your fonts will help.

Some of that can be done in configs, but I had to use Card Mod to do most of it. Look at my YAML in the first post for examples.

1 Like

reTerminal E1002
i love this software
Is there a way to significantly shorten the image processing time? Otherwise, the reTerminal E1002 is unusable.

The fewer colors you use the less time, but even then it’s 15 secs for Black & White only. I haven’t found a way to reduce it yet, but I’m still trying.

The B&W version (E1001) will be significantly shorter (around 5 secs), but you will of course be limited to Black and White.

Battery life seems to be about 1/2 of the B&W model. I’m sure this is due to the long refresh times.

It looks like it last about 18 days with every 30 mins refreshes that are live for 45 secs. I’m charging it again now and will start a new battery days counter.

I did find a parameter to control when to do a full refresh, but I’m not sure if this display supports it. I’m trying it out now.

full_update_every: (Optional, int): On screens that support partial updates, this sets the number of updates before a full update is forced. Defaults to 1 which will make every update a full update.

display:
  - platform: epaper_spi
    id: epaper_display
    model: Seeed-reTerminal-E1002
    full_update_every: 10
    update_interval: never
    lambda: |-
      it.image(0, 0, id(dashboard_image));