ESPHome - Pogodynka na ekranie e-ink ☁

OPIS

Przedstawiam projekt pogodynki na ekranie e-ink z układem ESP32.
Kod nie jest mój, jest to zlepek kilku projektów i dostosowany do moich danych w Home Assistant.
Ekran odświeża się domyślnie przy wykryciu ruchu w pomieszczeniu, jeśli dane z czujników się zmieniły.
Ja zrobiłem pomocnika przełącznik, który robi za fałszywy czujnik ruchu, wtedy ekran odświeża się przy odebraniu danych, pozwala mi to na wyłączanie odświeżania ekranu, jeśli nikogo nie ma w domu, czy podczas snu.
Sam projekt można przerobić na układ z baterią i użyć funkcji deep sleep.
W samym Home Assistant mamy podgląd ilości, czasu odświeżania itp.




Sprzęt

Oprogramowanie

  • ESPHome
  • Home Assistant

Źródła danych

  • Integracja Met.no
  • Integracja Thermal Comfort
  • Integracja sun oraz moon
  • Lokalny czujnik temperatury, wilgotności oraz ciśnienia
  • Sensor z dniem, nazwą tygodnia i miesiąca link

Instalacja

Nie jest wymagane lutowanie, ponieważ ekran e-ink należy połączyć taśmą z płytą ESP32.
Skopiować /font do folderu /config/esphome/
font.zip (727,8 KB)

