WetterMan, der Pille-Palle-Anzieh-Helfer :-)

Hallo allerseits!
Das folgende Projekt ist eigentlich nicht der Rede Wert, der Nutzen für andere geht wohl auch gegen Null…aber es hat extrem viel Spass gemacht und mein Wissen prächtig erweitert :rofl:

Worum geht’s / Problemstellung:
In unseren Schlafzimmern gilt striktes Handy & Tablet Verbot (WAF…) Wenn ich in der Früh erwache, weiss ich nicht was ich anziehen soll. Grundlage hierfür wäre zu wissen, welche aktuelle Temperaturen anliegen, was die Tages-Höchsttemperatur sein wird und eine Zusammenfassung der allgemeinen Lage.

Ich habe keine Lust, am Morgen durch die Villa zu trotten, runter in die Küche, ein Tablet zu suchen oder vor den grossen HA Screen zu stehen und dann wieder nach oben anziehen gehen. Den Wetterbericht am Vorabend zu studieren ist ebenfalls sinnlos, da ich den beim Aufwachen vergessen habe.

Tja, was tun? Ich habe genug Entitäten im HA mit solchen Wetter-Infos, aber wie kann ich die “unsichtbar” in die Reithalle schmuggeln? :thinking:

Bei meinen Streifzügen durch’s Internet blieb ich plötzlich bei VahaC und seinen Projekten hängen. Ich hatte mich zuvor noch nie gross mit ESP32 oder sonstigen Mikrokontroller beschäftigt. Aber hier tat sich eine ganz neue Welt für mich auf! Sein sehr gut dokumentiertes Touch-Display-Projekt hat mich sehr inspiriert und mich auf die Idee gebracht, meine Wetterinfos auf so ein Display zu bringen und selbiges im Bett-Rahmen zu verstecken :rofl:

Also hopp-hopp bei Aliexpress für ein Appel und 'n Ei eine Handvoll CYD und Guition Displays bestellt. Schon krass wie wenig das kostet! Ich tat mich anfangs sehr schwer mit dem LVGL-GUI Design. Aber die ESPHome Designer Integration und der Online Designer halfen mir gewaltig aus der Patsche.

Nach erfolgreichen Tests konstruierte und druckte ich eine Halterung und fixierte diese mittels Klett an der Innenseite des kleinen in der Bett-Konstruktion integrierten Schrankes. Wenn ich via HA nun sanft zum aufwachen das Licht einschalte, geht das Display an und zeigt mir die benötigten Infos wenn ich die Türe öffne und geht wieder aus wenn das Licht ausschaltet wird. Diese Lösung hat vollumfänglich zu 100% den WAF erfüllt :star_struck:

So schaut es aus:

Hier der unoptimierte, aber funktionierende Code falls es jemanden interessiert:

# Board: Guition ESP32-S3-4848S040 480*480 Smart Screen
# Definition: definitions/boards/guition_esp32_s3_4848s040/manifest.yaml

esphome:
  name: wetterman
  friendly_name: WetterMan

esp32:
  variant: esp32s3
  framework:
    type: esp-idf

# Enable logging
logger:
  baud_rate: 0
  level: INFO

api:
  encryption:
    key: !secret wetterman__encryption_key
ota:
  - platform: esphome
    encryption:

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

  ap:
    ssid: WetterMan Fallback Hotspot
    password: "DeinPW"

captive_portal:

debug:
  update_interval: 5s
    
touchscreen:
  platform: gt911
  id: my_touchscreen
  transform:
    swap_xy: false
    mirror_x: false
    mirror_y: false
  on_release:
    - if:
        condition: lvgl.is_paused
        then:
          - lvgl.resume:
          - lvgl.widget.redraw:
          - light.turn_on: display_backlight

