37e653f102
De config staat nu op twee plekken: deze repo en
/config/esphome/smartdisplay.yaml op Home Assistant. Die laatste las de
secrets van HA, deze repo zijn eigen set — met andere waarden voor de
api key en het OTA-wachtwoord. Wie het laatst flashte won, en de ander
kwam er daarna niet meer bij.
Beide configs lezen nu dezelfde namen als /config/esphome/secrets.yaml:
smartdisplay_api_key -> api_encryption_key
wifi_ap_password -> ap_password
ota_password -> ongewijzigd van naam, wel HA's waarde
De lokale secrets.yaml (niet in git) is een kopie van die op HA, dus
beide routes leveren exact dezelfde api key en hetzelfde OTA-wachtwoord
op. home-like.yaml en /config/esphome/smartdisplay.yaml zijn nu
byte-identiek.
buttons.yaml krijgt dezelfde omzetting. Die config valideert overigens
al niet meer sinds ESPHome 2026.6 ("Invalid offsets" op de display-
transform); dat stond los van deze wijziging en is hier niet aangeraakt.
Ook opgeruimd: de header verwees nog naar achtergrondplaatjes, die sinds
de gradient niet meer bestaan. Het MDI-font is de enige asset die de
config nog nodig heeft.
Geverifieerd na het gelijktrekken: OTA vanuit de repo werkt, OTA met
HA's credentials werkt, en de ESPHome-integratie in HA bleef verbonden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5639 lines
243 KiB
YAML
5639 lines
243 KiB
YAML
############################################################
|
||
# SmartDisplay – iOS Home app–inspired Tiles UI
|
||
# Target device: ESP32-2432S028 / Cheap Yellow Display (CYD)
|
||
# Resolution: configurable via ORIENTATION substitutions (landscape 320×240 default)
|
||
#
|
||
# Target hardware
|
||
# - Cheap Yellow Display (ESP32-2432S028 or compatible)
|
||
# - Built-in ILI9341 320x240 display
|
||
# - Built-in XPT2046 touchscreen
|
||
#
|
||
# What this config does
|
||
# - Renders a wallpaper background + a 2x3 grid of “tiles” (LVGL).
|
||
# - Each tile shows: icon (MDI font), title, and a value line.
|
||
# - Tile taps publish a short-lived text_sensor event (smartdisplay_action)
|
||
# so you can build Home Assistant automations on top.
|
||
# - Optional: the device can also call Home Assistant services directly
|
||
# (toggle lights/switches/fans/covers, activate scenes/scripts, set fan preset,
|
||
# set light brightness, set fan percentage, set cover position).
|
||
#
|
||
# The layout is inspired by common modern smart home UIs.
|
||
#
|
||
# How to customize
|
||
# 1) Edit the TILE1..TILE6 blocks below.
|
||
# 2) If you change icons, update the FONT GLYPHS block in the substitutions below.
|
||
# Each glyph must appear exactly once — same icon on multiple tiles is fine, just list it once.
|
||
# 3) The background is a gradient (BG_TOP_COLOR / BG_BOTTOM_COLOR), not an image.
|
||
# The only asset this config needs is fonts/materialdesignicons-webfont.ttf.
|
||
#
|
||
# Tile configuration model
|
||
# - TILE*_TYPE defines the entity kind: light | fan | switch | scene | script | cover
|
||
# - TILE*_TAP_ACTION defines what happens on short tap:
|
||
# auto | toggle | activate | fan_toggle_preset | custom
|
||
# - TILE*_LONGPRESS defines the long press mode: none | slider | action
|
||
# slider: opens a value overlay — configure with TILE*_LONGPRESS_SLIDER + TILE*_LONGPRESS_OFF_VALUE
|
||
# action: fires a different entity — configure with TILE*_LONGPRESS_ACTION*
|
||
# - TILE*_VALUE_MODE defines how the value line is rendered:
|
||
# auto | brightness | percentage | preset | text
|
||
#
|
||
# Notes
|
||
# - DIRECT_ACTIONS="true" enables direct HA service calls from the device.
|
||
# If "false", only the smartdisplay_action event is emitted.
|
||
# - In auto mode, values and long-press behavior are derived from TILE*_TYPE:
|
||
# light -> brightness, fan -> percentage/preset, scene/script -> text
|
||
#
|
||
# Secrets
|
||
# This config is kept byte-identical in two places:
|
||
# - this repository (flash with `esphome upload home-like.yaml`)
|
||
# - /config/esphome/smartdisplay.yaml on Home Assistant (flash from the
|
||
# ESPHome dashboard)
|
||
# Both read the same secret names, and the repo's secrets.yaml is a copy of the
|
||
# one in /config/esphome/. That way either route produces the same api key and
|
||
# OTA password, so the two never fight over the device.
|
||
#
|
||
# What you must adapt before flashing
|
||
# 1) Secrets: api_encryption_key, ota_password, WiFi credentials (see secrets.yaml)
|
||
# 2) Pins, touch calibration and transform if your CYD variant differs
|
||
# 3) TILE*_ENTITY / TILE*_STATE_ENTITY: Home Assistant entity IDs you want to control/mirror
|
||
# 4) Orientation + touch calibration:
|
||
# - rotation is set via LVGL_ROTATION in the ORIENTATION presets (LVGL
|
||
# rotates display + touch together, ESPHome 2026.4+)
|
||
# - calibration is device-specific
|
||
############################################################
|
||
|
||
substitutions:
|
||
|
||
DIRECT_ACTIONS: "true" # true = call Home Assistant services directly, false = only emit action events
|
||
ROOM_NAME: "Woonkamer" # room name shown in the top-left header
|
||
TIME_24H: "true" # true = 24h format, false = 12h format
|
||
|
||
## DIM settings
|
||
AUTO_DIM: "true" # true = enable auto dimming
|
||
AUTO_DIM_TIMEOUT: "10" # seconds after last touch
|
||
MAX_BRIGHTNESS: "100" # backlight brightness in percent
|
||
DIM_BRIGHTNESS: "20" # dimmed backlight brightness in percent
|
||
NIGHT_MODE: "true" # true = use separate dim level at night
|
||
NIGHT_START_HOUR: "22" # 0-23
|
||
NIGHT_END_HOUR: "7" # 0-23
|
||
NIGHT_DIM_BRIGHTNESS: "10" # backlight brightness in percent at night
|
||
|
||
# ============================================================
|
||
# ACCENTKLEUREN — automatisch bepaald uit het Home Assistant-domein
|
||
#
|
||
# Het domein is het stuk voor de punt in het entity_id:
|
||
# script.goede_morgen -> "script" -> ACCENT_ACTION
|
||
# light.woonkamer_2 -> "light" -> ACCENT_LIGHT
|
||
# vacuum.s8 -> "vacuum" -> ACCENT_DEVICE
|
||
#
|
||
# Dat is Home Assistant's eigen indeling, dus een tegel krijgt vanzelf de
|
||
# juiste kleur zodra je er een entity in zet — je hoeft per tegel geen
|
||
# kleuren meer te kiezen. Het onderscheid dat telt: een script/scene vuur
|
||
# je af, een apparaat zet je aan of uit. Die twee horen niet hetzelfde
|
||
# te zijn.
|
||
#
|
||
# Zet TILE*_CIRCLE_ACTIVE_COLOR / TILE*_ICON_DISABLED_COLOR op
|
||
# ${ACCENT_AUTO} om dit te gebruiken, of op een echte hexwaarde om die
|
||
# ene tegel handmatig te kleuren (zie tegel 8 — rood zodra er post ligt).
|
||
# ============================================================
|
||
ACCENT_AUTO: "0x1000000" # sentinel: bewust buiten het 24-bits kleurbereik
|
||
ACCENT_ACTION: "0x8B5CF6" # script / scene / button / input_button / automation
|
||
ACCENT_LIGHT: "0xFEC600" # light
|
||
ACCENT_DEVICE: "0x00C5EC" # switch, fan, cover, climate, vacuum, media_player, rest
|
||
|
||
# ============================================================
|
||
# FONT GLYPHS
|
||
# List every unique MDI icon used across your tiles — each value must appear exactly once.
|
||
# Reusing the same icon on multiple tiles is fine: just list that glyph once here.
|
||
# If you use fewer than 6 unique icons, leave the extra slots with their default values
|
||
# (unused glyphs add a few bytes to the font but cause no errors).
|
||
# ============================================================
|
||
MDI_GLYPH_1: "\U000F001B"
|
||
MDI_GLYPH_2: "\U000F04B9"
|
||
MDI_GLYPH_3: "\U000F02E3"
|
||
MDI_GLYPH_4: "\U000F059C"
|
||
MDI_GLYPH_5: "\U000F0425"
|
||
MDI_GLYPH_6: "\U000F070D"
|
||
MDI_GLYPH_7: "\U000F06B5" # mdi:lamp
|
||
MDI_GLYPH_8: "\U000F06EE" # mdi:mailbox
|
||
|
||
# ============================================================
|
||
# TILE 1
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE1_ENTITY: "climate.airco_remeha_diva_ac" # main Home Assistant entity for this tile
|
||
TILE1_STATE_ENTITY: "climate.airco_remeha_diva_ac" # entity used for active/inactive state styling
|
||
TILE1_TITLE: "Airco" # tile title shown in the UI
|
||
TILE1_ICON: "\U000F001B" # MDI glyph — also add to MDI_GLYPH_* list above if not already there
|
||
TILE1_TYPE: "climate" # tile type: light | fan | switch | scene | script | cover | climate
|
||
TILE1_TAP_ACTION: "toggle" # tap action: auto | toggle | activate | fan_toggle_preset | custom
|
||
TILE1_LONGPRESS: "slider" # long press mode: none | slider | action
|
||
TILE1_VALUE_MODE: "auto" # value mode: auto | brightness | percentage | preset | text
|
||
TILE1_LABEL_OFF: "Uit" # text shown when entity is off
|
||
TILE1_LABEL_ON: "Aan" # text shown for text-mode tiles when entity is on
|
||
TILE1_ACTIVE_STATE: "" # optional: exact HA state that counts as "active" (empty = default "on")
|
||
|
||
# --- colors ---
|
||
TILE1_CIRCLE_ACTIVE_COLOR: "${ACCENT_AUTO}" # icon circle color when active
|
||
TILE1_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE1_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE1_ICON_DISABLED_COLOR: "${ACCENT_AUTO}" # icon color when inactive
|
||
TILE1_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE1_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE1_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE1_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE1_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE1_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE1_TAP_SERVICE: "climate.toggle" # optional service override, e.g. light.toggle
|
||
TILE1_TAP_PARAM_KEY: "" # optional extra service parameter key
|
||
TILE1_TAP_PARAM_VAL: "" # optional extra service parameter value
|
||
# if LONGPRESS = slider:
|
||
TILE1_LONGPRESS_SLIDER: "auto" # slider type: auto | brightness | percentage | cover_position
|
||
TILE1_LONGPRESS_OFF_VALUE: "0" # initial slider value when entity is off
|
||
# if LONGPRESS = action:
|
||
TILE1_LONGPRESS_ACTION: "" # entity_id to fire on long press
|
||
TILE1_LONGPRESS_ACTION_TYPE: "" # entity type: light | fan | switch | scene | script | cover | climate (empty = same as TILE*_TYPE)
|
||
TILE1_LONGPRESS_ACTION_SERVICE: "" # service to call (default: activate/turn_on — set e.g. "light.toggle" to toggle instead)
|
||
# if TYPE = climate:
|
||
TILE1_CLIMATE_MIN_TEMP: "16.0" # minimum target temperature shown in the thermostat overlay
|
||
TILE1_CLIMATE_MAX_TEMP: "32.0" # maximum target temperature shown in the thermostat overlay
|
||
TILE1_CLIMATE_STEP: "1.0" # target temperature step size
|
||
TILE1_CLIMATE_CURRENT_TEMP_ATTRIBUTE: "current_temperature" # HA attribute for current room temperature
|
||
TILE1_CLIMATE_TARGET_TEMP_ATTRIBUTE: "temperature" # HA attribute for target temperature
|
||
TILE1_CLIMATE_HUMIDITY_ATTRIBUTE: "current_humidity" # HA attribute for humidity; leave as-is if unavailable
|
||
|
||
|
||
# ============================================================
|
||
# TILE 2
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE2_ENTITY: "light.woonkamer_2" # main Home Assistant entity for this tile
|
||
TILE2_STATE_ENTITY: "light.woonkamer_2" # entity used for active/inactive state styling
|
||
TILE2_TITLE: "Woonkamer" # tile title shown in the UI
|
||
TILE2_ICON: "\U000F04B9" # MDI glyph — also add to MDI_GLYPH_* list above if not already there
|
||
TILE2_TYPE: "light" # tile type: light | fan | switch | scene | script | cover | climate
|
||
TILE2_TAP_ACTION: "auto" # tap action: auto | toggle | activate | fan_toggle_preset | custom
|
||
TILE2_LONGPRESS: "slider" # long press mode: none | slider | action
|
||
TILE2_VALUE_MODE: "auto" # value mode: auto | brightness | percentage | preset | text
|
||
TILE2_LABEL_OFF: "Uit" # text shown when entity is off
|
||
TILE2_LABEL_ON: "Aan" # text shown for text-mode tiles when entity is on
|
||
TILE2_ACTIVE_STATE: "" # optional: exact HA state that counts as "active" (empty = default "on")
|
||
|
||
# --- colors ---
|
||
TILE2_CIRCLE_ACTIVE_COLOR: "${ACCENT_AUTO}" # icon circle color when active
|
||
TILE2_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE2_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE2_ICON_DISABLED_COLOR: "${ACCENT_AUTO}" # icon color when inactive
|
||
TILE2_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE2_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE2_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE2_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE2_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE2_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE2_TAP_SERVICE: "" # optional service override, e.g. light.toggle
|
||
TILE2_TAP_PARAM_KEY: "" # optional extra service parameter key
|
||
TILE2_TAP_PARAM_VAL: "" # optional extra service parameter value
|
||
# if LONGPRESS = slider:
|
||
TILE2_LONGPRESS_SLIDER: "auto" # slider type: auto | brightness | percentage | cover_position
|
||
TILE2_LONGPRESS_OFF_VALUE: "0" # initial slider value when entity is off
|
||
# if LONGPRESS = action:
|
||
TILE2_LONGPRESS_ACTION: "" # entity_id to fire on long press
|
||
TILE2_LONGPRESS_ACTION_TYPE: "" # entity type: light | fan | switch | scene | script | cover | climate (empty = same as TILE*_TYPE)
|
||
TILE2_LONGPRESS_ACTION_SERVICE: "" # service to call (default: activate/turn_on — set e.g. "light.toggle" to toggle instead)
|
||
# if TYPE = climate:
|
||
TILE2_CLIMATE_MIN_TEMP: "5.0" # minimum target temperature shown in the thermostat overlay
|
||
TILE2_CLIMATE_MAX_TEMP: "30.0" # maximum target temperature shown in the thermostat overlay
|
||
TILE2_CLIMATE_STEP: "0.5" # target temperature step size
|
||
TILE2_CLIMATE_CURRENT_TEMP_ATTRIBUTE: "current_temperature" # HA attribute for current room temperature
|
||
TILE2_CLIMATE_TARGET_TEMP_ATTRIBUTE: "temperature" # HA attribute for target temperature
|
||
TILE2_CLIMATE_HUMIDITY_ATTRIBUTE: "current_humidity" # HA attribute for humidity; leave as-is if unavailable
|
||
|
||
|
||
# ============================================================
|
||
# TILE 3
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE3_ENTITY: "script.1692558675845" # main Home Assistant entity for this tile
|
||
TILE3_STATE_ENTITY: "script.1692558675845" # entity used for active/inactive state styling
|
||
TILE3_TITLE: "Naar bed" # tile title shown in the UI
|
||
TILE3_ICON: "\U000F02E3" # MDI glyph — also add to MDI_GLYPH_* list above if not already there
|
||
TILE3_TYPE: "script" # tile type: light | fan | switch | scene | script | cover | climate
|
||
TILE3_TAP_ACTION: "auto" # tap action: auto | toggle | activate | fan_toggle_preset | custom
|
||
TILE3_LONGPRESS: "none" # long press mode: none | slider | action
|
||
TILE3_VALUE_MODE: "text" # value mode: auto | brightness | percentage | preset | text
|
||
TILE3_LABEL_OFF: "Start" # text shown when entity is off
|
||
TILE3_LABEL_ON: "Start" # text shown for text-mode tiles when entity is on
|
||
TILE3_ACTIVE_STATE: "" # optional: exact HA state that counts as "active" (empty = default "on")
|
||
|
||
# --- colors ---
|
||
TILE3_CIRCLE_ACTIVE_COLOR: "${ACCENT_AUTO}" # icon circle color when active
|
||
TILE3_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE3_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE3_ICON_DISABLED_COLOR: "${ACCENT_AUTO}" # icon color when inactive
|
||
TILE3_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE3_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE3_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE3_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE3_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE3_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE3_TAP_SERVICE: "" # optional service override, e.g. light.toggle
|
||
TILE3_TAP_PARAM_KEY: "" # optional extra service parameter key
|
||
TILE3_TAP_PARAM_VAL: "" # optional extra service parameter value
|
||
# if LONGPRESS = slider:
|
||
TILE3_LONGPRESS_SLIDER: "auto" # slider type: auto | brightness | percentage | cover_position
|
||
TILE3_LONGPRESS_OFF_VALUE: "0" # initial slider value when entity is off
|
||
# if LONGPRESS = action:
|
||
TILE3_LONGPRESS_ACTION: "" # entity_id to fire on long press
|
||
TILE3_LONGPRESS_ACTION_TYPE: "" # entity type: light | fan | switch | scene | script | cover | climate (empty = same as TILE*_TYPE)
|
||
TILE3_LONGPRESS_ACTION_SERVICE: "" # service to call (default: activate/turn_on — set e.g. "light.toggle" to toggle instead)
|
||
# if TYPE = climate:
|
||
TILE3_CLIMATE_MIN_TEMP: "5.0" # minimum target temperature shown in the thermostat overlay
|
||
TILE3_CLIMATE_MAX_TEMP: "30.0" # maximum target temperature shown in the thermostat overlay
|
||
TILE3_CLIMATE_STEP: "0.5" # target temperature step size
|
||
TILE3_CLIMATE_CURRENT_TEMP_ATTRIBUTE: "current_temperature" # HA attribute for current room temperature
|
||
TILE3_CLIMATE_TARGET_TEMP_ATTRIBUTE: "temperature" # HA attribute for target temperature
|
||
TILE3_CLIMATE_HUMIDITY_ATTRIBUTE: "current_humidity" # HA attribute for humidity; leave as-is if unavailable
|
||
|
||
|
||
# ============================================================
|
||
# TILE 4
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE4_ENTITY: "script.goede_morgen" # main Home Assistant entity for this tile
|
||
TILE4_STATE_ENTITY: "script.goede_morgen" # entity used for active/inactive state styling
|
||
TILE4_TITLE: "Goede morgen" # tile title shown in the UI
|
||
TILE4_ICON: "\U000F059C" # MDI glyph — also add to MDI_GLYPH_* list above if not already there
|
||
TILE4_TYPE: "script" # tile type: light | fan | switch | scene | script | cover | climate
|
||
TILE4_TAP_ACTION: "auto" # tap action: auto | toggle | activate | fan_toggle_preset | custom
|
||
TILE4_LONGPRESS: "none" # long press mode: none | slider | action
|
||
TILE4_VALUE_MODE: "text" # value mode: auto | brightness | percentage | preset | text
|
||
TILE4_LABEL_OFF: "Start" # text shown when entity is off
|
||
TILE4_LABEL_ON: "Start" # text shown for text-mode tiles when entity is on
|
||
TILE4_ACTIVE_STATE: "" # optional: exact HA state that counts as "active" (empty = default "on")
|
||
|
||
# --- colors ---
|
||
TILE4_CIRCLE_ACTIVE_COLOR: "${ACCENT_AUTO}" # icon circle color when active
|
||
TILE4_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE4_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE4_ICON_DISABLED_COLOR: "${ACCENT_AUTO}" # icon color when inactive
|
||
TILE4_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE4_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE4_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE4_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE4_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE4_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE4_TAP_SERVICE: "" # optional service override, e.g. light.toggle
|
||
TILE4_TAP_PARAM_KEY: "" # optional extra service parameter key
|
||
TILE4_TAP_PARAM_VAL: "" # optional extra service parameter value
|
||
# if LONGPRESS = slider:
|
||
TILE4_LONGPRESS_SLIDER: "auto" # slider type: auto | brightness | percentage | cover_position
|
||
TILE4_LONGPRESS_OFF_VALUE: "0" # initial slider value when entity is off
|
||
# if LONGPRESS = action:
|
||
TILE4_LONGPRESS_ACTION: "" # entity_id to fire on long press
|
||
TILE4_LONGPRESS_ACTION_TYPE: "" # entity type: light | fan | switch | scene | script | cover | climate (empty = same as TILE*_TYPE)
|
||
TILE4_LONGPRESS_ACTION_SERVICE: "" # service to call (default: activate/turn_on — set e.g. "light.toggle" to toggle instead)
|
||
# if TYPE = climate:
|
||
TILE4_CLIMATE_MIN_TEMP: "5.0" # minimum target temperature shown in the thermostat overlay
|
||
TILE4_CLIMATE_MAX_TEMP: "30.0" # maximum target temperature shown in the thermostat overlay
|
||
TILE4_CLIMATE_STEP: "0.5" # target temperature step size
|
||
TILE4_CLIMATE_CURRENT_TEMP_ATTRIBUTE: "current_temperature" # HA attribute for current room temperature
|
||
TILE4_CLIMATE_TARGET_TEMP_ATTRIBUTE: "temperature" # HA attribute for target temperature
|
||
TILE4_CLIMATE_HUMIDITY_ATTRIBUTE: "current_humidity" # HA attribute for humidity; leave as-is if unavailable
|
||
|
||
|
||
# ============================================================
|
||
# TILE 5
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE5_ENTITY: "script.1692529759669" # main Home Assistant entity for this tile
|
||
TILE5_STATE_ENTITY: "script.1692529759669" # entity used for active/inactive state styling
|
||
TILE5_TITLE: "Alles uit" # tile title shown in the UI
|
||
TILE5_ICON: "\U000F0425" # MDI glyph — also add to MDI_GLYPH_* list above if not already there
|
||
TILE5_TYPE: "script" # tile type: light | fan | switch | scene | script | cover | climate
|
||
TILE5_TAP_ACTION: "auto" # tap action: auto | toggle | activate | fan_toggle_preset | custom
|
||
TILE5_LONGPRESS: "none" # long press mode: none | slider | action
|
||
TILE5_VALUE_MODE: "text" # value mode: auto | brightness | percentage | preset | text
|
||
TILE5_LABEL_OFF: "Start" # text shown when entity is off (closed)
|
||
TILE5_LABEL_ON: "Start" # text shown when entity is on (open)
|
||
TILE5_ACTIVE_STATE: "" # optional: exact HA state that counts as "active" (empty = default "on")
|
||
|
||
# --- colors ---
|
||
TILE5_CIRCLE_ACTIVE_COLOR: "${ACCENT_AUTO}" # icon circle color when active
|
||
TILE5_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE5_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE5_ICON_DISABLED_COLOR: "${ACCENT_AUTO}" # icon color when inactive
|
||
TILE5_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE5_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE5_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE5_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE5_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE5_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE5_TAP_SERVICE: "" # optional service override, e.g. light.toggle
|
||
TILE5_TAP_PARAM_KEY: "" # optional extra service parameter key
|
||
TILE5_TAP_PARAM_VAL: "" # optional extra service parameter value
|
||
# if LONGPRESS = slider:
|
||
TILE5_LONGPRESS_SLIDER: "auto" # slider type: auto | brightness | percentage | cover_position
|
||
TILE5_LONGPRESS_OFF_VALUE: "0" # initial slider value when entity is off
|
||
# if LONGPRESS = action:
|
||
TILE5_LONGPRESS_ACTION: "" # entity_id to fire on long press
|
||
TILE5_LONGPRESS_ACTION_TYPE: "" # entity type: light | fan | switch | scene | script | cover | climate (empty = same as TILE*_TYPE)
|
||
TILE5_LONGPRESS_ACTION_SERVICE: "" # service to call (default: activate/turn_on — set e.g. "light.toggle" to toggle instead)
|
||
# if TYPE = climate:
|
||
TILE5_CLIMATE_MIN_TEMP: "5.0" # minimum target temperature shown in the thermostat overlay
|
||
TILE5_CLIMATE_MAX_TEMP: "30.0" # maximum target temperature shown in the thermostat overlay
|
||
TILE5_CLIMATE_STEP: "0.5" # target temperature step size
|
||
TILE5_CLIMATE_CURRENT_TEMP_ATTRIBUTE: "current_temperature" # HA attribute for current room temperature
|
||
TILE5_CLIMATE_TARGET_TEMP_ATTRIBUTE: "temperature" # HA attribute for target temperature
|
||
TILE5_CLIMATE_HUMIDITY_ATTRIBUTE: "current_humidity" # HA attribute for humidity; leave as-is if unavailable
|
||
|
||
|
||
# ============================================================
|
||
# TILE 6
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE6_ENTITY: "vacuum.s8" # main Home Assistant entity for this tile
|
||
TILE6_STATE_ENTITY: "binary_sensor.s8_schoonmaken" # entity used for active/inactive state styling
|
||
TILE6_TITLE: "Pippa" # tile title shown in the UI
|
||
TILE6_ICON: "\U000F070D" # MDI glyph — also add to MDI_GLYPH_* list above if not already there
|
||
TILE6_TYPE: "switch" # tile type: light | fan | switch | scene | script | cover | climate
|
||
TILE6_TAP_ACTION: "toggle" # tap action: auto | toggle | activate | fan_toggle_preset | custom
|
||
TILE6_LONGPRESS: "vacuum" # long press mode: none | slider | action
|
||
TILE6_VALUE_MODE: "text" # value mode: auto | brightness | percentage | preset | text
|
||
TILE6_LABEL_OFF: "Gedockt" # text shown when entity is off
|
||
TILE6_LABEL_ON: "Bezig" # text shown for text-mode tiles when entity is on
|
||
TILE6_ACTIVE_STATE: "" # optional: exact HA state that counts as "active" (empty = default "on")
|
||
|
||
# --- colors ---
|
||
TILE6_CIRCLE_ACTIVE_COLOR: "${ACCENT_AUTO}" # icon circle color when active
|
||
TILE6_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE6_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE6_ICON_DISABLED_COLOR: "${ACCENT_AUTO}" # icon color when inactive
|
||
TILE6_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE6_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE6_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE6_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE6_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE6_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE6_TAP_SERVICE: "vacuum.start" # optional service override, e.g. fan.toggle
|
||
TILE6_TAP_PARAM_KEY: "" # optional extra service parameter key
|
||
TILE6_TAP_PARAM_VAL: "" # optional extra service parameter value
|
||
# if LONGPRESS = slider:
|
||
TILE6_LONGPRESS_SLIDER: "auto" # slider type: auto | brightness | percentage | cover_position
|
||
TILE6_LONGPRESS_OFF_VALUE: "0" # initial slider value when entity is off
|
||
# if LONGPRESS = action:
|
||
TILE6_LONGPRESS_ACTION: "vacuum.s8" # entity_id to fire on long press
|
||
TILE6_LONGPRESS_ACTION_TYPE: "switch" # entity type: light | fan | switch | scene | script | cover | climate (empty = same as TILE*_TYPE)
|
||
TILE6_LONGPRESS_ACTION_SERVICE: "vacuum.return_to_base" # service to call (default: activate/turn_on — set e.g. "light.toggle" to toggle instead)
|
||
# if TYPE = climate:
|
||
TILE6_CLIMATE_MIN_TEMP: "5.0" # minimum target temperature shown in the thermostat overlay
|
||
TILE6_CLIMATE_MAX_TEMP: "30.0" # maximum target temperature shown in the thermostat overlay
|
||
TILE6_CLIMATE_STEP: "0.5" # target temperature step size
|
||
TILE6_CLIMATE_CURRENT_TEMP_ATTRIBUTE: "current_temperature" # HA attribute for current room temperature
|
||
TILE6_CLIMATE_TARGET_TEMP_ATTRIBUTE: "temperature" # HA attribute for target temperature
|
||
TILE6_CLIMATE_HUMIDITY_ATTRIBUTE: "current_humidity" # HA attribute for humidity; leave as-is if unavailable
|
||
# ============================================================
|
||
# TILE 7 — Tafellamp
|
||
# Alleen-helderheid lamp (HA meldt supported_color_modes: ['brightness']),
|
||
# dus de kleurenknop in de slider-overlay blijft vanzelf verborgen.
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE7_ENTITY: "light.tafellamp_licht" # main Home Assistant entity for this tile
|
||
TILE7_STATE_ENTITY: "light.tafellamp_licht" # entity used for active/inactive state styling
|
||
TILE7_TITLE: "Tafellamp" # tile title shown in the UI
|
||
TILE7_ICON: "\U000F06B5" # mdi:lamp — also listed in MDI_GLYPH_7 above
|
||
TILE7_TYPE: "light" # tile type: light | fan | switch | scene | script | cover | climate
|
||
TILE7_TAP_ACTION: "auto" # tap action: auto | toggle | activate | fan_toggle_preset
|
||
TILE7_LONGPRESS: "slider" # long press mode: none | slider | action | vacuum
|
||
TILE7_VALUE_MODE: "auto" # value mode: auto | brightness | percentage | preset | text
|
||
TILE7_LABEL_OFF: "Uit" # text shown when entity is off
|
||
TILE7_LABEL_ON: "Aan" # text shown for text-mode tiles when entity is on
|
||
TILE7_ACTIVE_STATE: "" # optional: exact HA state that counts as "active" (empty = default "on")
|
||
|
||
# --- colors ---
|
||
TILE7_CIRCLE_ACTIVE_COLOR: "${ACCENT_AUTO}" # icon circle color when active
|
||
TILE7_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE7_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE7_ICON_DISABLED_COLOR: "${ACCENT_AUTO}" # icon color when inactive
|
||
TILE7_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE7_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE7_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE7_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE7_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE7_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE7_TAP_SERVICE: "" # optional service override, e.g. light.toggle
|
||
TILE7_TAP_PARAM_KEY: "" # optional extra service parameter key
|
||
TILE7_TAP_PARAM_VAL: "" # optional extra service parameter value
|
||
# if LONGPRESS = slider:
|
||
TILE7_LONGPRESS_SLIDER: "auto" # auto = brightness voor een light
|
||
TILE7_LONGPRESS_OFF_VALUE: "0" # initial slider value when entity is off
|
||
# if LONGPRESS = action:
|
||
TILE7_LONGPRESS_ACTION: "" # entity_id to fire on long press
|
||
TILE7_LONGPRESS_ACTION_TYPE: "" # entity type (empty = same as TILE*_TYPE)
|
||
TILE7_LONGPRESS_ACTION_SERVICE: "" # service to call (default: activate/turn_on)
|
||
|
||
# ============================================================
|
||
# TILE 8 — Brievenbus reset
|
||
# input_select.brievenbus_status heeft opties ['Vol', 'Leeg'].
|
||
# De tegel licht op zodra de status "Vol" is (ACTIVE_STATE), en een korte
|
||
# tik zet hem terug op "Leeg" via input_select.select_option.
|
||
# ============================================================
|
||
|
||
# --- basic settings ---
|
||
TILE8_ENTITY: "input_select.brievenbus_status" # main Home Assistant entity for this tile
|
||
TILE8_STATE_ENTITY: "input_select.brievenbus_status" # entity used for active/inactive state styling
|
||
TILE8_TITLE: "Brievenbus" # tile title shown in the UI
|
||
TILE8_ICON: "\U000F06EE" # mdi:mailbox — also listed in MDI_GLYPH_8 above
|
||
TILE8_TYPE: "switch" # generiek type; het echte gedrag komt uit TAP_SERVICE hieronder
|
||
TILE8_TAP_ACTION: "activate" # activate + TAP_SERVICE = losse service call
|
||
TILE8_LONGPRESS: "none" # long press stuurt alleen het event naar HA
|
||
TILE8_VALUE_MODE: "text" # toont LABEL_ON / LABEL_OFF
|
||
TILE8_LABEL_OFF: "Leeg" # text shown when entity is off / inactive
|
||
TILE8_LABEL_ON: "Vol" # text shown when entity is active
|
||
TILE8_ACTIVE_STATE: "Vol" # tegel is "aan" zodra de status exact "Vol" is
|
||
|
||
# --- colors ---
|
||
TILE8_CIRCLE_ACTIVE_COLOR: "0xE5484D" # rood zodra er post ligt
|
||
TILE8_CIRCLE_DISABLED_COLOR: "0x7B7B6F" # icon circle color when inactive
|
||
TILE8_ICON_ACTIVE_COLOR: "0xFFFFFF" # icon color when active
|
||
TILE8_ICON_DISABLED_COLOR: "0xE5484D" # icon color when inactive
|
||
TILE8_BG_ACTIVE_COLOR: "0xFFFFFF" # tile background color when active
|
||
TILE8_BG_DISABLED_COLOR: "0x939391" # tile background color when inactive
|
||
TILE8_TITLE_ACTIVE_COLOR: "0x000000" # title color when active
|
||
TILE8_TITLE_DISABLED_COLOR: "0xFFFFFF" # title color when inactive
|
||
TILE8_VALUE_ACTIVE_COLOR: "0x7A7A7C" # value color when active
|
||
TILE8_VALUE_DISABLED_COLOR: "0xD9D9D9" # value color when inactive
|
||
|
||
# --- advanced settings ---
|
||
TILE8_TAP_SERVICE: "input_select.select_option" # zet de input_select op een vaste optie
|
||
TILE8_TAP_PARAM_KEY: "option" # parameternaam voor input_select.select_option
|
||
TILE8_TAP_PARAM_VAL: "Leeg" # de "reset"-waarde
|
||
# if LONGPRESS = slider:
|
||
TILE8_LONGPRESS_SLIDER: "auto" # niet in gebruik (LONGPRESS = none)
|
||
TILE8_LONGPRESS_OFF_VALUE: "0" # niet in gebruik
|
||
# if LONGPRESS = action:
|
||
TILE8_LONGPRESS_ACTION: "" # entity_id to fire on long press
|
||
TILE8_LONGPRESS_ACTION_TYPE: "" # entity type (empty = same as TILE*_TYPE)
|
||
TILE8_LONGPRESS_ACTION_SERVICE: "" # service to call (default: activate/turn_on)
|
||
|
||
# ============================================================
|
||
# TILE 9 / TILE 10 — nog vrij
|
||
# Rij 5 van het raster (GRID_ROW5_Y) is al gedefinieerd en leeg.
|
||
# Zie TILE_CONFIGURATION.md, sectie "Een tegel toevoegen" voor de
|
||
# zes plekken die je per extra tegel moet invullen.
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# ORIENTATION / LAYOUT
|
||
# Uncomment ONE preset block below, comment out the others.
|
||
# Touch calibration values (TOUCH_CAL_*) are device-specific —
|
||
# portrait/flipped values are approximate and may need tuning.
|
||
#
|
||
# LVGL_ROTATION (0/90/180/270) drives the rotation — LVGL rotates both the
|
||
# display output and the touch coordinates (ESPHome 2026.4+). DISPLAY_W/H are
|
||
# the *logical* (post-rotation) canvas size used by the tile layout below;
|
||
# the physical panel is always driven at its native 240×320 (see display:).
|
||
#
|
||
# Tile content layout:
|
||
# Landscape — icon left, title + value to its right
|
||
# Portrait — icon centered on top, title + value stacked below
|
||
# ============================================================
|
||
|
||
# ----- 0° — Landscape 320×240 (default, USB on right) -----
|
||
ORIENTATION: "0"
|
||
DISPLAY_W: "320"
|
||
DISPLAY_H: "240"
|
||
LVGL_ROTATION: "90"
|
||
TOUCH_SWAP_XY: "false"
|
||
TOUCH_MIRROR_X: "false"
|
||
TOUCH_MIRROR_Y: "false"
|
||
TOUCH_CAL_X_MIN: "280"
|
||
TOUCH_CAL_X_MAX: "3860"
|
||
TOUCH_CAL_Y_MIN: "340"
|
||
TOUCH_CAL_Y_MAX: "3860"
|
||
GRID_COL1_X: "9"
|
||
GRID_COL2_X: "164"
|
||
GRID_ROW1_Y: "8"
|
||
GRID_ROW2_Y: "68"
|
||
GRID_ROW3_Y: "128"
|
||
GRID_ROW4_Y: "188"
|
||
GRID_ROW5_Y: "248"
|
||
SCROLL_Y: "44"
|
||
SCROLL_H: "196"
|
||
TILE_W: "147"
|
||
TILE_H: "52"
|
||
HEADER_Y: "14"
|
||
OVERLAY_SLIDER_W: "96"
|
||
OVERLAY_SLIDER_H: "160"
|
||
OVERLAY_ICON_Y: "74"
|
||
BG_TOP_COLOR: "0xAEC4D9" # achtergrond boven (zacht blauw)
|
||
BG_BOTTOM_COLOR: "0xD6BB9E" # achtergrond onder (warm zand)
|
||
TILE_ICON_X: "0"
|
||
TILE_ICON_Y: "0"
|
||
TILE_TITLE_X: "48"
|
||
TILE_TITLE_Y: "4"
|
||
TILE_VALUE_X: "48"
|
||
TILE_VALUE_Y: "18"
|
||
|
||
# ----- 90° — Portrait 240×320 (USB on bottom) -----
|
||
# ORIENTATION: "90"
|
||
# DISPLAY_W: "240"
|
||
# DISPLAY_H: "320"
|
||
# LVGL_ROTATION: "180"
|
||
# TOUCH_SWAP_XY: "false"
|
||
# TOUCH_MIRROR_X: "true"
|
||
# TOUCH_MIRROR_Y: "false"
|
||
# TOUCH_CAL_X_MIN: "280"
|
||
# TOUCH_CAL_X_MAX: "3860"
|
||
# TOUCH_CAL_Y_MIN: "340"
|
||
# TOUCH_CAL_Y_MAX: "3860"
|
||
# GRID_COL1_X: "6"
|
||
# GRID_COL2_X: "123"
|
||
# GRID_ROW1_Y: "8"
|
||
# GRID_ROW2_Y: "96"
|
||
# GRID_ROW3_Y: "184"
|
||
# GRID_ROW4_Y: "272"
|
||
# GRID_ROW5_Y: "360"
|
||
# SCROLL_Y: "44"
|
||
# SCROLL_H: "276"
|
||
# TILE_W: "111"
|
||
# TILE_H: "80"
|
||
# HEADER_Y: "14"
|
||
# OVERLAY_SLIDER_W: "96"
|
||
# OVERLAY_SLIDER_H: "200"
|
||
# OVERLAY_ICON_Y: "94"
|
||
# BG_TOP_COLOR: "0xAEC4D9" # achtergrond boven (zacht blauw)
|
||
# BG_BOTTOM_COLOR: "0xD6BB9E" # achtergrond onder (warm zand)
|
||
# TILE_ICON_X: "0"
|
||
# TILE_ICON_Y: "0"
|
||
# TILE_TITLE_X: "4"
|
||
# TILE_TITLE_Y: "38"
|
||
# TILE_VALUE_X: "4"
|
||
# TILE_VALUE_Y: "52"
|
||
|
||
# ----- 180° — Landscape flipped 320×240 (USB on left) -----
|
||
# ORIENTATION: "180"
|
||
# DISPLAY_W: "320"
|
||
# DISPLAY_H: "240"
|
||
# LVGL_ROTATION: "270"
|
||
# TOUCH_SWAP_XY: "false"
|
||
# TOUCH_MIRROR_X: "true"
|
||
# TOUCH_MIRROR_Y: "false"
|
||
# TOUCH_CAL_X_MIN: "280"
|
||
# TOUCH_CAL_X_MAX: "3860"
|
||
# TOUCH_CAL_Y_MIN: "340"
|
||
# TOUCH_CAL_Y_MAX: "3860"
|
||
# GRID_COL1_X: "9"
|
||
# GRID_COL2_X: "164"
|
||
# GRID_ROW1_Y: "8"
|
||
# GRID_ROW2_Y: "68"
|
||
# GRID_ROW3_Y: "128"
|
||
# GRID_ROW4_Y: "188"
|
||
# GRID_ROW5_Y: "248"
|
||
# SCROLL_Y: "44"
|
||
# SCROLL_H: "196"
|
||
# TILE_W: "147"
|
||
# TILE_H: "52"
|
||
# HEADER_Y: "14"
|
||
# OVERLAY_SLIDER_W: "96"
|
||
# OVERLAY_SLIDER_H: "160"
|
||
# OVERLAY_ICON_Y: "74"
|
||
# BG_TOP_COLOR: "0xAEC4D9" # achtergrond boven (zacht blauw)
|
||
# BG_BOTTOM_COLOR: "0xD6BB9E" # achtergrond onder (warm zand)
|
||
# TILE_ICON_X: "0"
|
||
# TILE_ICON_Y: "0"
|
||
# TILE_TITLE_X: "48"
|
||
# TILE_TITLE_Y: "4"
|
||
# TILE_VALUE_X: "48"
|
||
# TILE_VALUE_Y: "18"
|
||
|
||
# ----- 270° — Portrait flipped 240×320 (USB on top) -----
|
||
# ORIENTATION: "270"
|
||
# DISPLAY_W: "240"
|
||
# DISPLAY_H: "320"
|
||
# LVGL_ROTATION: "0"
|
||
# TOUCH_SWAP_XY: "false"
|
||
# TOUCH_MIRROR_X: "true"
|
||
# TOUCH_MIRROR_Y: "false"
|
||
# TOUCH_CAL_X_MIN: "280"
|
||
# TOUCH_CAL_X_MAX: "3860"
|
||
# TOUCH_CAL_Y_MIN: "340"
|
||
# TOUCH_CAL_Y_MAX: "3860"
|
||
# GRID_COL1_X: "6"
|
||
# GRID_COL2_X: "123"
|
||
# GRID_ROW1_Y: "8"
|
||
# GRID_ROW2_Y: "96"
|
||
# GRID_ROW3_Y: "184"
|
||
# GRID_ROW4_Y: "272"
|
||
# GRID_ROW5_Y: "360"
|
||
# SCROLL_Y: "44"
|
||
# SCROLL_H: "276"
|
||
# TILE_W: "111"
|
||
# TILE_H: "80"
|
||
# HEADER_Y: "14"
|
||
# OVERLAY_SLIDER_W: "96"
|
||
# OVERLAY_SLIDER_H: "200"
|
||
# OVERLAY_ICON_Y: "94"
|
||
# BG_TOP_COLOR: "0xAEC4D9" # achtergrond boven (zacht blauw)
|
||
# BG_BOTTOM_COLOR: "0xD6BB9E" # achtergrond onder (warm zand)
|
||
# TILE_ICON_X: "0"
|
||
# TILE_ICON_Y: "0"
|
||
# TILE_TITLE_X: "4"
|
||
# TILE_TITLE_Y: "38"
|
||
# TILE_VALUE_X: "4"
|
||
# TILE_VALUE_Y: "52"
|
||
|
||
# ============================================================
|
||
# 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).
|
||
# The Material Design Icons font is included at fonts/materialdesignicons-webfont.ttf (Apache 2.0).
|
||
# Source: https://github.com/Templarian/MaterialDesign-Webfont
|
||
# ============================================================
|
||
|
||
esphome:
|
||
name: smartdisplay
|
||
friendly_name: SmartDisplay
|
||
on_boot:
|
||
priority: 600
|
||
then:
|
||
- lambda: |-
|
||
id(last_touch_ms) = millis();
|
||
id(display_dimmed) = false;
|
||
- light.turn_on:
|
||
id: back_light
|
||
brightness: !lambda "return atoi(\"${MAX_BRIGHTNESS}\") / 100.0f;"
|
||
- script.execute: ui_refresh
|
||
|
||
esp32:
|
||
board: esp32dev
|
||
framework:
|
||
type: arduino
|
||
advanced:
|
||
# ESPHome adviseert deze twee zelf in de boot-log. Samen leveren ze
|
||
# ~40KB extra IRAM en een kleinere binary, en op dit bord telt elke KB.
|
||
sram1_as_iram: true
|
||
# Let op: hiermee draait deze firmware niet meer op ESP32's ouder dan
|
||
# rev 3.1. Dit bord is 3.1, dus prima, maar niet blind overzetten.
|
||
minimum_chip_revision: "3.1"
|
||
version: recommended
|
||
variant: esp32
|
||
|
||
logger:
|
||
# Voor diagnose tijdelijk op DEBUG of VERBOSE zetten. Let op: ESPHome logt
|
||
# zijn configuratieregels op niveau CONFIG, die zijn op INFO onzichtbaar.
|
||
level: INFO
|
||
|
||
|
||
# Credentials are loaded from secrets.yaml — copy secrets.yaml.example to secrets.yaml and fill in your values.
|
||
api:
|
||
encryption:
|
||
key: !secret api_encryption_key
|
||
|
||
ota:
|
||
- platform: esphome
|
||
password: !secret ota_password
|
||
|
||
wifi:
|
||
ssid: !secret wifi_ssid
|
||
password: !secret wifi_password
|
||
ap:
|
||
ssid: "Smartdisplay Fallback Hotspot"
|
||
password: !secret ap_password
|
||
|
||
captive_portal:
|
||
|
||
interval:
|
||
- interval: 1s
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
if (std::string("${AUTO_DIM}") != "true") return false;
|
||
if (id(display_dimmed)) return false;
|
||
return (millis() - id(last_touch_ms)) > (atoi("${AUTO_DIM_TIMEOUT}") * 1000UL);
|
||
then:
|
||
- script.execute: dim_display
|
||
|
||
time:
|
||
- platform: homeassistant
|
||
id: homeassistant_time
|
||
on_time:
|
||
- seconds: 0
|
||
minutes: /1
|
||
then:
|
||
- script.execute: ui_refresh
|
||
|
||
- seconds: 0
|
||
minutes: 0
|
||
hours: ${NIGHT_START_HOUR}
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return id(display_dimmed);'
|
||
then:
|
||
- script.execute: dim_display
|
||
|
||
- seconds: 0
|
||
minutes: 0
|
||
hours: ${NIGHT_END_HOUR}
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return id(display_dimmed);'
|
||
then:
|
||
- script.execute: dim_display
|
||
|
||
globals:
|
||
- id: active_entity
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: active_control_kind
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: active_value_suffix
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: last_touch_ms
|
||
type: uint32_t
|
||
restore_value: no
|
||
initial_value: '0'
|
||
|
||
- id: display_dimmed
|
||
type: bool
|
||
restore_value: no
|
||
initial_value: 'false'
|
||
|
||
- id: active_label_off
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: active_label_on
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: active_title
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: active_icon_text
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: active_brightness_pct
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '100'
|
||
|
||
- id: active_supports_color
|
||
type: bool
|
||
restore_value: no
|
||
initial_value: 'false'
|
||
|
||
- id: active_supports_temperature
|
||
type: bool
|
||
restore_value: no
|
||
initial_value: 'false'
|
||
|
||
- id: active_color_detail_tab
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '0'
|
||
|
||
- id: active_climate_current_x10
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '0'
|
||
|
||
- id: active_climate_target_x10
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '200'
|
||
|
||
- id: active_climate_humidity
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '-1'
|
||
|
||
- id: active_climate_min_x10
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '50'
|
||
|
||
- id: active_climate_max_x10
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '300'
|
||
|
||
- id: active_climate_step_x10
|
||
type: int
|
||
restore_value: no
|
||
initial_value: '5'
|
||
|
||
- id: active_climate_mode
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
- id: active_climate_supported_modes
|
||
type: std::string
|
||
restore_value: no
|
||
|
||
font:
|
||
- file: "gfonts://Roboto@500"
|
||
id: time_label
|
||
size: 18
|
||
bpp: 4
|
||
glyphs: ['0','1','2','3','4','5','6','7','8','9',':',' ', 'A', 'M', 'P']
|
||
|
||
- file: "gfonts://Roboto@500"
|
||
id: headline
|
||
size: 18
|
||
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@700"
|
||
id: label
|
||
size: 11
|
||
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: sublabel
|
||
size: 11
|
||
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: sublabel_big
|
||
size: 14
|
||
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: 28
|
||
glyphs:
|
||
- "${MDI_GLYPH_1}"
|
||
- "${MDI_GLYPH_2}"
|
||
- "${MDI_GLYPH_3}"
|
||
- "${MDI_GLYPH_4}"
|
||
- "${MDI_GLYPH_5}"
|
||
- "${MDI_GLYPH_6}"
|
||
- "${MDI_GLYPH_7}"
|
||
- "${MDI_GLYPH_8}"
|
||
- "\U000F012C" # check
|
||
|
||
# Geheugen zichtbaar maken: dit bord heeft geen PSRAM, dus de vrije heap
|
||
# bepaalt hoeveel UI erbij kan. Verschijnt ook als entity in Home Assistant.
|
||
debug:
|
||
update_interval: 30s
|
||
|
||
sensor:
|
||
- platform: debug
|
||
free:
|
||
name: "Heap Free"
|
||
id: heap_free
|
||
fragmentation:
|
||
name: "Heap Fragmentation"
|
||
block:
|
||
name: "Heap Largest Block"
|
||
- platform: wifi_signal
|
||
name: "Wifi Signal"
|
||
update_interval: 600s
|
||
|
||
- platform: uptime
|
||
name: "Uptime"
|
||
id: uptime_s
|
||
update_interval: 15s
|
||
|
||
- platform: homeassistant
|
||
id: tile1_brightness
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: brightness
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_percentage
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: percentage
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_brightness
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: brightness
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_percentage
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: percentage
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_brightness
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: brightness
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_percentage
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: percentage
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_brightness
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: brightness
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_percentage
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: percentage
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_brightness
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: brightness
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_percentage
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: percentage
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_brightness
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: brightness
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_percentage
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: percentage
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
# Tegel 7 (Tafellamp) is een alleen-helderheid light: alleen brightness.
|
||
# Bewust geen percentage / cover_position / climate-sensors — elke
|
||
# HA-subscription kost heap op een bord zonder PSRAM.
|
||
- platform: homeassistant
|
||
id: tile7_brightness
|
||
entity_id: ${TILE7_ENTITY}
|
||
attribute: brightness
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_cover_position
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: current_position
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_cover_position
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: current_position
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_cover_position
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: current_position
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_cover_position
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: current_position
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_cover_position
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: current_position
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_cover_position
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: current_position
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_climate_current_temperature
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: ${TILE1_CLIMATE_CURRENT_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_climate_target_temperature
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: ${TILE1_CLIMATE_TARGET_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_climate_humidity
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: ${TILE1_CLIMATE_HUMIDITY_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_climate_current_temperature
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: ${TILE2_CLIMATE_CURRENT_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_climate_target_temperature
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: ${TILE2_CLIMATE_TARGET_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_climate_humidity
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: ${TILE2_CLIMATE_HUMIDITY_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_climate_current_temperature
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: ${TILE3_CLIMATE_CURRENT_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_climate_target_temperature
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: ${TILE3_CLIMATE_TARGET_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_climate_humidity
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: ${TILE3_CLIMATE_HUMIDITY_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_climate_current_temperature
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: ${TILE4_CLIMATE_CURRENT_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_climate_target_temperature
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: ${TILE4_CLIMATE_TARGET_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_climate_humidity
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: ${TILE4_CLIMATE_HUMIDITY_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_climate_current_temperature
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: ${TILE5_CLIMATE_CURRENT_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_climate_target_temperature
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: ${TILE5_CLIMATE_TARGET_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_climate_humidity
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: ${TILE5_CLIMATE_HUMIDITY_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_climate_current_temperature
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: ${TILE6_CLIMATE_CURRENT_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_climate_target_temperature
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: ${TILE6_CLIMATE_TARGET_TEMP_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_climate_humidity
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: ${TILE6_CLIMATE_HUMIDITY_ATTRIBUTE}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
binary_sensor:
|
||
- platform: status
|
||
name: "Node Status"
|
||
id: system_status
|
||
|
||
# ----------------------------------------------------------
|
||
# Text sensors
|
||
#
|
||
# smartdisplay_action:
|
||
# - Main touch event channel exported to Home Assistant
|
||
# - On short tap, publishes one of:
|
||
# tile1_press / tile2_press / tile3_press / tile4_press / tile5_press / tile6_press
|
||
# - The value is reset shortly after publishing
|
||
#
|
||
# tile*_preset:
|
||
# - Mirrors the Home Assistant preset_mode attribute for each tile entity
|
||
# - Used by the UI to display fan presets when applicable
|
||
# ----------------------------------------------------------
|
||
text_sensor:
|
||
- platform: template
|
||
id: smartdisplay_action
|
||
name: "SmartDisplay Action"
|
||
icon: mdi:gesture-tap-button
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile1
|
||
entity_id: ${TILE1_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile2
|
||
entity_id: ${TILE2_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile3
|
||
entity_id: ${TILE3_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile4
|
||
entity_id: ${TILE4_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile5
|
||
entity_id: ${TILE5_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile6
|
||
entity_id: ${TILE6_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile7
|
||
entity_id: ${TILE7_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: ha_state_tile8
|
||
entity_id: ${TILE8_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
# Gebruikt door de slider-overlay om te bepalen of de kleurknop nodig is.
|
||
# Deze lamp meldt ['brightness'], dus de knop blijft vanzelf verborgen.
|
||
- platform: homeassistant
|
||
id: tile7_supported_color_modes
|
||
entity_id: ${TILE7_ENTITY}
|
||
attribute: supported_color_modes
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_preset
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: preset_mode
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_preset
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: preset_mode
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_preset
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: preset_mode
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_preset
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: preset_mode
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_preset
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: preset_mode
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_preset
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: preset_mode
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_supported_color_modes
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: supported_color_modes
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_supported_color_modes
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: supported_color_modes
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_supported_color_modes
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: supported_color_modes
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_supported_color_modes
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: supported_color_modes
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_supported_color_modes
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: supported_color_modes
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_supported_color_modes
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: supported_color_modes
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_climate_mode
|
||
entity_id: ${TILE1_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_climate_mode
|
||
entity_id: ${TILE2_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_climate_mode
|
||
entity_id: ${TILE3_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_climate_mode
|
||
entity_id: ${TILE4_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_climate_mode
|
||
entity_id: ${TILE5_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_climate_mode
|
||
entity_id: ${TILE6_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile1_climate_supported_modes
|
||
entity_id: ${TILE1_ENTITY}
|
||
attribute: hvac_modes
|
||
|
||
- platform: homeassistant
|
||
id: tile2_climate_supported_modes
|
||
entity_id: ${TILE2_ENTITY}
|
||
attribute: hvac_modes
|
||
|
||
- platform: homeassistant
|
||
id: tile3_climate_supported_modes
|
||
entity_id: ${TILE3_ENTITY}
|
||
attribute: hvac_modes
|
||
|
||
- platform: homeassistant
|
||
id: tile4_climate_supported_modes
|
||
entity_id: ${TILE4_ENTITY}
|
||
attribute: hvac_modes
|
||
|
||
- platform: homeassistant
|
||
id: tile5_climate_supported_modes
|
||
entity_id: ${TILE5_ENTITY}
|
||
attribute: hvac_modes
|
||
|
||
- platform: homeassistant
|
||
id: tile6_climate_supported_modes
|
||
entity_id: ${TILE6_ENTITY}
|
||
attribute: hvac_modes
|
||
|
||
- platform: homeassistant
|
||
id: tile1_cover_state
|
||
entity_id: ${TILE1_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile2_cover_state
|
||
entity_id: ${TILE2_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile3_cover_state
|
||
entity_id: ${TILE3_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile4_cover_state
|
||
entity_id: ${TILE4_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile5_cover_state
|
||
entity_id: ${TILE5_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
- platform: homeassistant
|
||
id: tile6_cover_state
|
||
entity_id: ${TILE6_STATE_ENTITY}
|
||
on_value: { then: [ script.execute: ui_refresh ] }
|
||
|
||
switch:
|
||
- platform: restart
|
||
name: "Restart"
|
||
|
||
# Two SPI buses used by the CYD:
|
||
# - lcd: built-in display
|
||
# - my_touchscreen: built-in touch controller
|
||
spi:
|
||
- id: lcd
|
||
clk_pin: GPIO14
|
||
mosi_pin: GPIO13
|
||
|
||
|
||
# I2C bus voor de GT911 capacitieve touch (2432S032C).
|
||
# scan: true laat ESPHome bij het booten loggen welke adressen er echt zitten.
|
||
i2c:
|
||
- id: touch_i2c
|
||
sda: GPIO33
|
||
scl: GPIO32
|
||
scan: true
|
||
|
||
output:
|
||
- platform: ledc
|
||
pin: GPIO27
|
||
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 (GT911, capacitief, via I2C)
|
||
# De GT911 levert absolute coordinaten, dus geen calibration nodig.
|
||
# LVGL regelt de rotatie (zie `rotation:` in het lvgl blok) en draait
|
||
# touch-coordinaten automatisch mee.
|
||
touchscreen:
|
||
platform: gt911
|
||
id: ts_touch
|
||
i2c_id: touch_i2c
|
||
# interrupt_pin bewust weggelaten: met een hardware-interrupt zet ESPHome
|
||
# het pollen uit, en dan komt er niets binnen. Nu pollt hij elke 50ms.
|
||
update_interval: 50ms
|
||
|
||
transform:
|
||
swap_xy: ${TOUCH_SWAP_XY}
|
||
mirror_x: ${TOUCH_MIRROR_X}
|
||
mirror_y: ${TOUCH_MIRROR_Y}
|
||
|
||
on_touch:
|
||
- lambda: |-
|
||
id(last_touch_ms) = millis();
|
||
|
||
# Display configuration (ST7789V)
|
||
display:
|
||
- platform: mipi_spi
|
||
id: my_display
|
||
spi_id: lcd
|
||
model: ST7789V
|
||
# Zonder dit draait mipi_spi op zijn standaard van 10MHz, en dan duurt een
|
||
# volledig scherm ~123ms: dat zie je van boven naar beneden vullen.
|
||
# ESPHome's eigen CYD-modellen gebruiken 40MHz. 80MHz kan meestal ook.
|
||
data_rate: 40MHz
|
||
cs_pin: 15
|
||
dc_pin: 2
|
||
invert_colors: true
|
||
update_interval: never
|
||
auto_clear_enabled: false
|
||
|
||
# Native panel dimensions (portrait). Do NOT swap these per orientation and
|
||
# do NOT add a display transform — rotation is handled by LVGL below.
|
||
# ESPHome 2026.6+ rejects a swapped transform here with "Invalid offsets".
|
||
dimensions:
|
||
width: 240
|
||
height: 320
|
||
|
||
lvgl:
|
||
displays:
|
||
- my_display
|
||
touchscreens:
|
||
- touchscreen_id: ts_touch
|
||
|
||
# De ESP32-WROOM-32 heeft geen PSRAM; een volledige framebuffer (320x240x2
|
||
# = 153600 bytes) past niet in DRAM. 25% is de gebruikelijke waarde.
|
||
# 12% in plaats van 25%: laat ~20KB extra aaneengesloten geheugen vrij,
|
||
# en LVGL schaalt de stroken van een ARGB-laag mee met de beschikbare
|
||
# tekenruimte, dus ook de gevraagde allocaties worden kleiner.
|
||
buffer_size: 12%
|
||
|
||
# Directe lokale feedback op de HELE UI, niet alleen op de tegels.
|
||
# Een thema zet de stijl per widget-type, dus dit dekt in een klap alle
|
||
# knoppen, tabs, sliders en de thermostaatring. Niet-klikbare widgets
|
||
# komen nooit in de pressed-state, dus die blijven onaangeroerd.
|
||
theme:
|
||
obj:
|
||
pressed:
|
||
# bg_opa en NIET opa: `opa` op widget-niveau dwingt LVGL het element
|
||
# naar een aparte laag te renderen, en die laag kost tientallen KB's
|
||
# per ingedrukte widget. Dat liep hier het geheugen uit.
|
||
bg_opa: 45%
|
||
slider:
|
||
knob:
|
||
pressed:
|
||
bg_opa: 65%
|
||
arc:
|
||
knob:
|
||
pressed:
|
||
bg_opa: 65%
|
||
|
||
# LVGL owns the rotation (display + touch) since ESPHome 2026.4.
|
||
rotation: ${LVGL_ROTATION}
|
||
|
||
style_definitions:
|
||
# Achtergrond: twee-stops verticale gradient in plaats van de 320x240
|
||
# RGB565-wallpaper. Scheelt ~150 KB flash en een image-blit per hertekening,
|
||
# wat je direct terugziet in de vloeiendheid van het scrollen.
|
||
- id: style_bg_grad
|
||
bg_color: ${BG_TOP_COLOR}
|
||
bg_grad_color: ${BG_BOTTOM_COLOR}
|
||
bg_grad_dir: VER
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
radius: 0
|
||
|
||
- id: style_tile
|
||
bg_color: 0xFFFFFF
|
||
bg_opa: 95%
|
||
radius: 18
|
||
border_width: 0
|
||
pad_left: 8
|
||
pad_right: 8
|
||
pad_top: 8
|
||
pad_bottom: 8
|
||
shadow_width: 0
|
||
shadow_opa: 40%
|
||
|
||
- id: style_tile_disabled
|
||
bg_color: 0x939391
|
||
bg_opa: 95%
|
||
radius: 18
|
||
border_width: 0
|
||
pad_left: 8
|
||
pad_right: 8
|
||
pad_top: 8
|
||
pad_bottom: 8
|
||
shadow_width: 0
|
||
shadow_opa: 25%
|
||
|
||
- id: style_icon_circle
|
||
bg_color: 0xFEC600
|
||
bg_opa: COVER
|
||
radius: 999
|
||
border_width: 0
|
||
|
||
- id: style_icon_circle_disabled
|
||
bg_color: 0x7B7B6F
|
||
bg_opa: 95%
|
||
radius: 999
|
||
border_width: 0
|
||
|
||
- id: style_room
|
||
text_font: headline
|
||
text_color: 0xFFFFFF
|
||
text_opa: 100%
|
||
|
||
- id: style_time
|
||
text_font: time_label
|
||
text_color: 0xFFFFFF
|
||
text_opa: 100%
|
||
|
||
- id: style_title
|
||
text_font: label
|
||
text_color: 0x000000
|
||
text_opa: 100%
|
||
|
||
- id: style_value
|
||
text_font: sublabel
|
||
text_color: 0x7A7A7C
|
||
text_opa: 100%
|
||
|
||
- id: style_value_big
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
text_opa: 80%
|
||
|
||
- id: style_title_disabled
|
||
text_font: label
|
||
text_color: 0xFFFFFF
|
||
text_opa: 100%
|
||
|
||
- id: style_value_disabled
|
||
text_font: sublabel
|
||
text_color: 0xD9D9D9
|
||
text_opa: 100%
|
||
|
||
pages:
|
||
- id: home_page
|
||
styles: style_bg_grad
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: lbl_room
|
||
x: 11
|
||
y: ${HEADER_Y}
|
||
text: "${ROOM_NAME}"
|
||
styles: style_room
|
||
|
||
- label:
|
||
id: lbl_time
|
||
align: TOP_RIGHT
|
||
x: -11
|
||
y: ${HEADER_Y}
|
||
text: ""
|
||
styles: style_time
|
||
|
||
# ---------- SCROLLBAAR TEGELRASTER ----------
|
||
# De pagina zelf scrollt niet: wallpaper-gradient, kamernaam en klok
|
||
# blijven staan. Alleen deze transparante container schuift.
|
||
#
|
||
# bg_opa: TRANSP is NIET hetzelfde als widget-`opa`: het maakt geen
|
||
# aparte renderlaag aan, dus het kost geen extra heap (zie de comment
|
||
# bij `theme:` hierboven).
|
||
#
|
||
# LVGL leidt de scroll-lengte zelf af uit de posities van de tegels;
|
||
# er is dus geen handmatige contenthoogte. Zodra een sleep de
|
||
# scroll-limiet (10 px) passeert annuleert LVGL de klik en de
|
||
# long-press, dus tikken en scrollen bijten elkaar niet.
|
||
- obj:
|
||
id: tile_scroll
|
||
x: 0
|
||
y: ${SCROLL_Y}
|
||
width: ${DISPLAY_W}
|
||
height: ${SCROLL_H}
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 8
|
||
scrollable: true
|
||
scroll_dir: VER
|
||
scrollbar_mode: 'OFF'
|
||
# Geen elastisch doorveren aan de randen: dat kost extra
|
||
# hertekeningen op een scherm dat toch al niet vloeiend scrollt.
|
||
scroll_elastic: false
|
||
scroll_momentum: true
|
||
# Klikt netjes per rij in. Verbergt de lage framerate een stuk
|
||
# beter dan vrij scrollen. Zet op NONE voor vrij scrollen.
|
||
scroll_snap_y: START
|
||
widgets:
|
||
# ---------- TILE 1 ----------
|
||
- obj:
|
||
id: tile1
|
||
x: ${GRID_COL1_X}
|
||
y: ${GRID_ROW1_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
# Directe lokale feedback: LVGL past dit toe zodra de vinger het
|
||
# glas raakt, in dezelfde frame. Onafhankelijk van Home Assistant.
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile1_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile1_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE1_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t1_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE1_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t1_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile1_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile1_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE1_TYPE}"
|
||
tap_action: "${TILE1_TAP_ACTION}"
|
||
service: "${TILE1_TAP_SERVICE}"
|
||
entity_id: "${TILE1_ENTITY}"
|
||
param_key: "${TILE1_TAP_PARAM_KEY}"
|
||
param_val: "${TILE1_TAP_PARAM_VAL}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE1_TYPE}") == "cover")
|
||
return id(tile1_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile1).state.c_str()) == "on";
|
||
on_long_press:
|
||
- script.execute: { id: tile1_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile1_long_press" }
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE1_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE1_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE1_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE1_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE1_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE1_LONGPRESS}\") == \"slider\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_value_overlay
|
||
title: "${TILE1_TITLE}"
|
||
entity: "${TILE1_ENTITY}"
|
||
tile_type: "${TILE1_TYPE}"
|
||
longpress_mode: "${TILE1_LONGPRESS_SLIDER}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE1_TYPE}") == "cover")
|
||
return id(tile1_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile1).state.c_str()) == "on";
|
||
off_value: !lambda "return atoi(\"${TILE1_LONGPRESS_OFF_VALUE}\");"
|
||
brightness_value: !lambda "return id(tile1_brightness).state;"
|
||
percentage_value: !lambda "return id(tile1_percentage).state;"
|
||
position_value: !lambda "return id(tile1_cover_position).state;"
|
||
icon_text: "${TILE1_ICON}"
|
||
slider_bg_color: ${TILE1_BG_DISABLED_COLOR}
|
||
slider_fill_color: ${TILE1_CIRCLE_ACTIVE_COLOR}
|
||
slider_icon_color: ${TILE1_ICON_ACTIVE_COLOR}
|
||
label_off: "${TILE1_LABEL_OFF}"
|
||
label_on: "${TILE1_LABEL_ON}"
|
||
supported_color_modes: !lambda "return std::string(id(tile1_supported_color_modes).state.c_str());"
|
||
climate_current_temperature: !lambda "return id(tile1_climate_current_temperature).state;"
|
||
climate_target_temperature: !lambda "return id(tile1_climate_target_temperature).state;"
|
||
climate_humidity: !lambda "return id(tile1_climate_humidity).state;"
|
||
climate_mode: !lambda "return std::string(id(tile1_climate_mode).state.c_str());"
|
||
climate_supported_modes: !lambda "return std::string(id(tile1_climate_supported_modes).state.c_str());"
|
||
climate_min_temp: "${TILE1_CLIMATE_MIN_TEMP}"
|
||
climate_max_temp: "${TILE1_CLIMATE_MAX_TEMP}"
|
||
climate_step: "${TILE1_CLIMATE_STEP}"
|
||
# Long-press modus "vacuum": opent de stofzuigerbediening.
|
||
# Valt buiten de action/slider-keten, dus die blijft ongewijzigd.
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE1_LONGPRESS}\") == \"vacuum\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_vacuum_overlay
|
||
title: "${TILE1_TITLE}"
|
||
entity: "${TILE1_ENTITY}"
|
||
|
||
# ---------- TILE 2 ----------
|
||
- obj:
|
||
id: tile2
|
||
x: ${GRID_COL2_X}
|
||
y: ${GRID_ROW1_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
# Directe lokale feedback: LVGL past dit toe zodra de vinger het
|
||
# glas raakt, in dezelfde frame. Onafhankelijk van Home Assistant.
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile2_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile2_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE2_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t2_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE2_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t2_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile2_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile2_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE2_TYPE}"
|
||
tap_action: "${TILE2_TAP_ACTION}"
|
||
service: "${TILE2_TAP_SERVICE}"
|
||
entity_id: "${TILE2_ENTITY}"
|
||
param_key: "${TILE2_TAP_PARAM_KEY}"
|
||
param_val: "${TILE2_TAP_PARAM_VAL}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE2_TYPE}") == "cover")
|
||
return id(tile2_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile2).state.c_str()) == "on";
|
||
on_long_press:
|
||
- script.execute: { id: tile2_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile2_long_press" }
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE2_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE2_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE2_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE2_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE2_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE2_LONGPRESS}\") == \"slider\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_value_overlay
|
||
title: "${TILE2_TITLE}"
|
||
entity: "${TILE2_ENTITY}"
|
||
tile_type: "${TILE2_TYPE}"
|
||
longpress_mode: "${TILE2_LONGPRESS_SLIDER}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE2_TYPE}") == "cover")
|
||
return id(tile2_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile2).state.c_str()) == "on";
|
||
off_value: !lambda "return atoi(\"${TILE2_LONGPRESS_OFF_VALUE}\");"
|
||
brightness_value: !lambda "return id(tile2_brightness).state;"
|
||
percentage_value: !lambda "return id(tile2_percentage).state;"
|
||
position_value: !lambda "return id(tile2_cover_position).state;"
|
||
icon_text: "${TILE2_ICON}"
|
||
slider_bg_color: ${TILE2_BG_DISABLED_COLOR}
|
||
slider_fill_color: ${TILE2_CIRCLE_ACTIVE_COLOR}
|
||
slider_icon_color: ${TILE2_ICON_ACTIVE_COLOR}
|
||
label_off: "${TILE2_LABEL_OFF}"
|
||
label_on: "${TILE2_LABEL_ON}"
|
||
supported_color_modes: !lambda "return std::string(id(tile2_supported_color_modes).state.c_str());"
|
||
climate_current_temperature: !lambda "return id(tile2_climate_current_temperature).state;"
|
||
climate_target_temperature: !lambda "return id(tile2_climate_target_temperature).state;"
|
||
climate_humidity: !lambda "return id(tile2_climate_humidity).state;"
|
||
climate_mode: !lambda "return std::string(id(tile2_climate_mode).state.c_str());"
|
||
climate_supported_modes: !lambda "return std::string(id(tile2_climate_supported_modes).state.c_str());"
|
||
climate_min_temp: "${TILE2_CLIMATE_MIN_TEMP}"
|
||
climate_max_temp: "${TILE2_CLIMATE_MAX_TEMP}"
|
||
climate_step: "${TILE2_CLIMATE_STEP}"
|
||
# Long-press modus "vacuum": opent de stofzuigerbediening.
|
||
# Valt buiten de action/slider-keten, dus die blijft ongewijzigd.
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE2_LONGPRESS}\") == \"vacuum\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_vacuum_overlay
|
||
title: "${TILE2_TITLE}"
|
||
entity: "${TILE2_ENTITY}"
|
||
|
||
# ---------- TILE 3 ----------
|
||
- obj:
|
||
id: tile3
|
||
x: ${GRID_COL1_X}
|
||
y: ${GRID_ROW2_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
# Directe lokale feedback: LVGL past dit toe zodra de vinger het
|
||
# glas raakt, in dezelfde frame. Onafhankelijk van Home Assistant.
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile3_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile3_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE3_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t3_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE3_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t3_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile3_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile3_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE3_TYPE}"
|
||
tap_action: "${TILE3_TAP_ACTION}"
|
||
service: "${TILE3_TAP_SERVICE}"
|
||
entity_id: "${TILE3_ENTITY}"
|
||
param_key: "${TILE3_TAP_PARAM_KEY}"
|
||
param_val: "${TILE3_TAP_PARAM_VAL}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE3_TYPE}") == "cover")
|
||
return id(tile3_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile3).state.c_str()) == "on";
|
||
on_long_press:
|
||
- script.execute: { id: tile3_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile3_long_press" }
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE3_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE3_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE3_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE3_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE3_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE3_LONGPRESS}\") == \"slider\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_value_overlay
|
||
title: "${TILE3_TITLE}"
|
||
entity: "${TILE3_ENTITY}"
|
||
tile_type: "${TILE3_TYPE}"
|
||
longpress_mode: "${TILE3_LONGPRESS_SLIDER}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE3_TYPE}") == "cover")
|
||
return id(tile3_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile3).state.c_str()) == "on";
|
||
off_value: !lambda "return atoi(\"${TILE3_LONGPRESS_OFF_VALUE}\");"
|
||
brightness_value: !lambda "return id(tile3_brightness).state;"
|
||
percentage_value: !lambda "return id(tile3_percentage).state;"
|
||
position_value: !lambda "return id(tile3_cover_position).state;"
|
||
icon_text: "${TILE3_ICON}"
|
||
slider_bg_color: ${TILE3_BG_DISABLED_COLOR}
|
||
slider_fill_color: ${TILE3_CIRCLE_ACTIVE_COLOR}
|
||
slider_icon_color: ${TILE3_ICON_ACTIVE_COLOR}
|
||
label_off: "${TILE3_LABEL_OFF}"
|
||
label_on: "${TILE3_LABEL_ON}"
|
||
supported_color_modes: !lambda "return std::string(id(tile3_supported_color_modes).state.c_str());"
|
||
climate_current_temperature: !lambda "return id(tile3_climate_current_temperature).state;"
|
||
climate_target_temperature: !lambda "return id(tile3_climate_target_temperature).state;"
|
||
climate_humidity: !lambda "return id(tile3_climate_humidity).state;"
|
||
climate_mode: !lambda "return std::string(id(tile3_climate_mode).state.c_str());"
|
||
climate_supported_modes: !lambda "return std::string(id(tile3_climate_supported_modes).state.c_str());"
|
||
climate_min_temp: "${TILE3_CLIMATE_MIN_TEMP}"
|
||
climate_max_temp: "${TILE3_CLIMATE_MAX_TEMP}"
|
||
climate_step: "${TILE3_CLIMATE_STEP}"
|
||
# Long-press modus "vacuum": opent de stofzuigerbediening.
|
||
# Valt buiten de action/slider-keten, dus die blijft ongewijzigd.
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE3_LONGPRESS}\") == \"vacuum\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_vacuum_overlay
|
||
title: "${TILE3_TITLE}"
|
||
entity: "${TILE3_ENTITY}"
|
||
|
||
# ---------- TILE 4 ----------
|
||
- obj:
|
||
id: tile4
|
||
x: ${GRID_COL2_X}
|
||
y: ${GRID_ROW2_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
# Directe lokale feedback: LVGL past dit toe zodra de vinger het
|
||
# glas raakt, in dezelfde frame. Onafhankelijk van Home Assistant.
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile4_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile4_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE4_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t4_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE4_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t4_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile4_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile4_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE4_TYPE}"
|
||
tap_action: "${TILE4_TAP_ACTION}"
|
||
service: "${TILE4_TAP_SERVICE}"
|
||
entity_id: "${TILE4_ENTITY}"
|
||
param_key: "${TILE4_TAP_PARAM_KEY}"
|
||
param_val: "${TILE4_TAP_PARAM_VAL}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE4_TYPE}") == "cover")
|
||
return id(tile4_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile4).state.c_str()) == "on";
|
||
on_long_press:
|
||
- script.execute: { id: tile4_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile4_long_press" }
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE4_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE4_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE4_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE4_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE4_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE4_LONGPRESS}\") == \"slider\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_value_overlay
|
||
title: "${TILE4_TITLE}"
|
||
entity: "${TILE4_ENTITY}"
|
||
tile_type: "${TILE4_TYPE}"
|
||
longpress_mode: "${TILE4_LONGPRESS_SLIDER}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE4_TYPE}") == "cover")
|
||
return id(tile4_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile4).state.c_str()) == "on";
|
||
off_value: !lambda "return atoi(\"${TILE4_LONGPRESS_OFF_VALUE}\");"
|
||
brightness_value: !lambda "return id(tile4_brightness).state;"
|
||
percentage_value: !lambda "return id(tile4_percentage).state;"
|
||
position_value: !lambda "return id(tile4_cover_position).state;"
|
||
icon_text: "${TILE4_ICON}"
|
||
slider_bg_color: ${TILE4_BG_DISABLED_COLOR}
|
||
slider_fill_color: ${TILE4_CIRCLE_ACTIVE_COLOR}
|
||
slider_icon_color: ${TILE4_ICON_ACTIVE_COLOR}
|
||
label_off: "${TILE4_LABEL_OFF}"
|
||
label_on: "${TILE4_LABEL_ON}"
|
||
supported_color_modes: !lambda "return std::string(id(tile4_supported_color_modes).state.c_str());"
|
||
climate_current_temperature: !lambda "return id(tile4_climate_current_temperature).state;"
|
||
climate_target_temperature: !lambda "return id(tile4_climate_target_temperature).state;"
|
||
climate_humidity: !lambda "return id(tile4_climate_humidity).state;"
|
||
climate_mode: !lambda "return std::string(id(tile4_climate_mode).state.c_str());"
|
||
climate_supported_modes: !lambda "return std::string(id(tile4_climate_supported_modes).state.c_str());"
|
||
climate_min_temp: "${TILE4_CLIMATE_MIN_TEMP}"
|
||
climate_max_temp: "${TILE4_CLIMATE_MAX_TEMP}"
|
||
climate_step: "${TILE4_CLIMATE_STEP}"
|
||
# Long-press modus "vacuum": opent de stofzuigerbediening.
|
||
# Valt buiten de action/slider-keten, dus die blijft ongewijzigd.
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE4_LONGPRESS}\") == \"vacuum\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_vacuum_overlay
|
||
title: "${TILE4_TITLE}"
|
||
entity: "${TILE4_ENTITY}"
|
||
|
||
# ---------- TILE 5 ----------
|
||
- obj:
|
||
id: tile5
|
||
x: ${GRID_COL1_X}
|
||
y: ${GRID_ROW3_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
# Directe lokale feedback: LVGL past dit toe zodra de vinger het
|
||
# glas raakt, in dezelfde frame. Onafhankelijk van Home Assistant.
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile5_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile5_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE5_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t5_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE5_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t5_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile5_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile5_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE5_TYPE}"
|
||
tap_action: "${TILE5_TAP_ACTION}"
|
||
service: "${TILE5_TAP_SERVICE}"
|
||
entity_id: "${TILE5_ENTITY}"
|
||
param_key: "${TILE5_TAP_PARAM_KEY}"
|
||
param_val: "${TILE5_TAP_PARAM_VAL}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE5_TYPE}") == "cover")
|
||
return id(tile5_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile5).state.c_str()) == "on";
|
||
on_long_press:
|
||
- script.execute: { id: tile5_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile5_long_press" }
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE5_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE5_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE5_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE5_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE5_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE5_LONGPRESS}\") == \"slider\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_value_overlay
|
||
title: "${TILE5_TITLE}"
|
||
entity: "${TILE5_ENTITY}"
|
||
tile_type: "${TILE5_TYPE}"
|
||
longpress_mode: "${TILE5_LONGPRESS_SLIDER}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE5_TYPE}") == "cover")
|
||
return id(tile5_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile5).state.c_str()) == "on";
|
||
off_value: !lambda "return atoi(\"${TILE5_LONGPRESS_OFF_VALUE}\");"
|
||
brightness_value: !lambda "return id(tile5_brightness).state;"
|
||
percentage_value: !lambda "return id(tile5_percentage).state;"
|
||
position_value: !lambda "return id(tile5_cover_position).state;"
|
||
icon_text: "${TILE5_ICON}"
|
||
slider_bg_color: ${TILE5_BG_DISABLED_COLOR}
|
||
slider_fill_color: ${TILE5_CIRCLE_ACTIVE_COLOR}
|
||
slider_icon_color: ${TILE5_ICON_ACTIVE_COLOR}
|
||
label_off: "${TILE5_LABEL_OFF}"
|
||
label_on: "${TILE5_LABEL_ON}"
|
||
supported_color_modes: !lambda "return std::string(id(tile5_supported_color_modes).state.c_str());"
|
||
climate_current_temperature: !lambda "return id(tile5_climate_current_temperature).state;"
|
||
climate_target_temperature: !lambda "return id(tile5_climate_target_temperature).state;"
|
||
climate_humidity: !lambda "return id(tile5_climate_humidity).state;"
|
||
climate_mode: !lambda "return std::string(id(tile5_climate_mode).state.c_str());"
|
||
climate_supported_modes: !lambda "return std::string(id(tile5_climate_supported_modes).state.c_str());"
|
||
climate_min_temp: "${TILE5_CLIMATE_MIN_TEMP}"
|
||
climate_max_temp: "${TILE5_CLIMATE_MAX_TEMP}"
|
||
climate_step: "${TILE5_CLIMATE_STEP}"
|
||
# Long-press modus "vacuum": opent de stofzuigerbediening.
|
||
# Valt buiten de action/slider-keten, dus die blijft ongewijzigd.
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE5_LONGPRESS}\") == \"vacuum\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_vacuum_overlay
|
||
title: "${TILE5_TITLE}"
|
||
entity: "${TILE5_ENTITY}"
|
||
|
||
# ---------- TILE 6 ----------
|
||
- obj:
|
||
id: tile6
|
||
x: ${GRID_COL2_X}
|
||
y: ${GRID_ROW3_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
# Directe lokale feedback: LVGL past dit toe zodra de vinger het
|
||
# glas raakt, in dezelfde frame. Onafhankelijk van Home Assistant.
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile6_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile6_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE6_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t6_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE6_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t6_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile6_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile6_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE6_TYPE}"
|
||
tap_action: "${TILE6_TAP_ACTION}"
|
||
service: "${TILE6_TAP_SERVICE}"
|
||
entity_id: "${TILE6_ENTITY}"
|
||
param_key: "${TILE6_TAP_PARAM_KEY}"
|
||
param_val: "${TILE6_TAP_PARAM_VAL}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE6_TYPE}") == "cover")
|
||
return id(tile6_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile6).state.c_str()) == "on";
|
||
on_long_press:
|
||
- script.execute: { id: tile6_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile6_long_press" }
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE6_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE6_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE6_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE6_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE6_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE6_LONGPRESS}\") == \"slider\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_value_overlay
|
||
title: "${TILE6_TITLE}"
|
||
entity: "${TILE6_ENTITY}"
|
||
tile_type: "${TILE6_TYPE}"
|
||
longpress_mode: "${TILE6_LONGPRESS_SLIDER}"
|
||
was_on: !lambda |-
|
||
if (std::string("${TILE6_TYPE}") == "cover")
|
||
return id(tile6_cover_state).state != "closed";
|
||
return std::string(id(ha_state_tile6).state.c_str()) == "on";
|
||
off_value: !lambda "return atoi(\"${TILE6_LONGPRESS_OFF_VALUE}\");"
|
||
brightness_value: !lambda "return id(tile6_brightness).state;"
|
||
percentage_value: !lambda "return id(tile6_percentage).state;"
|
||
position_value: !lambda "return id(tile6_cover_position).state;"
|
||
icon_text: "${TILE6_ICON}"
|
||
slider_bg_color: ${TILE6_BG_DISABLED_COLOR}
|
||
slider_fill_color: ${TILE6_CIRCLE_ACTIVE_COLOR}
|
||
slider_icon_color: ${TILE6_ICON_ACTIVE_COLOR}
|
||
label_off: "${TILE6_LABEL_OFF}"
|
||
label_on: "${TILE6_LABEL_ON}"
|
||
supported_color_modes: !lambda "return std::string(id(tile6_supported_color_modes).state.c_str());"
|
||
climate_current_temperature: !lambda "return id(tile6_climate_current_temperature).state;"
|
||
climate_target_temperature: !lambda "return id(tile6_climate_target_temperature).state;"
|
||
climate_humidity: !lambda "return id(tile6_climate_humidity).state;"
|
||
climate_mode: !lambda "return std::string(id(tile6_climate_mode).state.c_str());"
|
||
climate_supported_modes: !lambda "return std::string(id(tile6_climate_supported_modes).state.c_str());"
|
||
climate_min_temp: "${TILE6_CLIMATE_MIN_TEMP}"
|
||
climate_max_temp: "${TILE6_CLIMATE_MAX_TEMP}"
|
||
climate_step: "${TILE6_CLIMATE_STEP}"
|
||
# Long-press modus "vacuum": opent de stofzuigerbediening.
|
||
# Valt buiten de action/slider-keten, dus die blijft ongewijzigd.
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE6_LONGPRESS}\") == \"vacuum\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_vacuum_overlay
|
||
title: "${TILE6_TITLE}"
|
||
entity: "${TILE6_ENTITY}"
|
||
|
||
# ---------- TILE 7 — Tafellamp ----------
|
||
# Alleen-helderheid lamp: er zijn bewust geen percentage/cover/
|
||
# climate-sensors voor deze tegel aangemaakt, die zouden alleen
|
||
# maar HA-subscriptions en heap kosten. De overlay krijgt daarom
|
||
# NAN / "" voor de velden die een light nooit gebruikt.
|
||
- obj:
|
||
id: tile7
|
||
x: ${GRID_COL1_X}
|
||
y: ${GRID_ROW4_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile7_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile7_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE7_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t7_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE7_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t7_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile7_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile7_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE7_TYPE}"
|
||
tap_action: "${TILE7_TAP_ACTION}"
|
||
service: "${TILE7_TAP_SERVICE}"
|
||
entity_id: "${TILE7_ENTITY}"
|
||
param_key: "${TILE7_TAP_PARAM_KEY}"
|
||
param_val: "${TILE7_TAP_PARAM_VAL}"
|
||
was_on: !lambda "return std::string(id(ha_state_tile7).state.c_str()) == \"on\";"
|
||
on_long_press:
|
||
- script.execute: { id: tile7_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile7_long_press" }
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE7_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE7_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE7_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE7_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE7_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE7_LONGPRESS}\") == \"slider\";"
|
||
then:
|
||
- script.execute:
|
||
id: open_value_overlay
|
||
title: "${TILE7_TITLE}"
|
||
entity: "${TILE7_ENTITY}"
|
||
tile_type: "${TILE7_TYPE}"
|
||
longpress_mode: "${TILE7_LONGPRESS_SLIDER}"
|
||
was_on: !lambda "return std::string(id(ha_state_tile7).state.c_str()) == \"on\";"
|
||
off_value: !lambda "return atoi(\"${TILE7_LONGPRESS_OFF_VALUE}\");"
|
||
brightness_value: !lambda "return id(tile7_brightness).state;"
|
||
percentage_value: !lambda "return NAN;"
|
||
position_value: !lambda "return NAN;"
|
||
icon_text: "${TILE7_ICON}"
|
||
slider_bg_color: ${TILE7_BG_DISABLED_COLOR}
|
||
slider_fill_color: ${TILE7_CIRCLE_ACTIVE_COLOR}
|
||
slider_icon_color: ${TILE7_ICON_ACTIVE_COLOR}
|
||
label_off: "${TILE7_LABEL_OFF}"
|
||
label_on: "${TILE7_LABEL_ON}"
|
||
supported_color_modes: !lambda "return std::string(id(tile7_supported_color_modes).state.c_str());"
|
||
climate_current_temperature: !lambda "return NAN;"
|
||
climate_target_temperature: !lambda "return NAN;"
|
||
climate_humidity: !lambda "return NAN;"
|
||
climate_mode: ""
|
||
climate_supported_modes: ""
|
||
climate_min_temp: "5.0"
|
||
climate_max_temp: "30.0"
|
||
climate_step: "0.5"
|
||
|
||
# ---------- TILE 8 — Brievenbus reset ----------
|
||
# input_select met opties Vol/Leeg. De tegel licht op zodra de
|
||
# status TILE8_ACTIVE_STATE ("Vol") is; een korte tik stuurt
|
||
# input_select.select_option met option: "Leeg".
|
||
# Geen slider-modus, dus deze tegel heeft alleen ha_state_tile8
|
||
# nodig — verder geen enkele HA-subscription.
|
||
- obj:
|
||
id: tile8
|
||
x: ${GRID_COL2_X}
|
||
y: ${GRID_ROW4_Y}
|
||
width: ${TILE_W}
|
||
height: ${TILE_H}
|
||
styles: style_tile
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pressed:
|
||
bg_opa: 45%
|
||
widgets:
|
||
- obj:
|
||
id: tile8_icon_circle
|
||
x: ${TILE_ICON_X}
|
||
y: ${TILE_ICON_Y}
|
||
width: 36
|
||
height: 36
|
||
styles: style_icon_circle
|
||
clickable: false
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: tile8_icon_lbl
|
||
align: CENTER
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE8_ICON}"
|
||
clickable: false
|
||
- label:
|
||
id: t8_title
|
||
x: ${TILE_TITLE_X}
|
||
y: ${TILE_TITLE_Y}
|
||
text: "${TILE8_TITLE}"
|
||
styles: style_title
|
||
clickable: false
|
||
- label:
|
||
id: t8_value
|
||
x: ${TILE_VALUE_X}
|
||
y: ${TILE_VALUE_Y}
|
||
text: "—"
|
||
styles: style_value
|
||
clickable: false
|
||
on_short_click:
|
||
- script.execute: { id: tile8_ack, is_long: false }
|
||
- script.execute: { id: publish_action, action: "tile8_press" }
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: "${TILE8_TYPE}"
|
||
tap_action: "${TILE8_TAP_ACTION}"
|
||
service: "${TILE8_TAP_SERVICE}"
|
||
entity_id: "${TILE8_ENTITY}"
|
||
param_key: "${TILE8_TAP_PARAM_KEY}"
|
||
param_val: "${TILE8_TAP_PARAM_VAL}"
|
||
was_on: !lambda "return std::string(id(ha_state_tile8).state.c_str()) == \"${TILE8_ACTIVE_STATE}\";"
|
||
on_long_press:
|
||
- script.execute: { id: tile8_ack, is_long: true }
|
||
- script.execute: { id: publish_action, action: "tile8_long_press" }
|
||
# Slider-modus kan hier niet zonder eerst de bijbehorende
|
||
# sensors aan te maken; "action" werkt wel out of the box.
|
||
- if:
|
||
condition:
|
||
lambda: "return std::string(\"${TILE8_LONGPRESS}\") == \"action\";"
|
||
then:
|
||
- script.execute:
|
||
id: do_tile_action
|
||
tile_type: !lambda |-
|
||
std::string t = "${TILE8_LONGPRESS_ACTION_TYPE}";
|
||
return t.empty() ? std::string("${TILE8_TYPE}") : t;
|
||
tap_action: "activate"
|
||
service: "${TILE8_LONGPRESS_ACTION_SERVICE}"
|
||
entity_id: "${TILE8_LONGPRESS_ACTION}"
|
||
param_key: ""
|
||
param_val: ""
|
||
was_on: false
|
||
|
||
# ---------- VALUE SLIDER OVERLAY ----------
|
||
- obj:
|
||
id: brightness_overlay
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
bg_opa: TRANSP
|
||
hidden: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
border_width: 0
|
||
widgets:
|
||
- obj:
|
||
id: brightness_overlay_bg
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
styles: style_bg_grad
|
||
scrollable: false
|
||
clickable: true
|
||
on_click:
|
||
- lvgl.widget.hide: brightness_overlay
|
||
|
||
- obj:
|
||
id: brightness_overlay_dim
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
bg_color: 0x000000
|
||
bg_opa: 18%
|
||
border_width: 0
|
||
clickable: false
|
||
scrollable: false
|
||
|
||
- label:
|
||
id: overlay_title
|
||
align: TOP_MID
|
||
y: 10
|
||
text: "Light"
|
||
styles: style_room
|
||
|
||
- label:
|
||
id: overlay_value
|
||
align: TOP_MID
|
||
y: 35
|
||
text: "100 %"
|
||
styles: style_value_big
|
||
|
||
- slider:
|
||
id: overlay_slider
|
||
align: CENTER
|
||
y: 18
|
||
width: ${OVERLAY_SLIDER_W}
|
||
height: ${OVERLAY_SLIDER_H}
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 100
|
||
adv_hittest: false
|
||
|
||
bg_color: 0x939391
|
||
bg_opa: 100%
|
||
radius: 28
|
||
# Enige widget met ZOWEL radius ALS een kind (overlay_icon).
|
||
# LVGL snijdt kinderen dan bij op de ronding, en dat kan
|
||
# alleen via een ARGB-laag van 96x160 = 46KB aaneengesloten.
|
||
# Lukt die allocatie niet, dan blijft LVGL het proberen tot
|
||
# de task watchdog het bord herstart. Ronding blijft, het
|
||
# bijsnijden gaat eraf.
|
||
clip_corner: false
|
||
border_width: 0
|
||
|
||
indicator:
|
||
bg_color: 0xFEC600
|
||
bg_opa: 100%
|
||
radius: 28
|
||
|
||
knob:
|
||
bg_opa: 0%
|
||
border_width: 0
|
||
shadow_width: 0
|
||
pad_all: 10
|
||
|
||
on_value:
|
||
- script.execute:
|
||
id: slider_preview
|
||
value: !lambda "return x;"
|
||
|
||
on_release:
|
||
- script.execute:
|
||
id: slider_commit
|
||
value: !lambda "return lv_slider_get_value(id(overlay_slider));"
|
||
|
||
|
||
# Het icoon zat als KIND in de slider. Een widget met een radius
|
||
# en kinderen moet die kinderen bijsnijden op de ronding, en dat
|
||
# kan LVGL alleen via een ARGB-laag van tientallen KB's. Zonder
|
||
# PSRAM mislukt dat soms, waarna LVGL blijft hertesten tot de
|
||
# watchdog het bord herstart. Als broer eroverheen is er geen
|
||
# laag nodig en ziet het er identiek uit.
|
||
- label:
|
||
id: overlay_icon
|
||
align: CENTER
|
||
y: ${OVERLAY_ICON_Y}
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
text: "${TILE1_ICON}"
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: overlay_color_button
|
||
align: BOTTOM_RIGHT
|
||
x: -13
|
||
y: -13
|
||
width: 44
|
||
height: 44
|
||
radius: 999
|
||
hidden: true
|
||
clickable: true
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
on_click:
|
||
- script.execute: open_color_detail
|
||
widgets:
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 0
|
||
end_angle: 45
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0xFF3B30
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 45
|
||
end_angle: 90
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0xFF9500
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 90
|
||
end_angle: 135
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0xFFD60A
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 135
|
||
end_angle: 180
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0x30D158
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 180
|
||
end_angle: 225
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0x32D6FF
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 225
|
||
end_angle: 270
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0x0A84FF
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 270
|
||
end_angle: 315
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0x5856D6
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
- arc:
|
||
align: CENTER
|
||
width: 40
|
||
height: 40
|
||
start_angle: 315
|
||
end_angle: 360
|
||
min_value: 0
|
||
max_value: 100
|
||
value: 0
|
||
adjustable: false
|
||
arc_width: 10
|
||
arc_rounded: false
|
||
arc_color: 0xBF5AF2
|
||
arc_opa: COVER
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: false
|
||
indicator:
|
||
arc_opa: TRANSP
|
||
knob:
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
|
||
# ---------- LIGHT COLOR DETAIL OVERLAY ----------
|
||
- obj:
|
||
id: color_detail_overlay
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
bg_color: 0x171719
|
||
bg_opa: COVER
|
||
hidden: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
border_width: 0
|
||
widgets:
|
||
- obj:
|
||
id: color_detail_bg
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
styles: style_bg_grad
|
||
scrollable: false
|
||
clickable: false
|
||
- obj:
|
||
id: color_detail_scrim
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
bg_color: 0x000000
|
||
bg_opa: 72%
|
||
border_width: 0
|
||
clickable: false
|
||
scrollable: false
|
||
|
||
- obj:
|
||
id: color_close_button
|
||
align: TOP_LEFT
|
||
x: 10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0x3C3C40
|
||
bg_opa: COVER
|
||
border_width: 1
|
||
border_color: 0xFFFFFF
|
||
border_opa: 20%
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: color_detail_overlay
|
||
- lvgl.widget.show: brightness_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "x"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- label:
|
||
id: color_detail_title
|
||
align: TOP_MID
|
||
y: 16
|
||
text: "Light Color"
|
||
styles: style_room
|
||
|
||
- obj:
|
||
id: color_done_button
|
||
align: TOP_RIGHT
|
||
x: -10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0xFF9F0A
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: color_detail_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "\U000F012C"
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: color_mode_tabs
|
||
align: TOP_MID
|
||
y: 58
|
||
width: 190
|
||
height: 28
|
||
radius: 14
|
||
bg_color: 0x3C3C40
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
scrollable: false
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
widgets:
|
||
- obj:
|
||
id: color_tab_indicator
|
||
x: 2
|
||
y: 2
|
||
width: 93
|
||
height: 24
|
||
radius: 12
|
||
bg_color: 0x8E8E93
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: false
|
||
scrollable: false
|
||
- obj:
|
||
x: 0
|
||
y: 0
|
||
width: 95
|
||
height: 28
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
on_click:
|
||
- script.execute:
|
||
id: show_color_detail_tab
|
||
tab: 0
|
||
widgets:
|
||
- label:
|
||
id: color_tab_label
|
||
align: CENTER
|
||
text: "Color"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
x: 95
|
||
y: 0
|
||
width: 95
|
||
height: 28
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
on_click:
|
||
- script.execute:
|
||
id: show_color_detail_tab
|
||
tab: 1
|
||
widgets:
|
||
- label:
|
||
id: temp_tab_label
|
||
align: CENTER
|
||
text: "Temp."
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: color_palette_card
|
||
align: TOP_MID
|
||
y: 98
|
||
width: 286
|
||
height: 48
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
scrollable: false
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
widgets:
|
||
- obj:
|
||
id: color_preset_red
|
||
x: 14
|
||
y: 8
|
||
width: 30
|
||
height: 30
|
||
radius: 999
|
||
bg_color: 0xFF3B30
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
on_click:
|
||
- script.execute: { id: set_light_color_name, color: "red" }
|
||
- obj:
|
||
id: color_preset_orange
|
||
x: 61
|
||
y: 8
|
||
width: 30
|
||
height: 30
|
||
radius: 999
|
||
bg_color: 0xFF9F0A
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
on_click:
|
||
- script.execute: { id: set_light_color_name, color: "orange" }
|
||
- obj:
|
||
id: color_preset_yellow
|
||
x: 108
|
||
y: 8
|
||
width: 30
|
||
height: 30
|
||
radius: 999
|
||
bg_color: 0xFFD60A
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
on_click:
|
||
- script.execute: { id: set_light_color_name, color: "yellow" }
|
||
- obj:
|
||
id: color_preset_green
|
||
x: 155
|
||
y: 8
|
||
width: 30
|
||
height: 30
|
||
radius: 999
|
||
bg_color: 0x30D158
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
on_click:
|
||
- script.execute: { id: set_light_color_name, color: "green" }
|
||
- obj:
|
||
id: color_preset_blue
|
||
x: 202
|
||
y: 8
|
||
width: 30
|
||
height: 30
|
||
radius: 999
|
||
bg_color: 0x0A84FF
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
on_click:
|
||
- script.execute: { id: set_light_color_name, color: "blue" }
|
||
- obj:
|
||
id: color_preset_purple
|
||
x: 249
|
||
y: 8
|
||
width: 30
|
||
height: 30
|
||
radius: 999
|
||
bg_color: 0xBF5AF2
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
on_click:
|
||
- script.execute: { id: set_light_color_name, color: "purple" }
|
||
|
||
- obj:
|
||
id: temp_palette_card
|
||
align: TOP_MID
|
||
y: 98
|
||
width: 286
|
||
height: 48
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
scrollable: false
|
||
hidden: true
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
widgets:
|
||
- obj:
|
||
id: temp_preset_2700
|
||
x: 24
|
||
y: 8
|
||
width: 62
|
||
height: 30
|
||
radius: 15
|
||
bg_color: 0xFFD4A3
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
on_click:
|
||
- script.execute: { id: set_light_color_temperature, kelvin: 2700 }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "2700K"
|
||
text_font: sublabel_big
|
||
text_color: 0x3A2A18
|
||
clickable: false
|
||
- obj:
|
||
id: temp_preset_4000
|
||
x: 112
|
||
y: 8
|
||
width: 62
|
||
height: 30
|
||
radius: 15
|
||
bg_color: 0xFFE8C8
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
on_click:
|
||
- script.execute: { id: set_light_color_temperature, kelvin: 4000 }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "4000K"
|
||
text_font: sublabel_big
|
||
text_color: 0x3A2A18
|
||
clickable: false
|
||
- obj:
|
||
id: temp_preset_6500
|
||
x: 200
|
||
y: 8
|
||
width: 62
|
||
height: 30
|
||
radius: 15
|
||
bg_color: 0xEAF4FF
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
on_click:
|
||
- script.execute: { id: set_light_color_temperature, kelvin: 6500 }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "6500K"
|
||
text_font: sublabel_big
|
||
text_color: 0x1E2A36
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: color_brightness_card
|
||
align: TOP_MID
|
||
y: 165
|
||
width: 188
|
||
height: 46
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
scrollable: false
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
widgets:
|
||
- label:
|
||
x: 13
|
||
y: 6
|
||
text: "Brightness"
|
||
text_font: sublabel
|
||
text_color: 0xD1D1D6
|
||
hidden: true
|
||
clickable: false
|
||
- slider:
|
||
id: color_brightness_slider
|
||
x: 0
|
||
y: 0
|
||
width: 188
|
||
height: 46
|
||
min_value: 1
|
||
max_value: 100
|
||
value: 100
|
||
bg_color: 0x939391
|
||
bg_opa: COVER
|
||
radius: 23
|
||
border_width: 0
|
||
indicator:
|
||
bg_color: 0xFEC600
|
||
bg_opa: COVER
|
||
radius: 23
|
||
knob:
|
||
bg_opa: 0%
|
||
border_width: 0
|
||
shadow_width: 0
|
||
pad_all: 10
|
||
on_value:
|
||
- script.execute:
|
||
id: color_brightness_preview
|
||
value: !lambda "return x;"
|
||
on_release:
|
||
- script.execute:
|
||
id: color_brightness_commit
|
||
value: !lambda "return lv_slider_get_value(id(color_brightness_slider));"
|
||
- label:
|
||
id: color_brightness_value
|
||
align: RIGHT_MID
|
||
x: -13
|
||
text: "100 %"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
hidden: true
|
||
clickable: false
|
||
|
||
# ---------- CLIMATE DETAIL OVERLAY ----------
|
||
- obj:
|
||
id: climate_detail_overlay
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
bg_color: 0x171719
|
||
bg_opa: COVER
|
||
hidden: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
pad_left: 0
|
||
pad_right: 0
|
||
pad_top: 0
|
||
pad_bottom: 0
|
||
border_width: 0
|
||
widgets:
|
||
- obj:
|
||
id: climate_detail_bg
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
styles: style_bg_grad
|
||
scrollable: false
|
||
clickable: false
|
||
- obj:
|
||
id: climate_detail_scrim
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
bg_color: 0x000000
|
||
bg_opa: 62%
|
||
border_width: 0
|
||
clickable: false
|
||
scrollable: false
|
||
|
||
- obj:
|
||
id: climate_close_button
|
||
align: TOP_LEFT
|
||
x: 10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0x3C3C40
|
||
bg_opa: COVER
|
||
border_width: 1
|
||
border_color: 0xFFFFFF
|
||
border_opa: 20%
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: climate_detail_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "x"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: climate_done_button
|
||
align: TOP_RIGHT
|
||
x: -10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0xFEC600
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: climate_detail_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "\U000F012C"
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- label:
|
||
id: climate_detail_title
|
||
align: TOP_MID
|
||
y: 16
|
||
text: "Climate"
|
||
styles: style_room
|
||
|
||
- label:
|
||
id: climate_current_label
|
||
align: TOP_MID
|
||
y: 42
|
||
text: "Current: --"
|
||
text_font: sublabel_big
|
||
text_color: 0xD1E8FF
|
||
text_opa: 85%
|
||
clickable: false
|
||
|
||
- arc:
|
||
id: climate_arc
|
||
align: CENTER
|
||
y: 20
|
||
width: 184
|
||
height: 184
|
||
start_angle: 135
|
||
end_angle: 45
|
||
min_value: 50
|
||
max_value: 300
|
||
value: 200
|
||
adjustable: true
|
||
arc_width: 20
|
||
arc_rounded: true
|
||
arc_color: 0x4B4B4F
|
||
arc_opa: 80%
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
indicator:
|
||
arc_color: 0xFEC600
|
||
arc_opa: COVER
|
||
arc_width: 20
|
||
arc_rounded: true
|
||
knob:
|
||
bg_color: 0xFFFFFF
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
shadow_width: 0
|
||
on_value:
|
||
- script.execute:
|
||
id: climate_target_preview
|
||
value: !lambda "return x;"
|
||
on_release:
|
||
- script.execute:
|
||
id: climate_target_commit
|
||
value: !lambda "return lv_arc_get_value(id(climate_arc));"
|
||
|
||
- label:
|
||
id: climate_set_label
|
||
align: CENTER
|
||
y: -16
|
||
text: "SET TO"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
text_opa: 68%
|
||
clickable: false
|
||
|
||
- label:
|
||
id: climate_target_label
|
||
align: CENTER
|
||
y: 20
|
||
text: "20.0°"
|
||
text_font: headline
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: climate_minus_button
|
||
align: BOTTOM_LEFT
|
||
x: 38
|
||
y: -16
|
||
width: 48
|
||
height: 36
|
||
radius: 18
|
||
bg_color: 0x3C3C40
|
||
bg_opa: COVER
|
||
border_width: 1
|
||
border_color: 0xFFFFFF
|
||
border_opa: 16%
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- script.execute:
|
||
id: climate_adjust_target
|
||
direction: -1
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "-"
|
||
text_font: headline
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: climate_plus_button
|
||
align: BOTTOM_RIGHT
|
||
x: -38
|
||
y: -16
|
||
width: 48
|
||
height: 36
|
||
radius: 18
|
||
bg_color: 0x3C3C40
|
||
bg_opa: COVER
|
||
border_width: 1
|
||
border_color: 0xFFFFFF
|
||
border_opa: 16%
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- script.execute:
|
||
id: climate_adjust_target
|
||
direction: 1
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "+"
|
||
text_font: headline
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: climate_mode_pill
|
||
align: BOTTOM_MID
|
||
y: -18
|
||
width: 126
|
||
height: 34
|
||
radius: 17
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 82%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- script.execute: open_climate_mode_picker
|
||
widgets:
|
||
- label:
|
||
id: climate_mode_label
|
||
align: CENTER
|
||
text: "Auto"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
# ---------- CLIMATE MODE PICKER ----------
|
||
# Vervangt het doorcyclen van modi. Cyclen stuurt bij elke tik een
|
||
# set_hvac_mode, dus om van 'cool' naar 'dry' te komen zet je een
|
||
# warmtepomp onderweg ook echt in 'heat' en 'fan_only'. Hier kies je
|
||
# rechtstreeks, en wordt er precies een opdracht verstuurd.
|
||
- obj:
|
||
id: climate_mode_overlay
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
hidden: true
|
||
bg_color: 0x000000
|
||
bg_opa: 90%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
align: TOP_MID
|
||
y: 16
|
||
text: "Mode"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
# Flex zorgt dat niet-ondersteunde modi geen gat achterlaten:
|
||
# LVGL slaat verborgen kinderen over bij het schikken.
|
||
- obj:
|
||
id: climate_mode_list
|
||
align: TOP_MID
|
||
y: 50
|
||
width: 98%
|
||
height: 178
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
scrollable: false
|
||
pad_all: 3
|
||
layout:
|
||
type: FLEX
|
||
flex_flow: ROW_WRAP
|
||
flex_align_main: CENTER
|
||
flex_align_cross: CENTER
|
||
flex_align_track: CENTER
|
||
widgets:
|
||
- obj:
|
||
id: cmode_off
|
||
width: 132
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: set_climate_mode, mode: "off" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Off"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: cmode_heat
|
||
width: 132
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: set_climate_mode, mode: "heat" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Heat"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: cmode_cool
|
||
width: 132
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: set_climate_mode, mode: "cool" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Cool"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: cmode_heat_cool
|
||
width: 132
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: set_climate_mode, mode: "heat_cool" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Heat/Cool"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: cmode_auto
|
||
width: 132
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: set_climate_mode, mode: "auto" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Auto"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: cmode_dry
|
||
width: 132
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: set_climate_mode, mode: "dry" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Dry"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: cmode_fan_only
|
||
width: 132
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: set_climate_mode, mode: "fan_only" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Fan"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: climate_mode_overlay_x
|
||
align: TOP_LEFT
|
||
x: 10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0x3C3C40
|
||
bg_opa: COVER
|
||
border_width: 1
|
||
border_color: 0xFFFFFF
|
||
border_opa: 20%
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: climate_mode_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "x"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: climate_mode_overlay_ok
|
||
align: TOP_RIGHT
|
||
x: -10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0xFEC600
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: climate_mode_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "\U000F012C"
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
# ---------- VACUUM CONTROL ----------
|
||
# Een stofzuiger is geen schakelaar: start, pauze, stop en terug naar
|
||
# het dock zijn losse opdrachten, en de zuigkracht is een aparte as.
|
||
- obj:
|
||
id: vacuum_overlay
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
hidden: true
|
||
bg_color: 0x000000
|
||
bg_opa: 90%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
widgets:
|
||
- label:
|
||
id: vacuum_title
|
||
align: TOP_MID
|
||
y: 16
|
||
text: "Vacuum"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: vacuum_actions
|
||
align: TOP_MID
|
||
y: 52
|
||
width: 98%
|
||
height: 80
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
scrollable: false
|
||
pad_all: 3
|
||
layout:
|
||
type: FLEX
|
||
flex_flow: ROW_WRAP
|
||
flex_align_main: CENTER
|
||
flex_align_cross: CENTER
|
||
flex_align_track: CENTER
|
||
widgets:
|
||
- obj:
|
||
id: vac_start
|
||
width: 72
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_action, service: "vacuum.start" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Start"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: vac_pause
|
||
width: 72
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_action, service: "vacuum.pause" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Pause"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: vac_return_to_base
|
||
width: 72
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_action, service: "vacuum.return_to_base" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Dock"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: vac_stop
|
||
width: 72
|
||
height: 38
|
||
radius: 19
|
||
bg_color: 0x2C2C30
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_action, service: "vacuum.stop" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Stop"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- label:
|
||
align: TOP_MID
|
||
y: 138
|
||
text: "Suction"
|
||
text_font: sublabel
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: vacuum_speeds
|
||
align: TOP_MID
|
||
y: 156
|
||
width: 98%
|
||
height: 74
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
scrollable: false
|
||
pad_all: 3
|
||
layout:
|
||
type: FLEX
|
||
flex_flow: ROW_WRAP
|
||
flex_align_main: CENTER
|
||
flex_align_cross: CENTER
|
||
flex_align_track: CENTER
|
||
widgets:
|
||
- obj:
|
||
id: vacspd_quiet
|
||
width: 72
|
||
height: 32
|
||
radius: 19
|
||
bg_color: 0x1F1F23
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_fan_speed, speed: "quiet" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Quiet"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: vacspd_balanced
|
||
width: 72
|
||
height: 32
|
||
radius: 19
|
||
bg_color: 0x1F1F23
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_fan_speed, speed: "balanced" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Balanced"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: vacspd_turbo
|
||
width: 72
|
||
height: 32
|
||
radius: 19
|
||
bg_color: 0x1F1F23
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_fan_speed, speed: "turbo" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Turbo"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
- obj:
|
||
id: vacspd_max
|
||
width: 72
|
||
height: 32
|
||
radius: 19
|
||
bg_color: 0x1F1F23
|
||
bg_opa: 92%
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
pad_all: 0
|
||
on_click:
|
||
- script.execute: { id: vacuum_fan_speed, speed: "max" }
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "Max"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: vacuum_overlay_x
|
||
align: TOP_LEFT
|
||
x: 10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0x3C3C40
|
||
bg_opa: COVER
|
||
border_width: 1
|
||
border_color: 0xFFFFFF
|
||
border_opa: 20%
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: vacuum_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "x"
|
||
text_font: sublabel_big
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
- obj:
|
||
id: vacuum_overlay_ok
|
||
align: TOP_RIGHT
|
||
x: -10
|
||
y: 9
|
||
width: 36
|
||
height: 36
|
||
radius: 999
|
||
bg_color: 0xFEC600
|
||
bg_opa: COVER
|
||
border_width: 0
|
||
clickable: true
|
||
scrollable: false
|
||
on_click:
|
||
- lvgl.widget.hide: vacuum_overlay
|
||
widgets:
|
||
- label:
|
||
align: CENTER
|
||
text: "\U000F012C"
|
||
text_font: materialdesign_icons
|
||
text_color: 0xFFFFFF
|
||
clickable: false
|
||
|
||
# ---------- DIM / WAKE OVERLAY ----------
|
||
- obj:
|
||
id: dim_wake_overlay
|
||
x: 0
|
||
y: 0
|
||
width: ${DISPLAY_W}
|
||
height: ${DISPLAY_H}
|
||
hidden: true
|
||
bg_opa: TRANSP
|
||
border_width: 0
|
||
scrollable: false
|
||
scrollbar_mode: 'OFF'
|
||
clickable: true
|
||
on_click:
|
||
- script.execute: wake_display
|
||
|
||
script:
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile1_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile1, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile1, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile1, bg_opa: 95% }
|
||
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile2_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile2, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile2, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile2, bg_opa: 95% }
|
||
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile3_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile3, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile3, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile3, bg_opa: 95% }
|
||
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile4_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile4, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile4, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile4, bg_opa: 95% }
|
||
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile5_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile5, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile5, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile5, bg_opa: 95% }
|
||
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile6_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile6, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile6, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile6, bg_opa: 95% }
|
||
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile7_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile7, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile7, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile7, bg_opa: 95% }
|
||
|
||
# Korte bevestigingspuls na het loslaten. Draait parallel, dus vertraagt
|
||
# de Home Assistant-actie of het openen van een overlay niet.
|
||
- id: tile8_ack
|
||
mode: restart
|
||
parameters:
|
||
is_long: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return is_long;'
|
||
then:
|
||
- lvgl.widget.update: { id: tile8, bg_opa: 20% }
|
||
- delay: 170ms
|
||
else:
|
||
- lvgl.widget.update: { id: tile8, bg_opa: 40% }
|
||
- delay: 90ms
|
||
- lvgl.widget.update: { id: tile8, bg_opa: 95% }
|
||
|
||
- id: wake_display
|
||
mode: restart
|
||
then:
|
||
- lambda: |-
|
||
id(last_touch_ms) = millis();
|
||
id(display_dimmed) = false;
|
||
- light.turn_on:
|
||
id: back_light
|
||
# Bijna instant omhoog. De ESPHome-standaard is 1s, en dat voelt traag
|
||
# als je het scherm aanraakt en op licht staat te wachten.
|
||
transition_length: 80ms
|
||
brightness: !lambda "return atoi(\"${MAX_BRIGHTNESS}\") / 100.0f;"
|
||
- lvgl.widget.hide: dim_wake_overlay
|
||
- lvgl.widget.hide: color_detail_overlay
|
||
- lvgl.widget.hide: climate_detail_overlay
|
||
- lvgl.widget.hide: climate_mode_overlay
|
||
- lvgl.widget.hide: vacuum_overlay
|
||
|
||
- id: dim_display
|
||
mode: restart
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${AUTO_DIM}") == "true";'
|
||
then:
|
||
- lambda: |-
|
||
int dim_value = atoi("${DIM_BRIGHTNESS}");
|
||
int max_value = atoi("${MAX_BRIGHTNESS}");
|
||
|
||
if (std::string("${NIGHT_MODE}") == "true") {
|
||
auto now = id(homeassistant_time).now();
|
||
int hour = now.hour;
|
||
int start_hour = atoi("${NIGHT_START_HOUR}");
|
||
int end_hour = atoi("${NIGHT_END_HOUR}");
|
||
bool is_night = false;
|
||
if (start_hour < end_hour) {
|
||
is_night = (hour >= start_hour && hour < end_hour);
|
||
} else {
|
||
is_night = (hour >= start_hour || hour < end_hour);
|
||
}
|
||
if (is_night) {
|
||
dim_value = atoi("${NIGHT_DIM_BRIGHTNESS}");
|
||
}
|
||
}
|
||
|
||
// Rustig omlaag faden is prettig; omhoog moet juist snel.
|
||
id(back_light).turn_on().set_transition_length(900).set_brightness(dim_value / 100.0f).perform();
|
||
|
||
// Always mark as dimmed to stop the interval from re-firing
|
||
id(display_dimmed) = true;
|
||
// Only show the touch-blocking overlay when brightness actually changes
|
||
if (dim_value < max_value) {
|
||
lv_obj_clear_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
|
||
}
|
||
|
||
# Terug naar het basisscherm bij standby. Bewust via de ESPHome
|
||
# acties en NIET met lv_obj_add_flag in een lambda: dit script
|
||
# draait vanuit een interval, dus buiten LVGL's eigen afhandeling.
|
||
- lvgl.widget.hide: brightness_overlay
|
||
- lvgl.widget.hide: color_detail_overlay
|
||
- lvgl.widget.hide: climate_detail_overlay
|
||
- lvgl.widget.hide: climate_mode_overlay
|
||
- lvgl.widget.hide: vacuum_overlay
|
||
|
||
- id: publish_action
|
||
mode: queued
|
||
parameters:
|
||
action: string
|
||
then:
|
||
- logger.log:
|
||
level: INFO
|
||
format: "publish_action: %s"
|
||
args: ["action.c_str()"]
|
||
- text_sensor.template.publish:
|
||
id: smartdisplay_action
|
||
state: !lambda 'return action;'
|
||
- delay: 500ms
|
||
- text_sensor.template.publish:
|
||
id: smartdisplay_action
|
||
state: ""
|
||
|
||
- id: climate_target_preview
|
||
parameters:
|
||
value: int
|
||
then:
|
||
- lambda: |-
|
||
int min_x10 = id(active_climate_min_x10);
|
||
int max_x10 = id(active_climate_max_x10);
|
||
if (value < min_x10) value = min_x10;
|
||
if (value > max_x10) value = max_x10;
|
||
int step_x10 = id(active_climate_step_x10);
|
||
if (step_x10 < 5) step_x10 = 5;
|
||
value = min_x10 + (int) lround((value - min_x10) / (float) step_x10) * step_x10;
|
||
if (value < min_x10) value = min_x10;
|
||
if (value > max_x10) value = max_x10;
|
||
id(active_climate_target_x10) = value;
|
||
|
||
char buf[16];
|
||
snprintf(buf, sizeof(buf), "%.1f°", value / 10.0f);
|
||
lv_label_set_text(id(climate_target_label), buf);
|
||
lv_arc_set_value(id(climate_arc), value);
|
||
|
||
- id: climate_target_commit
|
||
parameters:
|
||
value: int
|
||
then:
|
||
- script.execute:
|
||
id: climate_target_preview
|
||
value: !lambda "return value;"
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${DIRECT_ACTIONS}") == "true";'
|
||
then:
|
||
- homeassistant.action:
|
||
action: climate.set_temperature
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
temperature: !lambda "return value / 10.0f;"
|
||
|
||
- id: climate_adjust_target
|
||
parameters:
|
||
direction: int
|
||
then:
|
||
- lambda: |-
|
||
int step_x10 = id(active_climate_step_x10);
|
||
if (step_x10 < 5) step_x10 = 5;
|
||
int next = id(active_climate_target_x10) + (direction * step_x10);
|
||
if (next < id(active_climate_min_x10)) next = id(active_climate_min_x10);
|
||
if (next > id(active_climate_max_x10)) next = id(active_climate_max_x10);
|
||
id(active_climate_target_x10) = next;
|
||
- script.execute:
|
||
id: climate_target_commit
|
||
value: !lambda "return id(active_climate_target_x10);"
|
||
|
||
# Opent de moduskiezer. Verbergt modi die de entiteit niet ondersteunt en
|
||
# markeert de huidige, zodat je ziet waar je staat voor je iets kiest.
|
||
# Opent de stofzuigerbediening en onthoudt om welke entiteit het gaat.
|
||
- id: open_vacuum_overlay
|
||
mode: restart
|
||
parameters:
|
||
title: string
|
||
entity: string
|
||
then:
|
||
- lambda: |-
|
||
id(active_entity) = entity;
|
||
lv_label_set_text(id(vacuum_title), title.c_str());
|
||
- lvgl.widget.show: vacuum_overlay
|
||
|
||
# Een losse opdracht: start, pause, stop of return_to_base.
|
||
- id: vacuum_action
|
||
mode: queued
|
||
parameters:
|
||
service: string
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${DIRECT_ACTIONS}") == "true";'
|
||
then:
|
||
- homeassistant.action:
|
||
action: !lambda "return service;"
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
|
||
# Zuigkracht is een aparte as, los van start/stop.
|
||
- id: vacuum_fan_speed
|
||
mode: queued
|
||
parameters:
|
||
speed: string
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${DIRECT_ACTIONS}") == "true";'
|
||
then:
|
||
- homeassistant.action:
|
||
action: vacuum.set_fan_speed
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
fan_speed: !lambda "return speed;"
|
||
|
||
- id: open_climate_mode_picker
|
||
mode: restart
|
||
then:
|
||
- lambda: |-
|
||
auto lower = [](std::string v) {
|
||
for (char &c : v) { if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a'); }
|
||
return v;
|
||
};
|
||
std::string supported = lower(id(active_climate_supported_modes));
|
||
std::string current = lower(id(active_climate_mode));
|
||
|
||
auto has = [&](const char* m) {
|
||
std::string mode(m);
|
||
if (supported.empty()) return true;
|
||
return supported.find("'" + mode + "'") != std::string::npos ||
|
||
supported.find("\"" + mode + "\"") != std::string::npos;
|
||
};
|
||
auto apply = [&](lv_obj_t* btn, const char* m) {
|
||
if (has(m)) lv_obj_clear_flag(btn, LV_OBJ_FLAG_HIDDEN);
|
||
else lv_obj_add_flag(btn, LV_OBJ_FLAG_HIDDEN);
|
||
bool selected = (current == std::string(m));
|
||
lv_obj_set_style_bg_color(btn,
|
||
lv_color_hex(selected ? 0xFF9F0A : 0x2C2C30), LV_PART_MAIN);
|
||
};
|
||
apply(id(cmode_off), "off");
|
||
apply(id(cmode_heat), "heat");
|
||
apply(id(cmode_cool), "cool");
|
||
apply(id(cmode_heat_cool), "heat_cool");
|
||
apply(id(cmode_auto), "auto");
|
||
apply(id(cmode_dry), "dry");
|
||
apply(id(cmode_fan_only), "fan_only");
|
||
- lvgl.widget.show: climate_mode_overlay
|
||
|
||
# Zet precies een modus. Geen tussenstappen, dus geen ongewenste
|
||
# commando's naar de warmtepomp onderweg.
|
||
- id: set_climate_mode
|
||
mode: restart
|
||
parameters:
|
||
mode: string
|
||
then:
|
||
- lambda: |-
|
||
id(active_climate_mode) = mode;
|
||
if (mode == "off") lv_label_set_text(id(climate_mode_label), "Off");
|
||
if (mode == "heat") lv_label_set_text(id(climate_mode_label), "Heat");
|
||
if (mode == "cool") lv_label_set_text(id(climate_mode_label), "Cool");
|
||
if (mode == "heat_cool") lv_label_set_text(id(climate_mode_label), "Heat/Cool");
|
||
if (mode == "auto") lv_label_set_text(id(climate_mode_label), "Auto");
|
||
if (mode == "dry") lv_label_set_text(id(climate_mode_label), "Dry");
|
||
if (mode == "fan_only") lv_label_set_text(id(climate_mode_label), "Fan");
|
||
- lvgl.widget.hide: climate_mode_overlay
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${DIRECT_ACTIONS}") == "true";'
|
||
then:
|
||
- homeassistant.action:
|
||
action: climate.set_hvac_mode
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
hvac_mode: !lambda "return id(active_climate_mode);"
|
||
|
||
- id: open_color_detail
|
||
mode: restart
|
||
then:
|
||
- lambda: |-
|
||
lv_label_set_text(id(color_detail_title), id(active_title).c_str());
|
||
|
||
char buf[12];
|
||
snprintf(buf, sizeof(buf), "%d %%", id(active_brightness_pct));
|
||
lv_label_set_text(id(color_brightness_value), buf);
|
||
lv_slider_set_value(id(color_brightness_slider), id(active_brightness_pct), LV_ANIM_OFF);
|
||
|
||
bool show_tabs = id(active_supports_color) && id(active_supports_temperature);
|
||
if (show_tabs) {
|
||
lv_obj_clear_flag(id(color_mode_tabs), LV_OBJ_FLAG_HIDDEN);
|
||
} else {
|
||
lv_obj_add_flag(id(color_mode_tabs), LV_OBJ_FLAG_HIDDEN);
|
||
}
|
||
|
||
if (atoi("${DISPLAY_W}") < atoi("${DISPLAY_H}")) {
|
||
int palette_y = show_tabs ? 96 : 76;
|
||
lv_obj_set_size(id(color_palette_card), 150, 86);
|
||
lv_obj_set_y(id(color_palette_card), palette_y);
|
||
lv_obj_set_pos(id(color_preset_red), 13, 4);
|
||
lv_obj_set_pos(id(color_preset_orange), 60, 4);
|
||
lv_obj_set_pos(id(color_preset_yellow), 107, 4);
|
||
lv_obj_set_pos(id(color_preset_green), 13, 52);
|
||
lv_obj_set_pos(id(color_preset_blue), 60, 52);
|
||
lv_obj_set_pos(id(color_preset_purple), 107, 52);
|
||
|
||
lv_obj_set_size(id(temp_palette_card), 150, 126);
|
||
lv_obj_set_y(id(temp_palette_card), palette_y);
|
||
lv_obj_set_pos(id(temp_preset_2700), 44, 4);
|
||
lv_obj_set_pos(id(temp_preset_4000), 44, 48);
|
||
lv_obj_set_pos(id(temp_preset_6500), 44, 92);
|
||
|
||
lv_obj_set_y(id(color_brightness_card), 256);
|
||
} else {
|
||
int palette_y = show_tabs ? 98 : 78;
|
||
lv_obj_set_size(id(color_palette_card), 286, 48);
|
||
lv_obj_set_y(id(color_palette_card), palette_y);
|
||
lv_obj_set_pos(id(color_preset_red), 14, 8);
|
||
lv_obj_set_pos(id(color_preset_orange), 61, 8);
|
||
lv_obj_set_pos(id(color_preset_yellow), 108, 8);
|
||
lv_obj_set_pos(id(color_preset_green), 155, 8);
|
||
lv_obj_set_pos(id(color_preset_blue), 202, 8);
|
||
lv_obj_set_pos(id(color_preset_purple), 249, 8);
|
||
|
||
lv_obj_set_size(id(temp_palette_card), 286, 48);
|
||
lv_obj_set_y(id(temp_palette_card), palette_y);
|
||
lv_obj_set_pos(id(temp_preset_2700), 24, 8);
|
||
lv_obj_set_pos(id(temp_preset_4000), 112, 8);
|
||
lv_obj_set_pos(id(temp_preset_6500), 200, 8);
|
||
|
||
lv_obj_set_y(id(color_brightness_card), 165);
|
||
}
|
||
|
||
id(active_color_detail_tab) = id(active_supports_color) ? 0 : 1;
|
||
- lvgl.widget.hide: brightness_overlay
|
||
- lvgl.widget.show: color_detail_overlay
|
||
- script.execute:
|
||
id: show_color_detail_tab
|
||
tab: !lambda "return id(active_color_detail_tab);"
|
||
|
||
- id: show_color_detail_tab
|
||
mode: restart
|
||
parameters:
|
||
tab: int
|
||
then:
|
||
- lambda: |-
|
||
if (tab == 0 && !id(active_supports_color) && id(active_supports_temperature)) {
|
||
tab = 1;
|
||
} else if (tab == 1 && !id(active_supports_temperature) && id(active_supports_color)) {
|
||
tab = 0;
|
||
}
|
||
id(active_color_detail_tab) = tab;
|
||
|
||
bool show_tabs = id(active_supports_color) && id(active_supports_temperature);
|
||
if (show_tabs) {
|
||
lv_obj_clear_flag(id(color_mode_tabs), LV_OBJ_FLAG_HIDDEN);
|
||
} else {
|
||
lv_obj_add_flag(id(color_mode_tabs), LV_OBJ_FLAG_HIDDEN);
|
||
}
|
||
|
||
if (tab == 0 && id(active_supports_color)) {
|
||
lv_obj_clear_flag(id(color_palette_card), LV_OBJ_FLAG_HIDDEN);
|
||
lv_obj_add_flag(id(temp_palette_card), LV_OBJ_FLAG_HIDDEN);
|
||
if (show_tabs) lv_obj_set_x(id(color_tab_indicator), 2);
|
||
} else if (tab == 1 && id(active_supports_temperature)) {
|
||
lv_obj_add_flag(id(color_palette_card), LV_OBJ_FLAG_HIDDEN);
|
||
lv_obj_clear_flag(id(temp_palette_card), LV_OBJ_FLAG_HIDDEN);
|
||
if (show_tabs) lv_obj_set_x(id(color_tab_indicator), 95);
|
||
} else {
|
||
lv_obj_add_flag(id(color_palette_card), LV_OBJ_FLAG_HIDDEN);
|
||
lv_obj_add_flag(id(temp_palette_card), LV_OBJ_FLAG_HIDDEN);
|
||
}
|
||
|
||
lv_obj_set_style_text_color(
|
||
id(color_tab_label),
|
||
id(active_supports_color) ? lv_color_hex(0xFFFFFF) : lv_color_hex(0x8E8E93),
|
||
0
|
||
);
|
||
lv_obj_set_style_text_color(
|
||
id(temp_tab_label),
|
||
id(active_supports_temperature) ? lv_color_hex(0xFFFFFF) : lv_color_hex(0x8E8E93),
|
||
0
|
||
);
|
||
if (tab == 0 && id(active_supports_color)) {
|
||
lv_obj_set_style_text_color(id(color_tab_label), lv_color_hex(0xFFFFFF), 0);
|
||
lv_obj_set_style_text_color(id(temp_tab_label), lv_color_hex(0xD1D1D6), 0);
|
||
} else if (tab == 1 && id(active_supports_temperature)) {
|
||
lv_obj_set_style_text_color(id(color_tab_label), lv_color_hex(0xD1D1D6), 0);
|
||
lv_obj_set_style_text_color(id(temp_tab_label), lv_color_hex(0xFFFFFF), 0);
|
||
}
|
||
|
||
- id: color_brightness_preview
|
||
parameters:
|
||
value: int
|
||
then:
|
||
- lambda: |-
|
||
id(active_brightness_pct) = value;
|
||
char buf[12];
|
||
snprintf(buf, sizeof(buf), "%d %%", value);
|
||
lv_label_set_text(id(color_brightness_value), buf);
|
||
|
||
- id: color_brightness_commit
|
||
parameters:
|
||
value: int
|
||
then:
|
||
- lambda: 'id(active_brightness_pct) = value;'
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${DIRECT_ACTIONS}") == "true";'
|
||
then:
|
||
- homeassistant.action:
|
||
action: light.turn_on
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
brightness_pct: !lambda "return value;"
|
||
|
||
- id: set_light_color_name
|
||
mode: queued
|
||
parameters:
|
||
color: string
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${DIRECT_ACTIONS}") == "true";'
|
||
then:
|
||
- homeassistant.action:
|
||
action: light.turn_on
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
color_name: !lambda "return color;"
|
||
brightness_pct: !lambda "return id(active_brightness_pct);"
|
||
|
||
- id: set_light_color_temperature
|
||
mode: queued
|
||
parameters:
|
||
kelvin: int
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: 'return std::string("${DIRECT_ACTIONS}") == "true";'
|
||
then:
|
||
- homeassistant.action:
|
||
action: light.turn_on
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
color_temp_kelvin: !lambda "return kelvin;"
|
||
brightness_pct: !lambda "return id(active_brightness_pct);"
|
||
|
||
- id: slider_preview
|
||
parameters:
|
||
value: int
|
||
then:
|
||
- lambda: |-
|
||
char buf[20];
|
||
snprintf(buf, sizeof(buf), "%d%s", value, id(active_value_suffix).c_str());
|
||
lv_label_set_text(id(overlay_value), buf);
|
||
int pct = value;
|
||
if (pct < 0) pct = 0;
|
||
if (pct > 100) pct = 100;
|
||
std::string value_text;
|
||
|
||
if (id(active_control_kind) == "cover_position") {
|
||
std::string closed_label = id(active_label_off).c_str();
|
||
std::string open_label = id(active_label_on).c_str();
|
||
|
||
if (pct <= 0) {
|
||
value_text = closed_label;
|
||
} else if (pct >= 100) {
|
||
value_text = open_label;
|
||
} else {
|
||
char buf[40];
|
||
snprintf(buf, sizeof(buf), "%s - %d%%", open_label.c_str(), pct);
|
||
value_text = buf;
|
||
}
|
||
} else {
|
||
if (pct <= 0) {
|
||
value_text = id(active_label_off).c_str();
|
||
} else {
|
||
char buf[20];
|
||
snprintf(buf, sizeof(buf), "%d%s", pct, id(active_value_suffix).c_str());
|
||
value_text = buf;
|
||
}
|
||
}
|
||
|
||
lv_label_set_text(id(overlay_value), value_text.c_str());
|
||
|
||
|
||
- id: slider_commit
|
||
parameters:
|
||
value: int
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return id(active_control_kind) == "light_brightness";
|
||
then:
|
||
- homeassistant.action:
|
||
action: light.turn_on
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
brightness_pct: !lambda "return value;"
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return id(active_control_kind) == "fan_percentage";
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: "return value <= 0;"
|
||
then:
|
||
- homeassistant.action:
|
||
action: fan.turn_off
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
else:
|
||
- homeassistant.action:
|
||
action: fan.turn_on
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
- delay: 150ms
|
||
- homeassistant.action:
|
||
action: fan.set_percentage
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
percentage: !lambda "return value;"
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return id(active_control_kind) == "cover_position";
|
||
then:
|
||
- homeassistant.action:
|
||
action: cover.set_cover_position
|
||
data:
|
||
entity_id: !lambda "return id(active_entity);"
|
||
position: !lambda "return value;"
|
||
|
||
# ----------------------------------------------------------
|
||
# do_tile_action
|
||
#
|
||
# Centralized handler for executing Home Assistant actions
|
||
# triggered by tile taps.
|
||
#
|
||
# Parameters
|
||
# - tile_type: light | fan | switch | scene | script | cover
|
||
# - tap_action: auto | toggle | activate | fan_toggle_preset
|
||
# - service: optional Home Assistant service override
|
||
# - entity_id: target entity
|
||
# - param_key / param_val: optional parameters for custom services
|
||
# - was_on: previous entity state (pass false for longpress actions)
|
||
#
|
||
# Behavior
|
||
#
|
||
# toggle
|
||
# -> toggles the entity
|
||
# light.toggle / fan.toggle / switch.toggle / cover.open_cover|close_cover (based on was_on)
|
||
#
|
||
# activate
|
||
# -> activates entity
|
||
# scene.turn_on / script.turn_on / light.turn_on / fan.turn_on / cover.open_cover
|
||
#
|
||
# auto
|
||
# -> resolves automatically based on tile_type:
|
||
# light/switch/fan/cover -> toggle
|
||
# scene/script -> activate
|
||
#
|
||
# fan_toggle_preset
|
||
# -> if fan was ON: fan.turn_off
|
||
# -> if fan was OFF:
|
||
# fan.turn_on
|
||
# wait 150ms
|
||
# fan.set_preset_mode(param_val)
|
||
#
|
||
# Custom service
|
||
# If "service" is set, it will be called directly.
|
||
# param_key / param_val may be used for parameters
|
||
# like preset_mode, brightness_pct, percentage, etc.
|
||
# ----------------------------------------------------------
|
||
- id: do_tile_action
|
||
mode: queued
|
||
parameters:
|
||
tile_type: string
|
||
tap_action: string
|
||
service: string
|
||
entity_id: string
|
||
param_key: string
|
||
param_val: string
|
||
was_on: bool
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return std::string("${DIRECT_ACTIONS}") == "true";
|
||
then:
|
||
- lambda: |-
|
||
ESP_LOGD("tile", "do_tile_action type=%s action=%s service=%s entity=%s key=%s val=%s was_on=%d",
|
||
tile_type.c_str(), tap_action.c_str(), service.c_str(), entity_id.c_str(),
|
||
param_key.c_str(), param_val.c_str(), (int)was_on);
|
||
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tap_action == "fan_toggle_preset";
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return was_on;
|
||
then:
|
||
- homeassistant.action:
|
||
action: fan.turn_off
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- homeassistant.action:
|
||
action: fan.turn_on
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
- delay: 150ms
|
||
- homeassistant.action:
|
||
action: fan.set_preset_mode
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
preset_mode: !lambda 'return param_val;'
|
||
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
std::string resolved = tap_action;
|
||
if (resolved == "auto") {
|
||
if (tile_type == "scene" || tile_type == "script") resolved = "activate";
|
||
else resolved = "toggle";
|
||
}
|
||
return resolved == "toggle";
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return service.size() > 0;
|
||
then:
|
||
- homeassistant.action:
|
||
action: !lambda 'return service;'
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "light";
|
||
then:
|
||
- homeassistant.action:
|
||
action: light.toggle
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "fan";
|
||
then:
|
||
- homeassistant.action:
|
||
action: fan.toggle
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "switch";
|
||
then:
|
||
- homeassistant.action:
|
||
action: switch.toggle
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "cover";
|
||
then:
|
||
- homeassistant.action:
|
||
action: cover.set_cover_position
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
position: !lambda |-
|
||
return was_on ? 0 : 100;
|
||
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
std::string resolved = tap_action;
|
||
if (resolved == "auto") {
|
||
if (tile_type == "scene" || tile_type == "script") resolved = "activate";
|
||
else resolved = "toggle";
|
||
}
|
||
return resolved == "activate";
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return service.size() > 0;
|
||
then:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return param_key == "preset_mode";
|
||
then:
|
||
- homeassistant.action:
|
||
action: !lambda 'return service;'
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
preset_mode: !lambda 'return param_val;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return param_key == "brightness_pct";
|
||
then:
|
||
- homeassistant.action:
|
||
action: !lambda 'return service;'
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
brightness_pct: !lambda 'return atoi(param_val.c_str());'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return param_key == "percentage";
|
||
then:
|
||
- homeassistant.action:
|
||
action: !lambda 'return service;'
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
percentage: !lambda 'return atoi(param_val.c_str());'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return param_key == "option";
|
||
then:
|
||
- homeassistant.action:
|
||
action: !lambda 'return service;'
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
option: !lambda 'return param_val;'
|
||
else:
|
||
- homeassistant.action:
|
||
action: !lambda 'return service;'
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "scene";
|
||
then:
|
||
- homeassistant.action:
|
||
action: scene.turn_on
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "script";
|
||
then:
|
||
- homeassistant.action:
|
||
action: script.turn_on
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "light";
|
||
then:
|
||
- homeassistant.action:
|
||
action: light.turn_on
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "fan";
|
||
then:
|
||
- homeassistant.action:
|
||
action: fan.turn_on
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "switch";
|
||
then:
|
||
- homeassistant.action:
|
||
action: switch.turn_on
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
else:
|
||
- if:
|
||
condition:
|
||
lambda: |-
|
||
return tile_type == "cover";
|
||
then:
|
||
- homeassistant.action:
|
||
action: cover.open_cover
|
||
data:
|
||
entity_id: !lambda 'return entity_id;'
|
||
|
||
- id: open_value_overlay
|
||
parameters:
|
||
title: string
|
||
entity: string
|
||
tile_type: string
|
||
longpress_mode: string
|
||
was_on: bool
|
||
off_value: int
|
||
brightness_value: float
|
||
percentage_value: float
|
||
position_value: float
|
||
icon_text: string
|
||
slider_bg_color: uint32_t
|
||
slider_fill_color: uint32_t
|
||
slider_icon_color: uint32_t
|
||
label_off: string
|
||
label_on: string
|
||
supported_color_modes: string
|
||
climate_current_temperature: float
|
||
climate_target_temperature: float
|
||
climate_humidity: float
|
||
climate_mode: string
|
||
climate_supported_modes: string
|
||
climate_min_temp: string
|
||
climate_max_temp: string
|
||
climate_step: string
|
||
then:
|
||
- lambda: |-
|
||
// Accentkleur uit het Home Assistant-domein: het stuk voor de punt in
|
||
// het entity_id. script/scene/button vuur je af (ACCENT_ACTION), een
|
||
// light is een light (ACCENT_LIGHT), de rest is apparaat.
|
||
// LET OP: deze helper staat twee keer in dit bestand — hier en in
|
||
// open_value_overlay. Pas ze samen aan.
|
||
auto accent_for = [](const std::string& entity_id) -> uint32_t {
|
||
std::string d = entity_id.substr(0, entity_id.find('.'));
|
||
if (d == "script" || d == "scene" || d == "button" ||
|
||
d == "input_button" || d == "automation") return ${ACCENT_ACTION};
|
||
if (d == "light") return ${ACCENT_LIGHT};
|
||
return ${ACCENT_DEVICE};
|
||
};
|
||
|
||
// De sliderkleur komt van de tegel, dus die kan ook ${ACCENT_AUTO} zijn.
|
||
uint32_t fill_color = (slider_fill_color == ${ACCENT_AUTO})
|
||
? accent_for(entity) : slider_fill_color;
|
||
|
||
std::string mode = longpress_mode;
|
||
auto contains_mode = [](const std::string& modes, const std::string& needle) {
|
||
return modes.find(needle) != std::string::npos;
|
||
};
|
||
if (mode == "auto") {
|
||
if (tile_type == "light") mode = "brightness";
|
||
else if (tile_type == "fan") mode = "percentage";
|
||
else if (tile_type == "cover") mode = "cover_position";
|
||
else if (tile_type == "climate") mode = "climate_temperature";
|
||
else mode = "none";
|
||
}
|
||
|
||
if (mode == "none") return;
|
||
|
||
id(active_entity) = entity;
|
||
id(active_value_suffix) = " %";
|
||
id(active_label_off) = label_off;
|
||
id(active_label_on) = label_on;
|
||
id(active_title) = title;
|
||
id(active_icon_text) = icon_text;
|
||
id(active_supports_color) = false;
|
||
id(active_supports_temperature) = false;
|
||
lv_obj_add_flag(id(overlay_color_button), LV_OBJ_FLAG_HIDDEN);
|
||
|
||
if (mode == "climate_temperature") {
|
||
auto to_x10 = [](float value, int fallback) -> int {
|
||
if (isnan(value)) return fallback;
|
||
return (int) lround(value * 10.0f);
|
||
};
|
||
auto parse_x10 = [](const std::string& value, int fallback) -> int {
|
||
if (value.empty()) return fallback;
|
||
return (int) lround(atof(value.c_str()) * 10.0f);
|
||
};
|
||
id(active_control_kind) = "climate_temperature";
|
||
id(active_climate_min_x10) = parse_x10(climate_min_temp, 50);
|
||
id(active_climate_max_x10) = parse_x10(climate_max_temp, 300);
|
||
id(active_climate_step_x10) = parse_x10(climate_step, 5);
|
||
if (id(active_climate_step_x10) < 5) id(active_climate_step_x10) = 5;
|
||
if (id(active_climate_max_x10) <= id(active_climate_min_x10)) {
|
||
id(active_climate_min_x10) = 50;
|
||
id(active_climate_max_x10) = 300;
|
||
}
|
||
|
||
id(active_climate_current_x10) = to_x10(climate_current_temperature, 0);
|
||
int fallback_target = isnan(climate_current_temperature) ? 200 : id(active_climate_current_x10);
|
||
id(active_climate_target_x10) = to_x10(climate_target_temperature, fallback_target);
|
||
if (id(active_climate_target_x10) < id(active_climate_min_x10)) id(active_climate_target_x10) = id(active_climate_min_x10);
|
||
if (id(active_climate_target_x10) > id(active_climate_max_x10)) id(active_climate_target_x10) = id(active_climate_max_x10);
|
||
id(active_climate_target_x10) = id(active_climate_min_x10) + (int) lround((id(active_climate_target_x10) - id(active_climate_min_x10)) / (float) id(active_climate_step_x10)) * id(active_climate_step_x10);
|
||
if (id(active_climate_target_x10) < id(active_climate_min_x10)) id(active_climate_target_x10) = id(active_climate_min_x10);
|
||
if (id(active_climate_target_x10) > id(active_climate_max_x10)) id(active_climate_target_x10) = id(active_climate_max_x10);
|
||
id(active_climate_humidity) = isnan(climate_humidity) ? -1 : (int) lround(climate_humidity);
|
||
id(active_climate_mode) = climate_mode.empty() ? std::string("Auto") : climate_mode;
|
||
id(active_climate_supported_modes) = climate_supported_modes;
|
||
|
||
lv_label_set_text(id(climate_detail_title), title.c_str());
|
||
|
||
char current_buf[48];
|
||
if (isnan(climate_current_temperature)) {
|
||
snprintf(current_buf, sizeof(current_buf), "Current: --");
|
||
} else if (id(active_climate_humidity) >= 0) {
|
||
snprintf(current_buf, sizeof(current_buf), "Current: %.1f° - Humidity: %d %%", climate_current_temperature, id(active_climate_humidity));
|
||
} else {
|
||
snprintf(current_buf, sizeof(current_buf), "Current: %.1f°", climate_current_temperature);
|
||
}
|
||
lv_label_set_text(id(climate_current_label), current_buf);
|
||
|
||
char target_buf[16];
|
||
snprintf(target_buf, sizeof(target_buf), "%.1f°", id(active_climate_target_x10) / 10.0f);
|
||
lv_label_set_text(id(climate_target_label), target_buf);
|
||
lv_label_set_text(id(climate_mode_label), id(active_climate_mode).c_str());
|
||
|
||
lv_arc_set_range(id(climate_arc), id(active_climate_min_x10), id(active_climate_max_x10));
|
||
lv_arc_set_value(id(climate_arc), id(active_climate_target_x10));
|
||
|
||
if (atoi("${DISPLAY_W}") < atoi("${DISPLAY_H}")) {
|
||
lv_obj_set_size(id(climate_arc), 196, 196);
|
||
lv_obj_set_y(id(climate_arc), 8);
|
||
lv_obj_set_y(id(climate_current_label), 48);
|
||
lv_obj_set_y(id(climate_minus_button), -62);
|
||
lv_obj_set_y(id(climate_plus_button), -62);
|
||
lv_obj_set_y(id(climate_mode_pill), -18);
|
||
} else {
|
||
lv_obj_set_size(id(climate_arc), 176, 176);
|
||
lv_obj_set_y(id(climate_arc), 18);
|
||
lv_obj_set_y(id(climate_current_label), 40);
|
||
lv_obj_set_y(id(climate_minus_button), -14);
|
||
lv_obj_set_y(id(climate_plus_button), -14);
|
||
lv_obj_set_y(id(climate_mode_pill), -15);
|
||
}
|
||
|
||
lv_obj_add_flag(id(brightness_overlay), LV_OBJ_FLAG_HIDDEN);
|
||
lv_obj_clear_flag(id(climate_detail_overlay), LV_OBJ_FLAG_HIDDEN);
|
||
return;
|
||
}
|
||
|
||
int pct = off_value;
|
||
|
||
if (mode == "brightness") {
|
||
id(active_control_kind) = "light_brightness";
|
||
if (was_on) {
|
||
if (isnan(brightness_value)) pct = 100;
|
||
else pct = (int) lround((brightness_value / 255.0f) * 100.0f);
|
||
}
|
||
} else if (mode == "percentage") {
|
||
id(active_control_kind) = "fan_percentage";
|
||
if (was_on) {
|
||
if (isnan(percentage_value)) pct = 0;
|
||
else pct = (int) lround(percentage_value);
|
||
}
|
||
} else if (mode == "cover_position") {
|
||
id(active_control_kind) = "cover_position";
|
||
if (isnan(position_value)) pct = was_on ? 100 : 0;
|
||
else pct = (int) lround(position_value);
|
||
} else {
|
||
return;
|
||
}
|
||
|
||
if (pct < 0) pct = 0;
|
||
if (pct > 100) pct = 100;
|
||
id(active_brightness_pct) = pct;
|
||
|
||
if (tile_type == "light" && std::string("${DIRECT_ACTIONS}") == "true") {
|
||
id(active_supports_color) =
|
||
contains_mode(supported_color_modes, "hs") ||
|
||
contains_mode(supported_color_modes, "rgb") ||
|
||
contains_mode(supported_color_modes, "rgbw") ||
|
||
contains_mode(supported_color_modes, "rgbww") ||
|
||
contains_mode(supported_color_modes, "xy");
|
||
id(active_supports_temperature) = contains_mode(supported_color_modes, "color_temp");
|
||
|
||
if (id(active_supports_color) || id(active_supports_temperature)) {
|
||
lv_obj_clear_flag(id(overlay_color_button), LV_OBJ_FLAG_HIDDEN);
|
||
}
|
||
}
|
||
|
||
if (tile_type == "cover") {
|
||
lv_obj_set_style_bg_color(id(overlay_slider), lv_color_hex(fill_color), LV_PART_MAIN);
|
||
lv_obj_set_style_bg_color(id(overlay_slider), lv_color_hex(slider_bg_color), LV_PART_INDICATOR);
|
||
lv_obj_set_style_bg_opa(id(overlay_slider), LV_OPA_COVER, LV_PART_MAIN);
|
||
}
|
||
else{
|
||
lv_obj_set_style_bg_color(id(overlay_slider), lv_color_hex(slider_bg_color), LV_PART_MAIN);
|
||
lv_obj_set_style_bg_color(id(overlay_slider), lv_color_hex(fill_color), LV_PART_INDICATOR);
|
||
lv_obj_set_style_bg_opa(id(overlay_slider), LV_OPA_COVER, LV_PART_MAIN);
|
||
}
|
||
lv_obj_set_style_radius(id(overlay_slider), 28, LV_PART_MAIN);
|
||
lv_obj_set_style_radius(id(overlay_slider), 0, LV_PART_INDICATOR);
|
||
|
||
lv_label_set_text(id(overlay_title), title.c_str());
|
||
lv_label_set_text(id(overlay_icon), icon_text.c_str());
|
||
lv_slider_set_value(id(overlay_slider), pct, LV_ANIM_OFF);
|
||
|
||
std::string value_text;
|
||
|
||
if (tile_type == "cover") {
|
||
if (pct <= 0) {
|
||
value_text = label_off;
|
||
} else if (pct >= 100) {
|
||
value_text = label_on;
|
||
} else {
|
||
char buf[40];
|
||
snprintf(buf, sizeof(buf), "%s - %d%%", label_on.c_str(), pct);
|
||
value_text = buf;
|
||
}
|
||
} else {
|
||
if (pct <= 0) {
|
||
value_text = label_off;
|
||
} else {
|
||
char buf[20];
|
||
snprintf(buf, sizeof(buf), "%d%s", pct, id(active_value_suffix).c_str());
|
||
value_text = buf;
|
||
}
|
||
}
|
||
|
||
lv_label_set_text(id(overlay_value), value_text.c_str());
|
||
|
||
lv_obj_set_style_bg_opa(id(overlay_slider), LV_OPA_COVER, LV_PART_INDICATOR);
|
||
|
||
lv_obj_set_style_text_color(id(overlay_icon), lv_color_hex(slider_icon_color), 0);
|
||
|
||
- if:
|
||
condition:
|
||
lambda: 'return id(active_control_kind) != "climate_temperature";'
|
||
then:
|
||
- lvgl.widget.show: brightness_overlay
|
||
|
||
# UI refresh (values + time + styles) + render
|
||
- id: ui_refresh
|
||
mode: queued
|
||
then:
|
||
- lambda: |-
|
||
auto set_lbl = [](lv_obj_t* lbl, const std::string& s) {
|
||
if (lbl) lv_label_set_text(lbl, s.c_str());
|
||
};
|
||
|
||
auto set_value_auto = [&](lv_obj_t* lbl,
|
||
bool on,
|
||
const std::string& tile_type,
|
||
const std::string& value_mode,
|
||
float brightness,
|
||
float percentage,
|
||
const std::string& preset,
|
||
float climate_current_temperature,
|
||
float climate_target_temperature,
|
||
float climate_humidity,
|
||
const std::string& label_on,
|
||
const std::string& label_off) {
|
||
std::string mode = value_mode;
|
||
|
||
if (mode == "auto" || mode.empty()) {
|
||
if (tile_type == "light") {
|
||
mode = "brightness";
|
||
} else if (tile_type == "fan") {
|
||
mode = preset.empty() ? "percentage" : "preset";
|
||
} else if (tile_type == "climate") {
|
||
mode = "climate";
|
||
} else {
|
||
mode = "text";
|
||
}
|
||
}
|
||
|
||
if (mode == "brightness") {
|
||
if (!on) {
|
||
set_lbl(lbl, label_off);
|
||
return;
|
||
}
|
||
if (isnan(brightness)) {
|
||
set_lbl(lbl, "100 %");
|
||
return;
|
||
}
|
||
int pct = (int) lround((brightness / 255.0f) * 100.0f);
|
||
char buf[8];
|
||
snprintf(buf, sizeof(buf), "%d %%", pct);
|
||
set_lbl(lbl, std::string(buf));
|
||
return;
|
||
}
|
||
|
||
if (mode == "percentage") {
|
||
if (!on) {
|
||
set_lbl(lbl, label_off);
|
||
return;
|
||
}
|
||
int pct = isnan(percentage) ? 0 : (int) lround(percentage);
|
||
char buf[8];
|
||
snprintf(buf, sizeof(buf), "%d %%", pct);
|
||
set_lbl(lbl, std::string(buf));
|
||
return;
|
||
}
|
||
|
||
if (mode == "preset") {
|
||
if (!on) {
|
||
set_lbl(lbl, label_off);
|
||
return;
|
||
}
|
||
if (!preset.empty()) {
|
||
set_lbl(lbl, preset);
|
||
} else {
|
||
set_lbl(lbl, label_on);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (mode == "climate") {
|
||
if (!on) {
|
||
set_lbl(lbl, label_off);
|
||
return;
|
||
}
|
||
|
||
float temp = isnan(climate_target_temperature) ? climate_current_temperature : climate_target_temperature;
|
||
if (isnan(temp)) {
|
||
set_lbl(lbl, label_on);
|
||
return;
|
||
}
|
||
|
||
char buf[16];
|
||
snprintf(buf, sizeof(buf), "%.1f °", temp);
|
||
set_lbl(lbl, std::string(buf));
|
||
return;
|
||
}
|
||
|
||
if (mode == "text") {
|
||
if (tile_type == "scene" || tile_type == "script") {
|
||
set_lbl(lbl, label_on);
|
||
} else {
|
||
set_lbl(lbl, on ? label_on : label_off);
|
||
}
|
||
return;
|
||
}
|
||
|
||
set_lbl(lbl, "—");
|
||
};
|
||
|
||
// active_state niet leeg? Dan telt alleen die exacte HA-state als "aan".
|
||
// Zo kan een input_select ("Vol"/"Leeg") ook een tegel laten oplichten.
|
||
auto is_active = [](const std::string& tile_type, const std::string& state,
|
||
const std::string& active_state = std::string()) {
|
||
if (!active_state.empty()) return state == active_state;
|
||
if (tile_type == "climate") {
|
||
return !state.empty() && state != "off" && state != "unavailable" && state != "unknown";
|
||
}
|
||
if (tile_type == "scene" || tile_type == "script") return true;
|
||
return state == "on";
|
||
};
|
||
|
||
// Cover-aware active state: "closed" = off, anything else (open/opening/closing) = on
|
||
bool t1_on = (std::string("${TILE1_TYPE}") == "cover") ? (id(tile1_cover_state).state != "closed") : is_active("${TILE1_TYPE}", std::string(id(ha_state_tile1).state.c_str()), "${TILE1_ACTIVE_STATE}");
|
||
bool t2_on = (std::string("${TILE2_TYPE}") == "cover") ? (id(tile2_cover_state).state != "closed") : is_active("${TILE2_TYPE}", std::string(id(ha_state_tile2).state.c_str()), "${TILE2_ACTIVE_STATE}");
|
||
bool t3_on = (std::string("${TILE3_TYPE}") == "cover") ? (id(tile3_cover_state).state != "closed") : is_active("${TILE3_TYPE}", std::string(id(ha_state_tile3).state.c_str()), "${TILE3_ACTIVE_STATE}");
|
||
bool t4_on = (std::string("${TILE4_TYPE}") == "cover") ? (id(tile4_cover_state).state != "closed") : is_active("${TILE4_TYPE}", std::string(id(ha_state_tile4).state.c_str()), "${TILE4_ACTIVE_STATE}");
|
||
bool t5_on = (std::string("${TILE5_TYPE}") == "cover") ? (id(tile5_cover_state).state != "closed") : is_active("${TILE5_TYPE}", std::string(id(ha_state_tile5).state.c_str()), "${TILE5_ACTIVE_STATE}");
|
||
bool t6_on = (std::string("${TILE6_TYPE}") == "cover") ? (id(tile6_cover_state).state != "closed") : is_active("${TILE6_TYPE}", std::string(id(ha_state_tile6).state.c_str()), "${TILE6_ACTIVE_STATE}");
|
||
// Tegel 7/8 hebben geen cover_state-sensor, dus geen cover-tak nodig.
|
||
bool t7_on = is_active("${TILE7_TYPE}", std::string(id(ha_state_tile7).state.c_str()), "${TILE7_ACTIVE_STATE}");
|
||
bool t8_on = is_active("${TILE8_TYPE}", std::string(id(ha_state_tile8).state.c_str()), "${TILE8_ACTIVE_STATE}");
|
||
|
||
set_value_auto(id(t1_value), t1_on, "${TILE1_TYPE}", "${TILE1_VALUE_MODE}",
|
||
id(tile1_brightness).state, id(tile1_percentage).state, std::string(id(tile1_preset).state.c_str()),
|
||
id(tile1_climate_current_temperature).state, id(tile1_climate_target_temperature).state, id(tile1_climate_humidity).state,
|
||
"${TILE1_LABEL_ON}", "${TILE1_LABEL_OFF}");
|
||
|
||
set_value_auto(id(t2_value), t2_on, "${TILE2_TYPE}", "${TILE2_VALUE_MODE}",
|
||
id(tile2_brightness).state, id(tile2_percentage).state, std::string(id(tile2_preset).state.c_str()),
|
||
id(tile2_climate_current_temperature).state, id(tile2_climate_target_temperature).state, id(tile2_climate_humidity).state,
|
||
"${TILE2_LABEL_ON}", "${TILE2_LABEL_OFF}");
|
||
|
||
set_value_auto(id(t3_value), t3_on, "${TILE3_TYPE}", "${TILE3_VALUE_MODE}",
|
||
id(tile3_brightness).state, id(tile3_percentage).state, std::string(id(tile3_preset).state.c_str()),
|
||
id(tile3_climate_current_temperature).state, id(tile3_climate_target_temperature).state, id(tile3_climate_humidity).state,
|
||
"${TILE3_LABEL_ON}", "${TILE3_LABEL_OFF}");
|
||
|
||
set_value_auto(id(t4_value), t4_on, "${TILE4_TYPE}", "${TILE4_VALUE_MODE}",
|
||
id(tile4_brightness).state, id(tile4_percentage).state, std::string(id(tile4_preset).state.c_str()),
|
||
id(tile4_climate_current_temperature).state, id(tile4_climate_target_temperature).state, id(tile4_climate_humidity).state,
|
||
"${TILE4_LABEL_ON}", "${TILE4_LABEL_OFF}");
|
||
|
||
set_value_auto(id(t5_value), t5_on, "${TILE5_TYPE}", "${TILE5_VALUE_MODE}",
|
||
id(tile5_brightness).state, id(tile5_percentage).state, std::string(id(tile5_preset).state.c_str()),
|
||
id(tile5_climate_current_temperature).state, id(tile5_climate_target_temperature).state, id(tile5_climate_humidity).state,
|
||
"${TILE5_LABEL_ON}", "${TILE5_LABEL_OFF}");
|
||
|
||
set_value_auto(id(t6_value), t6_on, "${TILE6_TYPE}", "${TILE6_VALUE_MODE}",
|
||
id(tile6_brightness).state, id(tile6_percentage).state, std::string(id(tile6_preset).state.c_str()),
|
||
id(tile6_climate_current_temperature).state, id(tile6_climate_target_temperature).state, id(tile6_climate_humidity).state,
|
||
"${TILE6_LABEL_ON}", "${TILE6_LABEL_OFF}");
|
||
|
||
// Tegel 7: light met alleen brightness. Tegel 8: puur tekst (Vol/Leeg).
|
||
set_value_auto(id(t7_value), t7_on, "${TILE7_TYPE}", "${TILE7_VALUE_MODE}",
|
||
id(tile7_brightness).state, NAN, std::string(),
|
||
NAN, NAN, NAN,
|
||
"${TILE7_LABEL_ON}", "${TILE7_LABEL_OFF}");
|
||
|
||
set_value_auto(id(t8_value), t8_on, "${TILE8_TYPE}", "${TILE8_VALUE_MODE}",
|
||
NAN, NAN, std::string(),
|
||
NAN, NAN, NAN,
|
||
"${TILE8_LABEL_ON}", "${TILE8_LABEL_OFF}");
|
||
|
||
if (id(active_control_kind) == "climate_temperature" && !lv_obj_has_flag(id(climate_detail_overlay), LV_OBJ_FLAG_HIDDEN)) {
|
||
auto label_for_climate_mode = [](std::string mode) {
|
||
for (char &c : mode) {
|
||
if (c >= 'A' && c <= 'Z') c = (char) (c - 'A' + 'a');
|
||
}
|
||
if (mode == "off") return std::string("Off");
|
||
if (mode == "heat") return std::string("Heat");
|
||
if (mode == "cool") return std::string("Cool");
|
||
if (mode == "heat_cool") return std::string("Heat/Cool");
|
||
if (mode == "auto") return std::string("Auto");
|
||
if (mode == "dry") return std::string("Dry");
|
||
if (mode == "fan_only") return std::string("Fan");
|
||
return mode.empty() ? std::string("Auto") : mode;
|
||
};
|
||
auto sync_open_climate = [&](const std::string& entity,
|
||
float current_temperature,
|
||
float target_temperature,
|
||
float humidity,
|
||
const std::string& climate_mode) {
|
||
if (id(active_entity) != entity) return;
|
||
auto to_x10 = [](float value, int fallback) -> int {
|
||
if (isnan(value)) return fallback;
|
||
return (int) lround(value * 10.0f);
|
||
};
|
||
|
||
id(active_climate_current_x10) = to_x10(current_temperature, id(active_climate_current_x10));
|
||
id(active_climate_humidity) = isnan(humidity) ? -1 : (int) lround(humidity);
|
||
if (!climate_mode.empty()) {
|
||
id(active_climate_mode) = climate_mode;
|
||
lv_label_set_text(id(climate_mode_label), label_for_climate_mode(climate_mode).c_str());
|
||
}
|
||
|
||
int target_x10 = to_x10(target_temperature, id(active_climate_target_x10));
|
||
if (target_x10 < id(active_climate_min_x10)) target_x10 = id(active_climate_min_x10);
|
||
if (target_x10 > id(active_climate_max_x10)) target_x10 = id(active_climate_max_x10);
|
||
int step_x10 = id(active_climate_step_x10);
|
||
if (step_x10 < 5) step_x10 = 5;
|
||
target_x10 = id(active_climate_min_x10) + (int) lround((target_x10 - id(active_climate_min_x10)) / (float) step_x10) * step_x10;
|
||
if (target_x10 < id(active_climate_min_x10)) target_x10 = id(active_climate_min_x10);
|
||
if (target_x10 > id(active_climate_max_x10)) target_x10 = id(active_climate_max_x10);
|
||
id(active_climate_target_x10) = target_x10;
|
||
|
||
char current_buf[48];
|
||
if (isnan(current_temperature)) {
|
||
snprintf(current_buf, sizeof(current_buf), "Current: --");
|
||
} else if (id(active_climate_humidity) >= 0) {
|
||
snprintf(current_buf, sizeof(current_buf), "Current: %.1f° - Humidity: %d %%", current_temperature, id(active_climate_humidity));
|
||
} else {
|
||
snprintf(current_buf, sizeof(current_buf), "Current: %.1f°", current_temperature);
|
||
}
|
||
lv_label_set_text(id(climate_current_label), current_buf);
|
||
|
||
char target_buf[16];
|
||
snprintf(target_buf, sizeof(target_buf), "%.1f°", id(active_climate_target_x10) / 10.0f);
|
||
lv_label_set_text(id(climate_target_label), target_buf);
|
||
lv_arc_set_value(id(climate_arc), id(active_climate_target_x10));
|
||
};
|
||
|
||
if (std::string("${TILE1_TYPE}") == "climate") sync_open_climate("${TILE1_ENTITY}", id(tile1_climate_current_temperature).state, id(tile1_climate_target_temperature).state, id(tile1_climate_humidity).state, std::string(id(tile1_climate_mode).state.c_str()));
|
||
if (std::string("${TILE2_TYPE}") == "climate") sync_open_climate("${TILE2_ENTITY}", id(tile2_climate_current_temperature).state, id(tile2_climate_target_temperature).state, id(tile2_climate_humidity).state, std::string(id(tile2_climate_mode).state.c_str()));
|
||
if (std::string("${TILE3_TYPE}") == "climate") sync_open_climate("${TILE3_ENTITY}", id(tile3_climate_current_temperature).state, id(tile3_climate_target_temperature).state, id(tile3_climate_humidity).state, std::string(id(tile3_climate_mode).state.c_str()));
|
||
if (std::string("${TILE4_TYPE}") == "climate") sync_open_climate("${TILE4_ENTITY}", id(tile4_climate_current_temperature).state, id(tile4_climate_target_temperature).state, id(tile4_climate_humidity).state, std::string(id(tile4_climate_mode).state.c_str()));
|
||
if (std::string("${TILE5_TYPE}") == "climate") sync_open_climate("${TILE5_ENTITY}", id(tile5_climate_current_temperature).state, id(tile5_climate_target_temperature).state, id(tile5_climate_humidity).state, std::string(id(tile5_climate_mode).state.c_str()));
|
||
if (std::string("${TILE6_TYPE}") == "climate") sync_open_climate("${TILE6_ENTITY}", id(tile6_climate_current_temperature).state, id(tile6_climate_target_temperature).state, id(tile6_climate_humidity).state, std::string(id(tile6_climate_mode).state.c_str()));
|
||
}
|
||
|
||
{
|
||
auto now = id(homeassistant_time).now();
|
||
char buf[10];
|
||
|
||
if (std::string("${TIME_24H}") == "true") {
|
||
snprintf(buf, sizeof(buf), "%02d:%02d", now.hour, now.minute);
|
||
} else {
|
||
int hour12 = now.hour % 12;
|
||
if (hour12 == 0) hour12 = 12;
|
||
snprintf(buf, sizeof(buf), "%d:%02d %s", hour12, now.minute, now.hour >= 12 ? "PM" : "AM");
|
||
}
|
||
|
||
set_lbl(id(lbl_time), std::string(buf));
|
||
}
|
||
|
||
// Accentkleur uit het Home Assistant-domein: het stuk voor de punt in
|
||
// het entity_id. script/scene/button vuur je af (ACCENT_ACTION), een
|
||
// light is een light (ACCENT_LIGHT), de rest is apparaat.
|
||
// LET OP: deze helper staat twee keer in dit bestand — hier en in
|
||
// open_value_overlay. Pas ze samen aan.
|
||
auto accent_for = [](const std::string& entity_id) -> uint32_t {
|
||
std::string d = entity_id.substr(0, entity_id.find('.'));
|
||
if (d == "script" || d == "scene" || d == "button" ||
|
||
d == "input_button" || d == "automation") return ${ACCENT_ACTION};
|
||
if (d == "light") return ${ACCENT_LIGHT};
|
||
return ${ACCENT_DEVICE};
|
||
};
|
||
|
||
auto apply_tile = [&](bool active,
|
||
const std::string& entity_id,
|
||
lv_obj_t* tile,
|
||
lv_obj_t* circle,
|
||
lv_obj_t* title,
|
||
lv_obj_t* value,
|
||
lv_obj_t* icon_lbl,
|
||
uint32_t circle_active,
|
||
uint32_t circle_disabled,
|
||
uint32_t icon_active,
|
||
uint32_t icon_disabled,
|
||
uint32_t bg_active,
|
||
uint32_t bg_disabled,
|
||
uint32_t title_active,
|
||
uint32_t title_disabled,
|
||
uint32_t value_active,
|
||
uint32_t value_disabled)
|
||
{
|
||
// ${ACCENT_AUTO} = "bepaal zelf"; een echte hexwaarde wint altijd.
|
||
uint32_t accent = accent_for(entity_id);
|
||
if (circle_active == ${ACCENT_AUTO}) circle_active = accent;
|
||
if (icon_disabled == ${ACCENT_AUTO}) icon_disabled = accent;
|
||
|
||
lv_obj_set_style_bg_color(tile, lv_color_hex(active ? bg_active : bg_disabled), 0);
|
||
lv_obj_set_style_bg_color(circle, lv_color_hex(active ? circle_active : circle_disabled), 0);
|
||
lv_obj_set_style_text_color(icon_lbl, lv_color_hex(active ? icon_active : icon_disabled), 0);
|
||
lv_obj_set_style_text_color(title, lv_color_hex(active ? title_active : title_disabled), 0);
|
||
lv_obj_set_style_text_color(value, lv_color_hex(active ? value_active : value_disabled), 0);
|
||
};
|
||
|
||
apply_tile(t1_on, "${TILE1_ENTITY}",
|
||
id(tile1), id(tile1_icon_circle), id(t1_title), id(t1_value), id(tile1_icon_lbl),
|
||
${TILE1_CIRCLE_ACTIVE_COLOR}, ${TILE1_CIRCLE_DISABLED_COLOR},
|
||
${TILE1_ICON_ACTIVE_COLOR}, ${TILE1_ICON_DISABLED_COLOR},
|
||
${TILE1_BG_ACTIVE_COLOR}, ${TILE1_BG_DISABLED_COLOR},
|
||
${TILE1_TITLE_ACTIVE_COLOR}, ${TILE1_TITLE_DISABLED_COLOR},
|
||
${TILE1_VALUE_ACTIVE_COLOR}, ${TILE1_VALUE_DISABLED_COLOR});
|
||
|
||
apply_tile(t2_on, "${TILE2_ENTITY}",
|
||
id(tile2), id(tile2_icon_circle), id(t2_title), id(t2_value), id(tile2_icon_lbl),
|
||
${TILE2_CIRCLE_ACTIVE_COLOR}, ${TILE2_CIRCLE_DISABLED_COLOR},
|
||
${TILE2_ICON_ACTIVE_COLOR}, ${TILE2_ICON_DISABLED_COLOR},
|
||
${TILE2_BG_ACTIVE_COLOR}, ${TILE2_BG_DISABLED_COLOR},
|
||
${TILE2_TITLE_ACTIVE_COLOR}, ${TILE2_TITLE_DISABLED_COLOR},
|
||
${TILE2_VALUE_ACTIVE_COLOR}, ${TILE2_VALUE_DISABLED_COLOR});
|
||
|
||
apply_tile(t3_on, "${TILE3_ENTITY}",
|
||
id(tile3), id(tile3_icon_circle), id(t3_title), id(t3_value), id(tile3_icon_lbl),
|
||
${TILE3_CIRCLE_ACTIVE_COLOR}, ${TILE3_CIRCLE_DISABLED_COLOR},
|
||
${TILE3_ICON_ACTIVE_COLOR}, ${TILE3_ICON_DISABLED_COLOR},
|
||
${TILE3_BG_ACTIVE_COLOR}, ${TILE3_BG_DISABLED_COLOR},
|
||
${TILE3_TITLE_ACTIVE_COLOR}, ${TILE3_TITLE_DISABLED_COLOR},
|
||
${TILE3_VALUE_ACTIVE_COLOR}, ${TILE3_VALUE_DISABLED_COLOR});
|
||
|
||
apply_tile(t4_on, "${TILE4_ENTITY}",
|
||
id(tile4), id(tile4_icon_circle), id(t4_title), id(t4_value), id(tile4_icon_lbl),
|
||
${TILE4_CIRCLE_ACTIVE_COLOR}, ${TILE4_CIRCLE_DISABLED_COLOR},
|
||
${TILE4_ICON_ACTIVE_COLOR}, ${TILE4_ICON_DISABLED_COLOR},
|
||
${TILE4_BG_ACTIVE_COLOR}, ${TILE4_BG_DISABLED_COLOR},
|
||
${TILE4_TITLE_ACTIVE_COLOR}, ${TILE4_TITLE_DISABLED_COLOR},
|
||
${TILE4_VALUE_ACTIVE_COLOR}, ${TILE4_VALUE_DISABLED_COLOR});
|
||
|
||
apply_tile(t5_on, "${TILE5_ENTITY}",
|
||
id(tile5), id(tile5_icon_circle), id(t5_title), id(t5_value), id(tile5_icon_lbl),
|
||
${TILE5_CIRCLE_ACTIVE_COLOR}, ${TILE5_CIRCLE_DISABLED_COLOR},
|
||
${TILE5_ICON_ACTIVE_COLOR}, ${TILE5_ICON_DISABLED_COLOR},
|
||
${TILE5_BG_ACTIVE_COLOR}, ${TILE5_BG_DISABLED_COLOR},
|
||
${TILE5_TITLE_ACTIVE_COLOR}, ${TILE5_TITLE_DISABLED_COLOR},
|
||
${TILE5_VALUE_ACTIVE_COLOR}, ${TILE5_VALUE_DISABLED_COLOR});
|
||
|
||
apply_tile(t6_on, "${TILE6_ENTITY}",
|
||
id(tile6), id(tile6_icon_circle), id(t6_title), id(t6_value), id(tile6_icon_lbl),
|
||
${TILE6_CIRCLE_ACTIVE_COLOR}, ${TILE6_CIRCLE_DISABLED_COLOR},
|
||
${TILE6_ICON_ACTIVE_COLOR}, ${TILE6_ICON_DISABLED_COLOR},
|
||
${TILE6_BG_ACTIVE_COLOR}, ${TILE6_BG_DISABLED_COLOR},
|
||
${TILE6_TITLE_ACTIVE_COLOR}, ${TILE6_TITLE_DISABLED_COLOR},
|
||
${TILE6_VALUE_ACTIVE_COLOR}, ${TILE6_VALUE_DISABLED_COLOR});
|
||
|
||
apply_tile(t7_on, "${TILE7_ENTITY}",
|
||
id(tile7), id(tile7_icon_circle), id(t7_title), id(t7_value), id(tile7_icon_lbl),
|
||
${TILE7_CIRCLE_ACTIVE_COLOR}, ${TILE7_CIRCLE_DISABLED_COLOR},
|
||
${TILE7_ICON_ACTIVE_COLOR}, ${TILE7_ICON_DISABLED_COLOR},
|
||
${TILE7_BG_ACTIVE_COLOR}, ${TILE7_BG_DISABLED_COLOR},
|
||
${TILE7_TITLE_ACTIVE_COLOR}, ${TILE7_TITLE_DISABLED_COLOR},
|
||
${TILE7_VALUE_ACTIVE_COLOR}, ${TILE7_VALUE_DISABLED_COLOR});
|
||
|
||
apply_tile(t8_on, "${TILE8_ENTITY}",
|
||
id(tile8), id(tile8_icon_circle), id(t8_title), id(t8_value), id(tile8_icon_lbl),
|
||
${TILE8_CIRCLE_ACTIVE_COLOR}, ${TILE8_CIRCLE_DISABLED_COLOR},
|
||
${TILE8_ICON_ACTIVE_COLOR}, ${TILE8_ICON_DISABLED_COLOR},
|
||
${TILE8_BG_ACTIVE_COLOR}, ${TILE8_BG_DISABLED_COLOR},
|
||
${TILE8_TITLE_ACTIVE_COLOR}, ${TILE8_TITLE_DISABLED_COLOR},
|
||
${TILE8_VALUE_ACTIVE_COLOR}, ${TILE8_VALUE_DISABLED_COLOR});
|
||
|
||
- component.update: my_display
|