Utworzyć w Home Assistant template

  - binary_sensor:
      - name: Weatherman Motion Detected
        unique_id: "dfa78de7-d761-425f-9731-86f1af332eac"
        device_class: "occupancy"
        delay_off: 1min
        state: >-
          {%- if states('input_boolean.eink_czujnik_ruchu') == 'on' %}
            on
          {%- else -%}
            off
          {%- endif -%}
  # Bundle up all the data to send over to Weatherman (ESPHome device).

  - trigger:
      platform: time_pattern
      minutes: "/1"
    action:
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: weather.forecast_home
        response_variable: hourly
    sensor:
      - name: Weatherman Data
        state: "OK"
        attributes:
          weather_condition_now: >
            {% set cond_now = states('weather.forecast_home') %}
            {% if states('sun.sun') == 'below_horizon' %}
                {% if cond_now == 'sunny' %} night {% elif cond_now == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond_now }} {% endif %}
            {% else %}
                {{ cond_now }}
            {% endif %}
            
          weather_condition_0: >
            {% set cond0 = hourly["weather.forecast_home"].forecast[0].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond0_time = as_timestamp(hourly["weather.forecast_home"].forecast[0].datetime) %}
            {% if cond0_time < next_rising and next_rising < next_setting %}
                {% if cond0 == 'sunny' %} night {% elif cond0 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond0 }} {% endif %}
            {% else %}
                {{ cond0 }}
            {% endif %}
          weather_temperature_0: >
            {{ hourly["weather.forecast_home"].forecast[0].temperature | round }}
          weather_timestamp_0: >
            {{ as_timestamp(hourly["weather.forecast_home"].forecast[0].datetime) | timestamp_custom('%H') | int }}
            
          weather_condition_1: >
            {% set cond1 = hourly["weather.forecast_home"].forecast[1].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond1_time = as_timestamp(hourly["weather.forecast_home"].forecast[1].datetime) %}
            {% if cond1_time < next_rising and next_rising < next_setting %}
                {% if cond1 == 'sunny' %} night {% elif cond1 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond1 }} {% endif %}
            {% else %}
                {{ cond1 }}
            {% endif %}
          weather_temperature_1: >
            {{ hourly["weather.forecast_home"].forecast[1].temperature | round }}
          weather_timestamp_1: >
            {{ as_timestamp(hourly["weather.forecast_home"].forecast[1].datetime) | timestamp_custom('%H') | int }}
            
          weather_condition_2: >
            {% set cond2 = hourly["weather.forecast_home"].forecast[2].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond2_time = as_timestamp(hourly["weather.forecast_home"].forecast[2].datetime) %}
            {% if cond2_time < next_rising and next_rising < next_setting %}
                {% if cond2 == 'sunny' %} night {% elif cond2 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond2 }} {% endif %}
            {% else %}
                {{ cond2 }}
            {% endif %}
          weather_temperature_2: >
            {{ hourly["weather.forecast_home"].forecast[2].temperature | round }}
          weather_timestamp_2: >
            {{ as_timestamp(hourly["weather.forecast_home"].forecast[2].datetime) | timestamp_custom('%H') | int }}
            
          weather_condition_3: >
            {% set cond3 = hourly["weather.forecast_home"].forecast[3].condition %}
            {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
            {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
            {% set cond3_time = as_timestamp(hourly["weather.forecast_home"].forecast[3].datetime) %}
            {% if cond3_time < next_rising and next_rising < next_setting %}
                {% if cond3 == 'sunny' %} night {% elif cond3 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond3 }} {% endif %}
            {% else %}
                {{ cond3 }}
            {% endif %}
          weather_temperature_3: >
            {{ hourly["weather.forecast_home"].forecast[3].temperature | round }}
          weather_timestamp_3: >
            {{ as_timestamp(hourly["weather.forecast_home"].forecast[3].datetime) | timestamp_custom('%H') | int }}

Zainstalować poniższy kod na płytę ESP32 za pomocą ESPHome.

# 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: eink
  friendly_name: eink
  on_boot:
      priority: 200.0
      then:
        - component.update: eink_display
        - wait_until:
            condition:
              lambda: 'return id(data_updated) == true;'
              # Poczekaj trochę dłużej, aby wszystkie elementy zostały odebrane
        - delay: 5s
        - logger.log: "Odebrano wstępne dane czujnika: Odświeżanie ekranu..."
        - lambda: 'id(initial_data_received) = true;'
        - script.execute: update_screen
esp32:
  board: esp32dev
  framework:
    type: arduino

logger:

api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

ota:
  password: "xxxxxxxxxxxxxxxxxxxxxxx"

web_server:
  port: 80

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  ap:
    ssid: "Eink Fallback Hotspot"
    password: "xxxxxxxxx"
   
captive_portal:

# Dołącz czas
# Sprawdź, czy wyświetlacz wymaga odświeżania co minutę,
# w zależności od tego, czy odebrano nowe dane, czy wykryto ruch. (Thanks @paviro!)
time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      - seconds: 0
        minutes: /30
        then:
          - if:
              condition:
                lambda: 'return id(data_updated) == true;'
              then:
                - if:
                    condition:
                      binary_sensor.is_on: motion_detected
                    then:
                      - logger.log: "Zaktualizowano dane czujnika i wykryto aktywność w domu: odświeżanie wyświetlacza..."
                      - script.execute: update_screen
                    else:
                      - logger.log: "Dane czujnika zostały zaktualizowane, ale w domu brak aktywności – pominięto odświeżanie wyświetlacza."
              else:
                - logger.log: "Brak aktualizacji czujników – pominięto odświeżanie wyświetlacza."


# Przyciski
button:
  - platform: shutdown
    name: "eink - Wyłączenie"
  - platform: restart
    name: "eink - Restart"
  - platform: template
    name: "eink - Odświeżanie ekranu"
    entity_category: config
    on_press:
      - script.execute: update_screen

# Zmienne globalne do wykrywania, czy ekran wymaga odświeżenia. (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'


# Skrypt do aktualizacji ekranu - Odśwież wyświetlacz i opublikuj liczbę i czas odświeżenia. (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);'
      

# Dołącz niestandardową czcionkę
font:
  - file: 'font/GothamRnd-Bold.ttf'
    id: font_large_bold
    size: 125
  - file: 'font/GothamRnd-Bold.ttf'
    id: font_large_bold_c
    size: 90
  - file: 'font/GothamRnd-Book.ttf'
    id: font_time
    size: 150
  - file: 'font/RobotoCondensed-Regular.ttf'
    id: font_weekday
    size: 50
    glyphs: ['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: 'font/RobotoCondensed-Light.ttf'
    id: font_day
    size: 50
    glyphs: ['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: 'font/GothamRnd-Bold.ttf'
    id: font_month
    size: 50
  - file: 'font/GothamRnd-Bold.ttf'
    id: font_medium_bold
    size: 42
  - file: 'font/GothamRnd-Bold.ttf'
    id: font_small_bold
    size: 22

  # Dołącz czcionkę Material Design Icons
  # Dzięki https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16
  - file: 'font/materialdesignicons-webfont.ttf'
    id: font_mdi_extra_large
    size: 140
    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
      - "\U000F002A" # mdi-alert-outline
      - "\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
      - "\U000F0E03" # mdi-thermometer-chevron-up
      - "\U000F0E02" # mdi-thermometer-chevron-down
      - "\U000F1A09" # mdi-triangle-small-down
      - "\U000F1A0A" # mdi-triangle-small-up
      - "\U000F02CA" # mdi-hadrdisk
      - "\U000F029A" # mdi-gauge
  - file: 'font/materialdesignicons-webfont.ttf'
    id: font_mdi_moon
    size: 60
    glyphs:
      - "\U000F058E" # mdi-water-percent
      - "\U000F04C5" # mdi-spedometer
      - "\U000F0F29" # mdi-snowflake-alert
      - "\U000F002A" # mdi-alert-outline
      - "\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: 'font/materialdesignicons-webfont.ttf'
    id: font_mdi_medium
    size: 50
    glyphs: *mdi-weather-glyphs
  - file: 'font/materialdesignicons-webfont.ttf'
    id: font_mdi_large
    size: 80
    glyphs: *mdi-weather-glyphs

# Sprawdź, czy w salonie wykryto ruch. (czy załączony pomocnik ruch)
binary_sensor:
  - platform: homeassistant
    entity_id: input_boolean.eink_czujnik_ruchu
    id: motion_detected

# Czujniki pogodowe z HA
sensor:
  # Twórz czujniki do zdalnego monitorowania Weatherman.
  - platform: template
    name: "eink - Czas aktualizacji wyświetlacza"
    device_class: timestamp
    entity_category: "diagnostic"
    id: display_last_update
    
  - platform: template
    name: "eink - Zarejestrowane Oswieżanie wyświetlacza"
    accuracy_decimals: 0
    unit_of_measurement: "Odświeżenia"
    state_class: "total_increasing"
    entity_category: "diagnostic"
    lambda: 'return id(recorded_display_refresh);'

  # Temperatura — moja stacja pogodowa
  - platform: homeassistant
    entity_id: sensor.netatmo_ogrod_temperature
    id: weather_temperature
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'
        
  # Wilgotność - moja stacja pogodowa
  - platform: homeassistant
    entity_id: sensor.netatmo_ogrod_humidity
    id: weather_humidity
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Ciśnienie - moja stacja pogodowa
  - platform: homeassistant
    entity_id: sensor.netatmo_wewnatrz_pressure
    id: weather_pressure
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Ilość opadu deszczu
  - platform: homeassistant
    entity_id: sensor.netatmo_czujnik_deszczu_rain_today
    id: ilosc_opadu
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Weatherman encje
  - 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:
  # Dzień tygodnia
  - platform: homeassistant
    entity_id: sensor.dzien_tygodnia
    id: nazwa_dnia
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Miesiąc
  - platform: homeassistant
    entity_id: sensor.miesiac
    id: miesiac
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Dzień
  - platform: homeassistant
    entity_id: sensor.dzien_miesiaca
    id: dzien
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Weatherman encje
  - 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;'

  # Wschód słońca
  - platform: homeassistant
    entity_id: sensor.czas_wschodu_slonca
    id: sun_sunrise
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Zachód słońca
  - platform: homeassistant
    entity_id: sensor.czas_zachodu_slonca
    id: sun_sunset
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Ryzyko mrozu
  - platform: homeassistant
    entity_id: sensor.thermal_comfort_frostrisk
    id: frost_risk
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Meteoalarm
  - platform: homeassistant
    entity_id: binary_sensor.meteoalarm
    id: meteoalarm
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

  # Faza Księżyca
  - platform: homeassistant
    entity_id: sensor.ksiezyc
    id: moon_phase
    on_value:
      then:
        - lambda: 'id(data_updated) = true;'

# Poprawka odświeżania
#external_components:
#  - source:
#      type: git
#      url: https://github.com/prapador/esphome_waveshare_fix
#      ref: dev
#    components: [ waveshare_epaper ]

# Zdefiniuj kolory
color:
  - id: color_black
    red: 0%
    green: 0%
    blue: 0%
    white: 50%
  - id: color_white
    red: 0%
    green: 0%
    blue: 0%
    white: 0%


# Piny Waveshare ePaper ESP Board
spi:
  clk_pin: GPIO13
  mosi_pin: GPIO14


# Teraz wyrenderuj wszystko na ekranie ePaper.
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°
    auto_clear_enabled: false
    lambda: |-
      // Map weather states to MDI characters.
      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"},
          {"waxing_crescent", "\U000F0F67"},
          {"waxing_gibbous", "\U000F0F68"},
          
        };
      std::map<std::string, std::string> weather_icon_map
        {
          {"cloudy", "\U000F0590"},
          {"cloudy-alert", "\U000F0F2F"},
          {"cloudy-arrow-right", "\U000F0E6E"},
          {"fog", "\U000F0591"},
          {"hail", "\U000F0592"},
          {"hazy", "\U000F0F30"},
          {"hurricane", "\U000F0898"},
          {"lightning", "\U000F0593"},
          {"lightning-rainy", "\U000F067E"},
          {"night", "\U000F0594"},
          {"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"},
        };

      // Wypełnij tło
      it.fill(color_white);

      // ------------------------------------------------------------Pokaż ekran ładowania przed odebraniem danych. ------------------------------------------------------
      if (id(initial_data_received) == false) {
        it.printf(300, 250, id(font_mdi_extra_large), color_black, TextAlign::CENTER_RIGHT, "\U000F02CA");
        it.printf(240, 390, id(font_medium_bold), color_black, TextAlign::TOP_CENTER, "Czekam na dane...");
      } else {
      // ----------------------------------------------------------------------------------- Dzień tygodnia ------------------------------------------------------------
      it.printf(210, 70, id(font_weekday), color_black, TextAlign::TOP_LEFT, "%s", id(nazwa_dnia).state.c_str());
      // ----------------------------------------------------------------------------------- Dzień miesiąca duże ------------------------------------------------------
      it.printf(220, 65, id(font_time), color_black, TextAlign::TOP_RIGHT, "%s", id(dzien).state.c_str());
      // ----------------------------------------------------------------------------------- Miesiąc ------------------------------------------------------------------
      it.printf(210, 140, id(font_day), color_black, TextAlign::TOP_LEFT, "%s", id(miesiac).state.c_str());

      // ----------------------------------------------------------------------------------- Wschód słońca ------------------------------------------------------------
        if(id(sun_sunrise).has_state ()) {
            it.printf(95, 235, id(font_mdi_medium), color_black, TextAlign::CENTER_RIGHT, "\U000F059C");
            it.printf(105, 235, id(font_small_bold), color_black, TextAlign::CENTER_LEFT, "%s", id(sun_sunrise).state.c_str());
        }      
      // ----------------------------------------------------------------------------------- Zachód słońca ------------------------------------------------------------
        if(id(sun_sunset).has_state ()) {
            it.printf(450, 235, id(font_mdi_medium), color_black, TextAlign::CENTER_RIGHT, "\U000F059B");
            it.printf(395, 235, id(font_small_bold), color_black, TextAlign::CENTER_RIGHT, "%s", id(sun_sunset).state.c_str());
        }
      // ----------------------------------------------------------------------------------- Faza księżyca ikona ------------------------------------------------------
        if(id(moon_phase).has_state ()) {
            it.printf(255, 240, id(font_mdi_moon), color_black, TextAlign::CENTER, "%s", moon_icon_map[id(moon_phase).state.c_str()].c_str());
        }     
      // ----------------------------------------------------------------------------------- Wilgotność ----------------------------------------------------------------
        if(id(weather_humidity).has_state ()) {
            it.printf(88, 332, id(font_mdi_medium), color_black, TextAlign::BASELINE_RIGHT, "\U000F058E");
            it.printf(151, 330, id(font_month), color_black, TextAlign::BASELINE_RIGHT, "%2.0f", id(weather_humidity).state);
            it.printf(155, 330, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "%s", "%");
        }      
      // ----------------------------------------------------------------------------------- Ciśnienie -----------------------------------------------------------------
        if(id(weather_pressure).has_state ()) {
            it.printf(293, 332, id(font_mdi_medium), color_black, TextAlign::BASELINE_RIGHT, "\U000F04C5");
            it.printf(415, 330, id(font_month), color_black, TextAlign::BASELINE_RIGHT, "%2.0f", id(weather_pressure).state);
            it.printf(420, 330, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "hPa");
        }
      // ----------------------------------------------------------------------------------- Meteoalarm ----------------------------------------------------------------
        if(id(meteoalarm).has_state ()) {
            if(id(meteoalarm).state != "off") {
                it.printf(350, 440, id(font_mdi_large), color_black, TextAlign::BASELINE_LEFT, "\U000F002A");
            }
        }
      // ----------------------------------------------------------------------------------- Ostrzeżenie przed mrozem --------------------------------------------------
        if(id(frost_risk).has_state ()) {
            if(id(frost_risk).state != "no_risk") {
                it.printf(75, 440, id(font_mdi_large), color_black, TextAlign::BASELINE_LEFT, "\U000F0F29");
            }
        }
      // ----------------------------------------------------------------------------------- Ilość opadu deszczu -------------------------------------------------------
        if(id(ilosc_opadu).has_state ()) {
            if(id(ilosc_opadu).state > 0) {
            it.printf(370, 470, id(font_small_bold), color_black, TextAlign::BASELINE_RIGHT, "%.1f", id(ilosc_opadu).state);
            it.printf(375, 470, id(font_small_bold), color_black, TextAlign::BASELINE_LEFT, "mm");
            }
        }

      // ----------------------------------------------------------------------------------- Stan pogody ikona ---------------------------------------------------------
            it.printf(252, 340, id(font_mdi_extra_large), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str());
      // ----------------------------------------------------------------------------------- TEMPERATURA ---------------------------------------------------------------
            it.printf(330, 482, id(font_large_bold), color_black, TextAlign::TOP_RIGHT, "%.1f", id(weather_temperature).state);
            it.printf(460, 508, id(font_large_bold_c), color_black, TextAlign::TOP_RIGHT, "°C");
      // ----------------------------------------------------------------------------------- Prognoza + 1 ---------------------------------------------------------------
        if(id(weather_temperature_0).has_state ()) {
            it.printf(83, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_0).state.c_str());
            it.printf(83, 630, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_0).state.c_str()].c_str());
            it.printf(83, 680, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_0).state);
        }
      // ----------------------------------------------------------------------------------- Prognoza + 2 ---------------------------------------------------------------  
        if(id(weather_temperature_1).has_state ()) {
            it.printf(195, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_1).state.c_str());
            it.printf(195, 630, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_1).state.c_str()].c_str());
            it.printf(195, 680, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_1).state);
        }
      // ----------------------------------------------------------------------------------- Prognoza + 3 ---------------------------------------------------------------
        if(id(weather_temperature_2).has_state ()) {
            it.printf(305, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_2).state.c_str());
            it.printf(305, 630, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_2).state.c_str()].c_str());
            it.printf(305, 680, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_2).state);
        }
      // ----------------------------------------------------------------------------------- Prognoza + 4 ---------------------------------------------------------------
        if(id(weather_temperature_3).has_state ()) {
            it.printf(415, 602, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_3).state.c_str());
            it.printf(415, 630, id(font_mdi_medium), color_black, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_3).state.c_str()].c_str());
            it.printf(415, 680, id(font_small_bold), color_black, TextAlign::TOP_CENTER, "%2.0f°C", id(weather_temperature_3).state);
        }
      } 