display:
  - platform: st7701s
    id: tft_display
    spi_id: spihwd
    dimensions:
      width: 480
      height: 480
    # rotation: 270    # Uncomment for placement with down-facing USB socket
    spi_mode: MODE3
    data_rate: 2MHz
    color_order: RGB
    invert_colors: false
    cs_pin: 39
    de_pin: 18
    hsync_pin: 16
    vsync_pin: 17
    pclk_pin: 21
    pclk_frequency: 12MHz
    pclk_inverted: false
    hsync_pulse_width: 8
    hsync_front_porch: 10
    hsync_back_porch: 20
    vsync_pulse_width: 8
    vsync_front_porch: 10
    vsync_back_porch: 10
    auto_clear_enabled: false
    update_interval: never
    init_sequence:
      - 1
      - [0xFF, 0x77, 0x01, 0x00, 0x00, 0x10] # CMD2_BKSEL_BK0
      - [0xCD, 0x00] # Disable MDT flag
    data_pins:
      red: [11, 12, 13, 14, 0]
      green: [8, 20, 3, 46, 9, 10]
      blue: [4, 5, 6, 7, 15]

# ====================================
# Device Settings
# ====================================
# Orientation: landscape
# Dark Mode: enabled
# Refresh Interval: 600
# Power Strategy: Backlight Off Schedule
# Deep Sleep: disabled
# Deep Sleep Stay Awake Switch: disabled
# Deep Sleep Stay Awake Entity: input_boolean.esphome_stay_awake
# Deep Sleep Firmware Guard: disabled
# Deep Sleep Interval: Disabled
# Layout Signature: 06db6003
# Layout Summary: mode=lvgl pages=1 widgets=6 types=label:1,lvgl_label:5
# Boot Log Hint: Compare the runtime build signature with this header to spot OTA rollbacks.
# ====================================

output:
  - platform: ledc
    pin: GPIO38
    id: gpio_backlight_pwm
    frequency: 100Hz
  - id: internal_relay_1
    platform: gpio
    pin: GPIO40
  - id: internal_relay_2
    platform: gpio
    pin: GPIO02
  - id: internal_relay_3
    platform: gpio
    pin: GPIO01

light:
  - platform: monochromatic
    output: gpio_backlight_pwm
    name: WetterMan Display Backlight
    icon: mdi:lightbulb-on
    id: wetterman_display_backlight
    internal: false                # Expose to Home Assistant as a normal light
    restore_mode: ALWAYS_ON
    gamma_correct: 1.0

spi:
  clk_pin: GPIO48
  mosi_pin: GPIO47
  interface: hardware
  id: spihwd

i2c:
  id: touchscreen_bus
  sda: GPIO19
  scl:
    number: 45
    ignore_strapping_warning: true

psram:
  mode: octal
  speed: 80MHz
  
globals:
  - id: display_page
    type: int
    restore_value: true
    initial_value: '0'
  - id: page_refresh_default_s
    type: int
    restore_value: true
    initial_value: '600'
  - id: page_refresh_current_s
    type: int
    restore_value: false
    initial_value: '60'
  - id: initial_sensor_sync_pending
    type: bool
    restore_value: false
    initial_value: 'true'

time:
  - platform: homeassistant
    id: ha_time

  # NUR FUER TEXTE
text_sensor:
  - platform: debug
    device:
      name: "Device Info"
    reset_reason:
      name: "Reset Reason"
  - platform: homeassistant
    id: sensor_ischpf17_forecast_summary_0d
    entity_id: sensor.ischpf17_forecast_summary_0d #Entität aus HA mit dem Kurz-Wetterbericht
    on_value:
      then:
        - lvgl.widget.refresh: w_bericht
    internal: true
    icon: "mdi:weather-partly-cloudy"

    # NUR FUER ZAHLEN ZND FLOATS
sensor:
  - platform: homeassistant
    id: sensor_ischpf17_temperature
    entity_id: sensor.ischpf17_temperature #Entität aus HA mit der aktuellen Temperatur
    unit_of_measurement: "°C"
    internal: true
    on_value:
      - lvgl.label.update:
          id: w_temp_aktuell
          text: !lambda |-
            float v = id(sensor_ischpf17_temperature).state;
            if (isnan(v)) return "??";
            return str_sprintf("%.0f °C", v);

  - platform: homeassistant
    id: sensor_ischpf17_forecast_temperature_0d
    entity_id: sensor.ischpf17_forecast_temperature_0d #Entität aus HA mit der prognost. Tages-Höchsttemperatur
    unit_of_measurement: "°C"
    internal: true
    on_value:
      - lvgl.label.update:
          id: w_temp_prognose
          text: !lambda |-
            float v = id(sensor_ischpf17_forecast_temperature_0d).state;
            if (isnan(v)) return "??";
            return str_sprintf("%.0f °C", v);

