Initial commit: SmartDisplay CYD config met scrollbaar tegelraster
Eerste commit van het project. De config staat op het punt waar het tegelraster scrollbaar is en er acht tegels in staan. Wat er in deze ronde aan de bestaande config veranderd is: - Home screen scrollt. Wallpaper, kamernaam en klok staan op de pagina zelf en blijven staan; alle tegels zitten in een transparante `tile_scroll` container die verticaal scrollt. Ruimte voor 2x5 = 10 tegels, waarvan rij 1-3 zichtbaar is in rust. GRID_ROW*_Y is nu relatief aan die container; SCROLL_Y/SCROLL_H per orientatiepreset. LVGL annuleert klik en long-press zodra een sleep de scroll-limiet passeert, dus tikken en scrollen bijten elkaar niet. - Achtergrond is een twee-stops gradient in plaats van een 320x240 RGB565-wallpaper. Scheelt ~150 KB flash en een image-blit per hertekening, wat het scrollen bruikbaar maakt. Dezelfde gradient wordt hergebruikt als achtergrond van de drie overlays. - Tegel 7: Tafellamp (light.tafellamp_licht), tik = toggle, long-press = helderheidsslider. - Tegel 8: Brievenbus reset (input_select.brievenbus_status). Licht op zodra de status "Vol" is, tik zet hem terug op "Leeg". - TILE*_ACTIVE_STATE toegevoegd: een exacte HA-state die als "actief" telt, voor entiteiten die geen on/off gebruiken. Leeg = oud gedrag. - do_tile_action kent nu param_key "option", voor input_select.select_option. Tegels 7 en 8 abonneren alleen op de HA-entiteiten die ze echt lezen, in plaats van op alle elf per tegel. Na flashen: 148 KB vrije heap, grootste blok 94 KB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+618
@@ -0,0 +1,618 @@
|
||||
############################################################
|
||||
# SmartDisplay (Cheap Yellow Display / CYD) – ESPHome + LVGL lockscreen UI
|
||||
#
|
||||
# Target hardware
|
||||
# - Cheap Yellow Display (CYD)
|
||||
# (ESP32 + ILI9341 320x240 display + XPT2046 touchscreen)
|
||||
#
|
||||
# Purpose
|
||||
# - LVGL renders a simple “lockscreen” UI: time/date + 4 toggle buttons.
|
||||
#
|
||||
# Touch input → Home Assistant
|
||||
# - Every touch publishes an “action” string via a text_sensor:
|
||||
# sensor.smartdisplay_action = "btn1_press" / "btn2_press" / "btn3_press" / "btn4_press"
|
||||
# - The sensor is auto-reset to "" after each press so repeated presses trigger reliably.
|
||||
#
|
||||
# Two supported control modes (choose what fits your setup)
|
||||
# 1) Direct control (DEFAULT, simplest)
|
||||
# - DIRECT_ACTIONS: "true" (default)
|
||||
# - Each touch will:
|
||||
# • publish the action string AND
|
||||
# • directly call a Home Assistant service
|
||||
# (default: light.toggle on BTN*_ENTITY)
|
||||
# - Use this if you simply want to toggle lights without creating automations.
|
||||
#
|
||||
# 2) Automation-only control (custom logic)
|
||||
# - DIRECT_ACTIONS: "false"
|
||||
# - Each touch will ONLY publish the action string.
|
||||
# - You implement the behavior in Home Assistant automations using:
|
||||
# Trigger → State → sensor.smartdisplay_action → To: btn1_press (etc.)
|
||||
#
|
||||
# If you do NOT want to toggle lights
|
||||
# - Either change the homeassistant.action blocks to a different service
|
||||
# (e.g. switch.toggle, script.turn_on, scene.turn_on, ...)
|
||||
# - Or set DIRECT_ACTIONS to "false" and handle everything via HA automations.
|
||||
#
|
||||
# UI updates
|
||||
# - On boot, every minute, and whenever the configured HA entities change
|
||||
# (mirrored into ESPHome to keep LVGL button states in sync).
|
||||
#
|
||||
# What you must adapt before flashing
|
||||
# 1) Secrets: api.encryption.key, ota.password, WiFi credentials (prefer secrets.yaml)
|
||||
# 2) BTN*_ENTITY: Home Assistant entity IDs you want to control/mirror
|
||||
# 3) Touch calibration + transform:
|
||||
# - calibration is panel-specific
|
||||
# - touchscreen.transform MUST match display.transform exactly
|
||||
#
|
||||
# Home Assistant note
|
||||
# - In the ESPHome device settings, enable:
|
||||
# “Allow the device to perform Home Assistant actions”
|
||||
# Otherwise homeassistant.action calls are blocked.
|
||||
############################################################
|
||||
|
||||
# ==========================================================
|
||||
# USER CONFIG (edit this section to match your HA setup)
|
||||
#
|
||||
# - DIRECT_ACTIONS:
|
||||
# "true" -> touch publishes action + calls HA service (light.toggle by default)
|
||||
# "false" -> touch only publishes action (all logic handled in HA)
|
||||
#
|
||||
# - BTN*_ENTITY:
|
||||
# The Home Assistant entity to control and mirror for the button state.
|
||||
# Must exist in Home Assistant.
|
||||
#
|
||||
# - BTN*_LABEL / BTN*_ICON:
|
||||
# Only affects what is shown on the display.
|
||||
# ==========================================================
|
||||
substitutions:
|
||||
# Enable/disable direct Home Assistant service calls on touch.
|
||||
# - "true" -> touching a button also calls light.toggle (plug & play)
|
||||
# - "false" -> touching a button ONLY publishes the action text_sensor
|
||||
DIRECT_ACTIONS: "true"
|
||||
|
||||
# Button 1
|
||||
BTN1_ENTITY: "light.desk_lamp"
|
||||
BTN1_LABEL: "Desk"
|
||||
BTN1_ICON: "\U000F095F"
|
||||
|
||||
# Button 2
|
||||
BTN2_ENTITY: "light.ceiling_light"
|
||||
BTN2_LABEL: "Ceiling"
|
||||
BTN2_ICON: "\U000F0769"
|
||||
|
||||
# Button 3
|
||||
BTN3_ENTITY: "light.globe_lamp"
|
||||
BTN3_LABEL: "Globe"
|
||||
BTN3_ICON: "\U000F08DD"
|
||||
|
||||
# Button 4
|
||||
BTN4_ENTITY: "light.ambient_light"
|
||||
BTN4_LABEL: "Ambient"
|
||||
BTN4_ICON: "\U000F1051"
|
||||
# ==========================================================
|
||||
|
||||
# ============================================================
|
||||
# No need to edit below this line.
|
||||
# All configuration is done via the substitutions above.
|
||||
# Credentials are managed in secrets.yaml (see secrets.yaml.example).
|
||||
# ============================================================
|
||||
|
||||
esphome:
|
||||
name: smartdisplay
|
||||
friendly_name: SmartDisplay
|
||||
on_boot:
|
||||
priority: 600
|
||||
then:
|
||||
- script.execute: ui_refresh
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
logger:
|
||||
|
||||
# Credentials are loaded from secrets.yaml — copy secrets.yaml.example to secrets.yaml and fill in your values.
|
||||
api:
|
||||
encryption:
|
||||
key: !secret smartdisplay_api_key
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
ap:
|
||||
ssid: "Smartdisplay Fallback Hotspot"
|
||||
password: !secret wifi_ap_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
# Use Home Assistant time so the display always matches your HA timezone.
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: homeassistant_time
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /1
|
||||
then:
|
||||
- script.execute: ui_refresh
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto@500"
|
||||
id: big_text
|
||||
size: 60
|
||||
bpp: 4
|
||||
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: "gfonts://Roboto@300"
|
||||
id: notice_text
|
||||
size: 16
|
||||
bpp: 4
|
||||
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: "gfonts://Roboto@400"
|
||||
id: label
|
||||
size: 13
|
||||
bpp: 4
|
||||
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','å','Ä','ä','Ö','ö','Ü','ü','/']
|
||||
|
||||
# Material Design Icons (MDI) — included at fonts/materialdesignicons-webfont.ttf (Apache 2.0)
|
||||
# Source: https://github.com/Templarian/MaterialDesign-Webfont
|
||||
- file: 'fonts/materialdesignicons-webfont.ttf'
|
||||
id: materialdesign_icons
|
||||
size: 35
|
||||
glyphs: ["\U000F095F", "\U000F0769", "\U000F08DD", "\U000F1051"]
|
||||
|
||||
color:
|
||||
- id: ACTIVE
|
||||
hex: "FEC600"
|
||||
- id: INACTIVE
|
||||
hex: "808080"
|
||||
|
||||
sensor:
|
||||
- platform: wifi_signal
|
||||
name: "Wifi Signal"
|
||||
update_interval: 600s
|
||||
|
||||
- platform: uptime
|
||||
name: "Uptime"
|
||||
id: uptime_s
|
||||
update_interval: 15s
|
||||
|
||||
binary_sensor:
|
||||
- platform: status
|
||||
name: "Node Status"
|
||||
id: system_status
|
||||
|
||||
# Mirror HA entities (used to render the "checked" state of each LVGL button)
|
||||
# These are read-only "views" of HA state for UI sync.
|
||||
- platform: homeassistant
|
||||
id: ha_state_btn1
|
||||
entity_id: ${BTN1_ENTITY}
|
||||
on_state:
|
||||
then:
|
||||
- script.execute: ui_refresh
|
||||
|
||||
- platform: homeassistant
|
||||
id: ha_state_btn2
|
||||
entity_id: ${BTN2_ENTITY}
|
||||
on_state:
|
||||
then:
|
||||
- script.execute: ui_refresh
|
||||
|
||||
- platform: homeassistant
|
||||
id: ha_state_btn3
|
||||
entity_id: ${BTN3_ENTITY}
|
||||
on_state:
|
||||
then:
|
||||
- script.execute: ui_refresh
|
||||
|
||||
- platform: homeassistant
|
||||
id: ha_state_btn4
|
||||
entity_id: ${BTN4_ENTITY}
|
||||
on_state:
|
||||
then:
|
||||
- script.execute: ui_refresh
|
||||
|
||||
text_sensor:
|
||||
# ----------------------------------------------------------
|
||||
# SmartDisplay Action
|
||||
#
|
||||
# This is the main "touch trigger" exported to Home Assistant:
|
||||
# - On touch, we publish one of:
|
||||
# btn1_press / btn2_press / btn3_press / btn4_press
|
||||
# - Then we auto-reset to "" so repeated presses still trigger.
|
||||
#
|
||||
# In HA automations, use a simple state trigger:
|
||||
# - Entity: sensor.smartdisplay_action
|
||||
# - To: btn1_press (etc.)
|
||||
# ----------------------------------------------------------
|
||||
- platform: template
|
||||
id: smartdisplay_action
|
||||
name: "SmartDisplay Action"
|
||||
icon: mdi:gesture-tap-button
|
||||
|
||||
- platform: template
|
||||
name: "Uptime (formatted)"
|
||||
lambda: |-
|
||||
uint32_t dur = (uint32_t) id(uptime_s).state;
|
||||
|
||||
int dys = 0;
|
||||
int hrs = 0;
|
||||
int mnts = 0;
|
||||
|
||||
if (dur >= 86400) { dys = dur / 86400; dur = dur - (dys * 86400); }
|
||||
if (dur >= 3600) { hrs = dur / 3600; dur = dur - (hrs * 3600); }
|
||||
if (dur >= 60) { mnts = dur / 60; dur = dur - (mnts * 60); }
|
||||
|
||||
static char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%dd %02dh %02dm %02ds", dys, hrs, mnts, dur);
|
||||
return {buffer};
|
||||
icon: mdi:clock-start
|
||||
update_interval: 60s
|
||||
|
||||
switch:
|
||||
- platform: restart
|
||||
name: "Restart"
|
||||
|
||||
# Two SPI buses:
|
||||
# - lcd: display
|
||||
# - my_touchscreen: touch controller
|
||||
spi:
|
||||
- id: lcd
|
||||
clk_pin: GPIO14
|
||||
mosi_pin: GPIO13
|
||||
|
||||
- id: my_touchscreen
|
||||
clk_pin: GPIO25
|
||||
mosi_pin: GPIO32
|
||||
miso_pin: GPIO39
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
pin: GPIO21
|
||||
id: gpio_backlight_pwm
|
||||
|
||||
- platform: ledc
|
||||
id: output_red
|
||||
pin: GPIO4
|
||||
inverted: true
|
||||
- platform: ledc
|
||||
id: output_green
|
||||
pin: GPIO16
|
||||
inverted: true
|
||||
- platform: ledc
|
||||
id: output_blue
|
||||
pin: GPIO17
|
||||
inverted: true
|
||||
|
||||
light:
|
||||
- platform: monochromatic
|
||||
output: gpio_backlight_pwm
|
||||
name: "Power Display Backlight"
|
||||
id: back_light
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
- platform: rgb
|
||||
name: LED
|
||||
red: output_red
|
||||
id: led
|
||||
green: output_green
|
||||
blue: output_blue
|
||||
restore_mode: ALWAYS_OFF
|
||||
|
||||
# Touchscreen configuration (XPT2046)
|
||||
# IMPORTANT:
|
||||
# - calibration values are device-specific
|
||||
# - touchscreen.transform must match display.transform exactly
|
||||
touchscreen:
|
||||
platform: xpt2046
|
||||
id: ts_touch
|
||||
spi_id: my_touchscreen
|
||||
cs_pin: 33
|
||||
interrupt_pin: 36
|
||||
update_interval: 50ms
|
||||
threshold: 400
|
||||
|
||||
calibration:
|
||||
x_min: 280
|
||||
x_max: 3860
|
||||
y_min: 340
|
||||
y_max: 3860
|
||||
|
||||
transform:
|
||||
swap_xy: true
|
||||
mirror_x: false
|
||||
mirror_y: false
|
||||
|
||||
# Display configuration (ILI9341)
|
||||
display:
|
||||
- platform: mipi_spi
|
||||
id: my_display
|
||||
spi_id: lcd
|
||||
model: ILI9341
|
||||
cs_pin: 15
|
||||
dc_pin: 2
|
||||
invert_colors: false
|
||||
update_interval: never
|
||||
auto_clear_enabled: false
|
||||
|
||||
transform:
|
||||
swap_xy: true
|
||||
mirror_x: false
|
||||
mirror_y: false
|
||||
|
||||
dimensions:
|
||||
width: 320
|
||||
height: 240
|
||||
|
||||
lvgl:
|
||||
displays:
|
||||
- my_display
|
||||
touchscreens:
|
||||
- touchscreen_id: ts_touch
|
||||
|
||||
theme:
|
||||
obj:
|
||||
bg_color: 0x000000
|
||||
bg_opa: COVER
|
||||
button:
|
||||
radius: 30
|
||||
width: 60
|
||||
height: 60
|
||||
border_width: 0
|
||||
shadow_width: 0
|
||||
bg_color: !lambda return id(INACTIVE);
|
||||
bg_opa: COVER
|
||||
checked:
|
||||
bg_color: 0xFFFFFF
|
||||
bg_opa: COVER
|
||||
|
||||
pages:
|
||||
- id: lockscreen_page
|
||||
bg_color: 0x000000
|
||||
bg_opa: COVER
|
||||
widgets:
|
||||
- label:
|
||||
id: lbl_date
|
||||
x: 0
|
||||
y: 30
|
||||
width: 320
|
||||
text_align: CENTER
|
||||
text_font: notice_text
|
||||
text_color: 0xFFFFFF
|
||||
text: ""
|
||||
|
||||
- label:
|
||||
id: lbl_time
|
||||
x: 0
|
||||
y: 55
|
||||
width: 320
|
||||
text_align: CENTER
|
||||
text_font: big_text
|
||||
text_color: 0xFFFFFF
|
||||
text: ""
|
||||
|
||||
# ---------------------------------------------------
|
||||
# LVGL Button 1
|
||||
# - ALWAYS publishes a HA-friendly action: btn1_press
|
||||
# - Optional direct action: light.toggle for BTN1_ENTITY
|
||||
# ---------------------------------------------------
|
||||
- button:
|
||||
id: btn1
|
||||
x: 25
|
||||
y: 140
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text_font: materialdesign_icons
|
||||
text_color: !lambda return id(ACTIVE);
|
||||
text: "${BTN1_ICON}"
|
||||
on_click:
|
||||
# ALWAYS: publish action to Home Assistant (state trigger friendly)
|
||||
- script.execute:
|
||||
id: publish_action
|
||||
action: "btn1_press"
|
||||
|
||||
# OPTIONAL: direct HA service call (plug & play)
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
return std::string("${DIRECT_ACTIONS}") == "true";
|
||||
then:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: ${BTN1_ENTITY}
|
||||
|
||||
- label:
|
||||
x: 25
|
||||
y: 205
|
||||
width: 60
|
||||
text_align: CENTER
|
||||
text_font: label
|
||||
text_color: 0xFFFFFF
|
||||
text: "${BTN1_LABEL}"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# LVGL Button 2
|
||||
# - ALWAYS publishes a HA-friendly action: btn2_press
|
||||
# ---------------------------------------------------
|
||||
- button:
|
||||
id: btn2
|
||||
x: 95
|
||||
y: 140
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text_font: materialdesign_icons
|
||||
text_color: !lambda return id(ACTIVE);
|
||||
text: "${BTN2_ICON}"
|
||||
on_click:
|
||||
- script.execute:
|
||||
id: publish_action
|
||||
action: "btn2_press"
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
return std::string("${DIRECT_ACTIONS}") == "true";
|
||||
then:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: ${BTN2_ENTITY}
|
||||
|
||||
- label:
|
||||
x: 95
|
||||
y: 205
|
||||
width: 60
|
||||
text_align: CENTER
|
||||
text_font: label
|
||||
text_color: 0xFFFFFF
|
||||
text: "${BTN2_LABEL}"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# LVGL Button 3
|
||||
# - ALWAYS publishes a HA-friendly action: btn3_press
|
||||
# ---------------------------------------------------
|
||||
- button:
|
||||
id: btn3
|
||||
x: 165
|
||||
y: 140
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text_font: materialdesign_icons
|
||||
text_color: !lambda return id(ACTIVE);
|
||||
text: "${BTN3_ICON}"
|
||||
on_click:
|
||||
- script.execute:
|
||||
id: publish_action
|
||||
action: "btn3_press"
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
return std::string("${DIRECT_ACTIONS}") == "true";
|
||||
then:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: ${BTN3_ENTITY}
|
||||
|
||||
- label:
|
||||
x: 165
|
||||
y: 205
|
||||
width: 60
|
||||
text_align: CENTER
|
||||
text_font: label
|
||||
text_color: 0xFFFFFF
|
||||
text: "${BTN3_LABEL}"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# LVGL Button 4
|
||||
# - ALWAYS publishes a HA-friendly action: btn4_press
|
||||
# ---------------------------------------------------
|
||||
- button:
|
||||
id: btn4
|
||||
x: 235
|
||||
y: 140
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text_font: materialdesign_icons
|
||||
text_color: !lambda return id(ACTIVE);
|
||||
text: "${BTN4_ICON}"
|
||||
on_click:
|
||||
- script.execute:
|
||||
id: publish_action
|
||||
action: "btn4_press"
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
return std::string("${DIRECT_ACTIONS}") == "true";
|
||||
then:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: ${BTN4_ENTITY}
|
||||
|
||||
- label:
|
||||
x: 235
|
||||
y: 205
|
||||
width: 60
|
||||
text_align: CENTER
|
||||
text_font: label
|
||||
text_color: 0xFFFFFF
|
||||
text: "${BTN4_LABEL}"
|
||||
|
||||
script:
|
||||
# ----------------------------------------------------------
|
||||
# publish_action
|
||||
#
|
||||
# Publishes a string to the "SmartDisplay Action" text_sensor and
|
||||
# then clears it again.
|
||||
# ----------------------------------------------------------
|
||||
- id: publish_action
|
||||
mode: queued
|
||||
parameters:
|
||||
action: string
|
||||
then:
|
||||
- text_sensor.template.publish:
|
||||
id: smartdisplay_action
|
||||
state: !lambda |-
|
||||
return action;
|
||||
- delay: 200ms
|
||||
- text_sensor.template.publish:
|
||||
id: smartdisplay_action
|
||||
state: ""
|
||||
|
||||
- id: ui_refresh
|
||||
mode: queued
|
||||
then:
|
||||
# Sync LVGL "checked" state with the mirrored HA entity states
|
||||
- lvgl.widget.update:
|
||||
id: btn1
|
||||
state:
|
||||
checked: !lambda return id(ha_state_btn1).state;
|
||||
|
||||
- lvgl.widget.update:
|
||||
id: btn2
|
||||
state:
|
||||
checked: !lambda return id(ha_state_btn2).state;
|
||||
|
||||
- lvgl.widget.update:
|
||||
id: btn3
|
||||
state:
|
||||
checked: !lambda return id(ha_state_btn3).state;
|
||||
|
||||
- lvgl.widget.update:
|
||||
id: btn4
|
||||
state:
|
||||
checked: !lambda return id(ha_state_btn4).state;
|
||||
|
||||
# Render current time (HH:MM)
|
||||
- lvgl.label.update:
|
||||
id: lbl_time
|
||||
text: !lambda |-
|
||||
auto now = id(homeassistant_time).now();
|
||||
static char buf[6];
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d", now.hour, now.minute);
|
||||
return std::string(buf);
|
||||
|
||||
# Render localized date in German (adjust to your language if needed)
|
||||
- lvgl.label.update:
|
||||
id: lbl_date
|
||||
text: !lambda |-
|
||||
static const char* days[] = {"", "Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"};
|
||||
static const char* months[] = {"","Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"};
|
||||
auto now = id(homeassistant_time).now();
|
||||
static char buf[40];
|
||||
snprintf(buf, sizeof(buf), "%s, %d. %s", days[now.day_of_week], now.day_of_month, months[now.month]);
|
||||
return std::string(buf);
|
||||
Reference in New Issue
Block a user