10 Likes

Wygląda świetnie. Gratulacje.

Nie potrafię utworzyc sensora z dniem, nazwą tygodnia i miesiąca, ktoś podpowie?

w configuration.yaml mam dodane

sensor: !include sensor.yaml

a w utworzonym pliku sensor.yaml

  - platform: template
    sensors:
      dzien_tygodnia:
        friendly_name: "Dzień tygodnia"
        icon_template: mdi:calendar-range-outline
        value_template: "{{ ['Poniedziałek','Wtorek','Środa','Czwartek','Piątek','Sobota','Niedziela'][now().weekday()] }}"

      dzien_miesiaca:
        friendly_name: "Dzień miesiąca"
        icon_template: mdi:calendar-outline
        value_template: "{{ ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31' ][ now().day-1] }}"

      miesiac:
        friendly_name: "Miesiąc"
        icon_template: mdi:calendar-month-outline
        value_template: "{{ ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'][now().month-1] }}"
1 Like

Dziękuję bardzo za naprowadzenie. Pozdrawiam

Więcej szczegołów na:

Cześć.
Ponieważ kupiłem sobie u chińczyków kolorowy wyświetlacz 2 cale z enkoderem i jednym przyciskiem za zawrotną kwotę 8 zł to w ramach testu i zabawy próbuję wzorując się na tym projekcie zrobić “wersję dla ubogich” pogodynki z podziałem na strony.