switch: #für HA zum Backlight ein- und ausschalten
  - platform: homeassistant
    id: ha_display_power
    entity_id: input_boolean.display_wetterman_aktiv
    on_turn_on:
      - light.turn_on: display_backlight
    on_turn_off:
      - light.turn_off: display_backlight
            
font:
  - file:
      type: gfonts
      family: "Montserrat"
      weight: 400
      italic: false
    id: font_montserrat_400_30
    size: 30
    glyphsets: [GF_Latin_Kernel]
    glyphs: "ÄÖÜäöüß°"   # Umlaute etc auch korrekt anzeigen
    ignore_missing_glyphs: true
  - file:
      type: gfonts
      family: "Montserrat"
      weight: 400
      italic: false
    id: font_montserrat_400_60
    size: 60
    glyphsets: [GF_Latin_Kernel]
    glyphs: "ÄÖÜäöüß°"
    ignore_missing_glyphs: true
  - file:
      type: gfonts
      family: "Roboto"
      weight: 400
      italic: false
    id: font_roboto_400_20
    size: 20
    glyphsets: [GF_Latin_Kernel]
    ignore_missing_glyphs: true

script:
  - id: manage_run_and_sleep
    mode: restart
    then:
      - if:
          condition:
            lambda: 'return !(id(ha_time).now().is_valid() && api_is_connected());'
          then:
            - logger.log: "Waiting for sync..."
            - wait_until:
                condition:
                  lambda: 'return id(ha_time).now().is_valid() && api_is_connected();'
                timeout: 120s
      - if:
          condition:
            lambda: 'return id(initial_sensor_sync_pending);'
          then:
            - logger.log: "ESPHome Designer build: sig=06db6003 mode=lvgl pages=1 widgets=6 types=label:1,lvgl_label:5"
            - logger.log: "OTA hint: if this build line does not match the YAML header, the device booted a different image or rolled back."
            - logger.log: "Waiting 5s for initial sensor updates..."
            - delay: 5s
            - lambda: 'id(initial_sensor_sync_pending) = false;'
      - lambda: |-
          int interval = id(page_refresh_default_s);
          id(page_refresh_current_s) = interval;
      - delay: !lambda 'return id(page_refresh_current_s) * 1000;'
      - script.execute: manage_run_and_sleep
      
# ============================================================================
# LVGL Configuration
# ============================================================================

lvgl:
  id: my_lvgl
  log_level: WARN
  bg_color: "0x000000"
  displays:
    - tft_display
  touchscreens:
    - touchscreen_id: my_touchscreen

  pages:
    - id: page_0
      widgets:

        # --- Wetterbericht ---
        - label:
            id: w_bericht
            text: !lambda |-
              if (id(sensor_ischpf17_forecast_summary_0d).has_state()) {
                return id(sensor_ischpf17_forecast_summary_0d).state;
              }
              return "Lade...";
            text_align: "LEFT"
            text_color: "0xB5B5B5"
            text_font: font_montserrat_400_30
            width: 438
            height: 194
            x: 22
            y: 267

        # --- Aktuelle Temperatur ---
        - label:
            id: w_temp_aktuell
            text: !lambda |-
              float v = id(sensor_ischpf17_temperature).state;
              if (isnan(v)) return "??";
              return str_sprintf("%.0f °C", v);
            text_align: "CENTER"
            text_color: "0xB5B5B5"
            text_font: font_montserrat_400_60
            width: 200
            height: 60
            x: 140
            y: 37

        # --- Prognose Temperatur ---
        - label:
            id: w_temp_prognose
            text: !lambda |-
              float v = id(sensor_ischpf17_forecast_temperature_0d).state;
              if (isnan(v)) return "??";
              return str_sprintf("%.0f °C", v);
            text_align: "CENTER"
            text_color: "0xB5B5B5"
            text_font: font_montserrat_400_60
            width: 200
            height: 60
            x: 140
            y: 144

Nun, man muss sich im Leben einfach zu helfen wissen :clinking_beer_mugs:

3 „Gefällt mir“