Pierwszy problem to pobieranie danych pogodowych. Zrobiłem template w config ale utworzyły się tylko dwie encje: Motion detected i Data - brak temperatur, pogody wschodu słońca itp.
W pliku zmieniłem jedynie encje bo zamiast home mam dom:

    action:
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: weather.forecast_dom

Drugi problem to integracja Thermal Comfort - niby działa i pokazuje sensory ale próba wejścia w ustawienia kończy się komunikatem: “Nie udało się wczytać interfejsu konfiguracji: 500 Internal Server Error Server got itself in trouble” nie wyświetla też wersji.
Minął rok i sporo mogło się zmienić?

Zacznijmy od początku.
U siebie używam odwołań !include do plików konfiguracyjnych jak w temacie Template sensors - #2 przez artpc
Masz tak samo czy template utworzyłeś w configuration.yaml?
Trzeba wtedy dostosować wcięcia.
Pogodynka działa do dziś

Tamtego tematu nie znalazłem już czytam i poprawiam.
Właśnie te wcięcia poprawiałem i brak myślnika przed sensorami ale zaraz się douczę.
Dzięki za pomoc

dodaj do configuration.yaml wpis o template (inne na razie nie są potrzebne), utwórz plik z rozszerzeniem template.yaml i w nim już możesz umieścić kod, wcięcia będą prawidłowe. Oczywiście zadziała po resecie.

Wyskakuje mi taki komunikat:

Incomplete template configuration

Ostrzeżenie · Zgłoszone przez Template

A trigger template configuration needs a trigger and at least one domain when defining an entity. This will be an configuration validation error in Home Assistant Core 2026.5.

Please remove the orphaned trigger from the configuration.

triggers:
  platform: time_pattern
  minutes: /1
actions:
- service: weather.get_forecasts
  data:
    type: hourly
  target:
    entity_id: weather.forecast_dom
  response_variable: hourly

Czy sekcja sensors ma być w template? Aby te czujniki się pojawiły musiałem dodać myślnik przed sensor ale i tak się nie pobrały danych.
Jak zostawiam bez myślnika to powyższy błąd się nie pojawia ale też czujników nie ma.

trigger nie triggers

Ja mam tak, zaczynam od 1 wcięcia, sprawdź poprawność wcięć
EDIT: 1 kropka to 1 spacja, albo Tab to od razu 2 kropki
To jest ostrzeżenie na teraz działa.

Poprawki naniesione.
W trigger jest action -action zamiast -service
Musiałem też zmienić wszystkie
weather.forecast_home na weather.forecast_dom
Teraz nie ma ostrzeżeń i pobiera dane:
weather_condition_now: fog
weather_condition_0: cloudy
weather_temperature_0: 4
weather_timestamp_0: 8
weather_condition_1: rainy
weather_temperature_1: 4
weather_timestamp_1: 9
weather_temperature_2: 4
weather_timestamp_2: 10
weather_temperature_3: 5
weather_timestamp_3: 11
friendly_name: Weatherman Data

Dzięki za pomoc

Koledzy a jak z zasileniem? E-Ink ma sens jak zrobimy zasilanie bateryjne. Sam wyświetlacz, w stanie spoczynku nie pobiera zasobów baterii - a ESP? Tutaj trzeba hibernować i wzbudzać np. co 10min. Choć mi brakuje lekko zegara - więc może co 1 minutę. Ma to ktoś przetestowane?

r

Ja swojego odświeżam co 30 minut, e-ink nie jest zalecany do częstego odświeżania np jako zegar, zużywa się. U mnie po roku też to widać. I do tego też używam ten wirtualny czujnik ruchu, który przy włączeniu zezwala na odświeżanie, jeśli nikogo nie ma w domu wyłączam.

1 Like

Postępy są ale kolejne pytanie.
Nie wyświetla mi czasu wschodu i zachodu słońca.
Czy sensory:

sensor.czas_wschodu_slonca
sensor.czas_zachodu_slonca

trzeba dodać do HA? Gdy zmienię na te co mam:

sensor.sun_next_rising
sensor.sun_next_setting

wstawia mi całą datę i godzinę.

Musisz utworzyć sobie sensor.

 - platform: template
     sensors:
       nastepnywschod:
         friendly_name: 'Następny Wschód Słońca'
         icon_template: mdi:weather-sunset-up
         unit_of_measurement: ''
         value_template: >
           {{ states('sensor.sun_next_rising') | as_timestamp | timestamp_custom('%H.%M.%S') }}
       nastepnyzachod:
         friendly_name: 'Następny Zachód Słońca'
         icon_template: mdi:weather-sunset-down
         unit_of_measurement: ''
         value_template: >
           {{ states('sensor.sun_next_setting') | as_timestamp | timestamp_custom('%H.%M.%S') }}       

Nie wiem czy prawidłowo - odałem w sensor.yaml pod dniem tygodnia i miesiącem w template:

  - platform: template
    sensors:
      dzien_tygodnia:
        friendly_name: "Dzień tygodnia"
        icon_template: mdi:calendar-range-outline
        value_template: "{{ ['Poniedziałek','Wtorek','Środa','Czwartek','Piątek','Sobota','Niedziela'][now().weekday()] }}"

      dzien_miesiaca:
        friendly_name: "Dzień miesiąca"
        icon_template: mdi:calendar-outline
        value_template: "{{ ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31' ][ now().day-1] }}"

      miesiac:
        friendly_name: "Miesiąc"
        icon_template: mdi:calendar-month-outline
        value_template: "{{ ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'][now().month-1] }}"

      nastepnywschod:
        friendly_name: 'Następny Wschód Słońca'
        icon_template: mdi:weather-sunset-up
        unit_of_measurement: ''
        value_template: >
          {{ states('sensor.sun_next_rising') | as_timestamp | timestamp_custom('%H.%M.%S') }}
      nastepnyzachod:
        friendly_name: 'Następny Zachód Słońca'
        icon_template: mdi:weather-sunset-down
        unit_of_measurement: ''
        value_template: >
          {{ states('sensor.sun_next_setting') | as_timestamp | timestamp_custom('%H.%M.%S') }}

i w ESP pojawiła się wartość: unknown natomiast w HA ostrzeżenie:

Wycofanie starszego szablonu sensor

Ostrzeżenie · Zgłoszone przez Template

Ten element przestanie działać w wersji 2026.6. Napraw problem przed aktualizacją.

The legacy `platform: template` syntax for `sensor` is being removed. Please migrate `nastepnywschod` to the modern template syntax.    

Próbowałem skleić z tą nową wersją, którą podaje HA ale nijak mi to nie wychodzi.

Mała poprawka usunąłem z sensora sekundy i czas się pokazał w ESP tak więc działa i będzie działać przynajmniej do czerwca :grinning:

Będę poprawiał dopiero u siebie wg nowej składni i całość tutaj wrzucę. Na razie mieszają się dwa formaty: stary (działa do czerwca 2026 z ostrzeżeniem) i nowy. Poprawność składni template sprawdzisz w Narzędzia deweloperskie > Szablon.