Let the screen own its settings, answer its pings, and show them as cards (0.2.57)
A code check found that a setting changed on a screen could jump back. ESP Screen Manager kept its own copy and sent it with every full update: after a restart of the app or Home Assistant, every hour and after every save. A change it missed while it or Home Assistant restarted came back to the old value, a reported setting was lost when save() refused a layout with a removed entity, and every step of a held - or + on the screen resent the whole screen. Firmware 0.2.49 offers every setting as an entity over the existing preference records (no migration): Night mode, Night starts/ends, 24-hour clock, Back to page 1 and after how long, Back to page 1 on standby, Swipe between pages and, on the Guition, Rotation, next to the four numbers and Auto standby. The settings page and every entity write through settings_screen::set(), which clamps like the page and stores, applies and reports only a real change. screen_message answers a caller that asks with its status and layout revision (api.respond). The settings tile opens without Home Assistant. The app reads and changes the settings of such a screen through its entities and leaves them out of the layout message. Older firmware keeps them with the layout; a setting it reports is stored without the tile check and not sent back. Pings to screens that can answer learn at once whether a tile is missing and resend after 30 s instead of 120 s. Layout sensors come back after a Home Assistant restart, and weather tiles only ask for the forecasts the entity supports. The editor's settings form is now three cards with the rows of the screen's own page; a change applies at once, and an offline screen shows its values as unknown. Checked with 245 Python and 14 C++ tests, generate_packages --check, ESP32 builds of both profiles, host-build API tests on both boards, and a live run against Home Assistant 2026.9.1 with a temporary host device. Firmware 0.2.49. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+135
-61
@@ -17,7 +17,7 @@
|
||||
############################################################
|
||||
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.48"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.49"
|
||||
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
|
||||
TOUCH_MOVE_LIMIT_PX: "56"
|
||||
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
|
||||
@@ -563,11 +563,23 @@ api:
|
||||
reason: "remote"
|
||||
# ESP Screen Manager 0.2.39+ hands a whole tile message over in one call instead of
|
||||
# base64 chunks in the text inbox; the inbox keeps showing the result.
|
||||
# Firmware 0.2.49+ also answers a caller that asks for it (app 0.2.57+ does for its pings): the same
|
||||
# status as the inbox, plus the revision of the layout on the screen. A call without a call id
|
||||
# (older apps) gets no answer, so ESPHome has nothing to warn about.
|
||||
- action: screen_message
|
||||
supports_response: optional
|
||||
variables:
|
||||
message: string
|
||||
then:
|
||||
- lambda: 'id(dashboard_inbox).publish_state(runtime_tiles::receive(message));'
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return call_id != 0;'
|
||||
then:
|
||||
- api.respond:
|
||||
data: !lambda |-
|
||||
root["status"] = id(dashboard_inbox).state;
|
||||
root["rev"] = runtime_tiles::layout_rev;
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
|
||||
@@ -1176,10 +1188,13 @@ text_sensor:
|
||||
switch:
|
||||
- platform: restart
|
||||
name: "Restart"
|
||||
# Auto standby, the same setting as the checkbox in ESP Screens (firmware 0.2.41+), so Home Assistant
|
||||
# automations can keep a screen on. Off wakes a screen the standby time dimmed and keeps it on (a Sleep
|
||||
# holds until a tap, Wake or an alert); on counts the standby time from that moment. Nothing runs at
|
||||
# boot: the switch follows the saved setting.
|
||||
# The screen settings as Home Assistant entities (firmware 0.2.49+, Auto standby since 0.2.41). The screen
|
||||
# owns them: the settings page, these entities and ESP Screens all change them through
|
||||
# settings_screen::set(), which saves, applies and reports a real change only. The saved values keep
|
||||
# their preference records. A template switch reads its lambda every loop and publishes a change itself;
|
||||
# the numbers and times are published by apply_screen_settings. Auto standby off wakes a screen the
|
||||
# standby time dimmed (a Sleep holds until a tap, Wake or an alert); on counts the standby time from that
|
||||
# moment. Nothing runs at boot: every switch follows the saved setting.
|
||||
- platform: template
|
||||
id: setting_auto_standby
|
||||
name: "Auto standby"
|
||||
@@ -1189,21 +1204,96 @@ switch:
|
||||
lambda: return screen_settings::current.standby_enabled;
|
||||
turn_on_action:
|
||||
- lambda: |-
|
||||
auto &s = screen_settings::current;
|
||||
id(last_touch_ms) = millis();
|
||||
if (s.standby_enabled) return;
|
||||
s.standby_enabled = true;
|
||||
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
|
||||
runtime_tiles::setting_event("standby_enabled", 1);
|
||||
id(apply_screen_settings).execute();
|
||||
settings_screen::set("standby_enabled", 1);
|
||||
turn_off_action:
|
||||
- lambda: |-
|
||||
auto &s = screen_settings::current;
|
||||
if (!s.standby_enabled) return;
|
||||
s.standby_enabled = false;
|
||||
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
|
||||
runtime_tiles::setting_event("standby_enabled", 0);
|
||||
id(apply_screen_settings).execute();
|
||||
- lambda: 'settings_screen::set("standby_enabled", 0);'
|
||||
- platform: template
|
||||
id: setting_night_mode
|
||||
name: "Night mode"
|
||||
icon: "mdi:weather-night"
|
||||
entity_category: config
|
||||
restore_mode: DISABLED
|
||||
lambda: return screen_settings::current.night_enabled;
|
||||
turn_on_action:
|
||||
- lambda: 'settings_screen::set("night_enabled", 1);'
|
||||
turn_off_action:
|
||||
- lambda: 'settings_screen::set("night_enabled", 0);'
|
||||
- platform: template
|
||||
id: setting_clock_24h
|
||||
name: "24-hour clock"
|
||||
icon: "mdi:clock-outline"
|
||||
entity_category: config
|
||||
restore_mode: DISABLED
|
||||
lambda: return screen_settings::current.clock_24h;
|
||||
turn_on_action:
|
||||
- lambda: 'settings_screen::set("clock_24h", 1);'
|
||||
turn_off_action:
|
||||
- lambda: 'settings_screen::set("clock_24h", 0);'
|
||||
- platform: template
|
||||
id: setting_auto_home
|
||||
name: "Back to page 1"
|
||||
icon: "mdi:home-import-outline"
|
||||
entity_category: config
|
||||
restore_mode: DISABLED
|
||||
lambda: return settings_screen::auto_home != 0;
|
||||
turn_on_action:
|
||||
- lambda: 'settings_screen::set("auto_home", 1);'
|
||||
turn_off_action:
|
||||
- lambda: 'settings_screen::set("auto_home", 0);'
|
||||
- platform: template
|
||||
id: setting_home_on_standby
|
||||
name: "Back to page 1 on standby"
|
||||
icon: "mdi:home-import-outline"
|
||||
entity_category: config
|
||||
restore_mode: DISABLED
|
||||
lambda: return screen_settings::current.home_on_standby;
|
||||
turn_on_action:
|
||||
- lambda: 'settings_screen::set("home_on_standby", 1);'
|
||||
turn_off_action:
|
||||
- lambda: 'settings_screen::set("home_on_standby", 0);'
|
||||
- platform: template
|
||||
id: setting_swipe_pages
|
||||
name: "Swipe between pages"
|
||||
icon: "mdi:gesture-swipe-horizontal"
|
||||
entity_category: config
|
||||
restore_mode: DISABLED
|
||||
lambda: return settings_screen::swipe_pages != 0;
|
||||
turn_on_action:
|
||||
- lambda: 'settings_screen::set("swipe_pages", 1);'
|
||||
turn_off_action:
|
||||
- lambda: 'settings_screen::set("swipe_pages", 0);'
|
||||
|
||||
# Night hours as Home Assistant times (firmware 0.2.49+); the screen keeps minutes since midnight.
|
||||
datetime:
|
||||
- platform: template
|
||||
type: time
|
||||
id: setting_night_start
|
||||
name: "Night starts"
|
||||
icon: "mdi:weather-sunset-down"
|
||||
entity_category: config
|
||||
update_interval: never
|
||||
lambda: |-
|
||||
ESPTime time{};
|
||||
time.hour = screen_settings::current.night_start / 60;
|
||||
time.minute = screen_settings::current.night_start % 60;
|
||||
return time;
|
||||
set_action:
|
||||
- lambda: 'settings_screen::set("night_start", x.hour * 60 + x.minute);'
|
||||
- platform: template
|
||||
type: time
|
||||
id: setting_night_end
|
||||
name: "Night ends"
|
||||
icon: "mdi:weather-sunset-up"
|
||||
entity_category: config
|
||||
update_interval: never
|
||||
lambda: |-
|
||||
ESPTime time{};
|
||||
time.hour = screen_settings::current.night_end / 60;
|
||||
time.minute = screen_settings::current.night_end % 60;
|
||||
return time;
|
||||
set_action:
|
||||
- lambda: 'settings_screen::set("night_end", x.hour * 60 + x.minute);'
|
||||
|
||||
# Two SPI buses used by the CYD:
|
||||
# - lcd: built-in display
|
||||
@@ -3610,11 +3700,17 @@ script:
|
||||
id(back_light).turn_on().set_transition_length(300).set_brightness(level / 100.0f).perform();
|
||||
if (id(display_dimmed)) { lv_obj_clear_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN); lv_obj_move_foreground(id(dim_wake_overlay)); }
|
||||
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
// The four setting entities only publish a changed value (HA keeps identical states anyway).
|
||||
// The setting numbers and times only publish a changed value (HA keeps identical states anyway); the
|
||||
// switches publish their own changes. An open settings page shows a change from Home Assistant too.
|
||||
if (id(setting_brightness).state != settings.brightness) id(setting_brightness).publish_state(settings.brightness);
|
||||
if (id(setting_standby_brightness).state != settings.standby_brightness) id(setting_standby_brightness).publish_state(settings.standby_brightness);
|
||||
if (id(setting_night_brightness).state != settings.night_brightness) id(setting_night_brightness).publish_state(settings.night_brightness);
|
||||
if (id(setting_standby_seconds).state != settings.standby_seconds) id(setting_standby_seconds).publish_state(settings.standby_seconds);
|
||||
if (id(setting_auto_home_seconds).state != settings_screen::auto_home_seconds) id(setting_auto_home_seconds).publish_state(settings_screen::auto_home_seconds);
|
||||
for (auto [entity, minutes] : {std::make_pair(id(setting_night_start), settings.night_start), std::make_pair(id(setting_night_end), settings.night_end)}) {
|
||||
if (!entity->has_state() || entity->hour * 60 + entity->minute != minutes) entity->update();
|
||||
}
|
||||
settings_screen::refresh();
|
||||
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
|
||||
|
||||
# Every card closes through runtime_tiles::dismiss (set in on_boot): the runtime detail card, the light,
|
||||
@@ -4537,6 +4633,8 @@ button:
|
||||
- script.execute: dim_display
|
||||
|
||||
number:
|
||||
# Firmware 0.2.49+: every number goes through settings_screen::set(), like the settings page, so a value the
|
||||
# screen already has writes and reports nothing (an automation may set it on every light change).
|
||||
- platform: template
|
||||
id: setting_brightness
|
||||
name: "Normal brightness"
|
||||
@@ -4548,18 +4646,7 @@ number:
|
||||
update_interval: never
|
||||
lambda: return screen_settings::current.brightness;
|
||||
set_action:
|
||||
- lambda: |-
|
||||
auto &s = screen_settings::current;
|
||||
const auto before = s;
|
||||
s.brightness = (int)x;
|
||||
s.standby_brightness = std::min(s.standby_brightness,s.brightness);
|
||||
s.night_brightness = std::min(s.night_brightness,s.brightness);
|
||||
// The value the screen already has (an automation that sets it on every light change):
|
||||
// no flash write, no event, so ESP Screen Manager does not resend the whole screen.
|
||||
if (s == before) return;
|
||||
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
|
||||
runtime_tiles::setting_event("brightness",s.brightness);
|
||||
id(apply_screen_settings).execute();
|
||||
- lambda: 'settings_screen::set("brightness", (int32_t) lround(x));'
|
||||
- platform: template
|
||||
id: setting_standby_brightness
|
||||
name: "Standby brightness"
|
||||
@@ -4571,16 +4658,7 @@ number:
|
||||
update_interval: never
|
||||
lambda: return screen_settings::current.standby_brightness;
|
||||
set_action:
|
||||
- lambda: |-
|
||||
auto &s = screen_settings::current;
|
||||
const auto before = s;
|
||||
s.standby_brightness = (int)x;
|
||||
s.standby_brightness = std::min(s.standby_brightness,s.brightness);
|
||||
s.night_brightness = std::min(s.night_brightness,s.brightness);
|
||||
if (s == before) return;
|
||||
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
|
||||
runtime_tiles::setting_event("standby_brightness",s.standby_brightness);
|
||||
id(apply_screen_settings).execute();
|
||||
- lambda: 'settings_screen::set("standby_brightness", (int32_t) lround(x));'
|
||||
- platform: template
|
||||
id: setting_night_brightness
|
||||
name: "Night brightness"
|
||||
@@ -4592,16 +4670,7 @@ number:
|
||||
update_interval: never
|
||||
lambda: return screen_settings::current.night_brightness;
|
||||
set_action:
|
||||
- lambda: |-
|
||||
auto &s = screen_settings::current;
|
||||
const auto before = s;
|
||||
s.night_brightness = (int)x;
|
||||
s.standby_brightness = std::min(s.standby_brightness,s.brightness);
|
||||
s.night_brightness = std::min(s.night_brightness,s.brightness);
|
||||
if (s == before) return;
|
||||
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
|
||||
runtime_tiles::setting_event("night_brightness",s.night_brightness);
|
||||
id(apply_screen_settings).execute();
|
||||
- lambda: 'settings_screen::set("night_brightness", (int32_t) lround(x));'
|
||||
- platform: template
|
||||
id: setting_standby_seconds
|
||||
name: "Standby after"
|
||||
@@ -4613,13 +4682,18 @@ number:
|
||||
update_interval: never
|
||||
lambda: return screen_settings::current.standby_seconds;
|
||||
set_action:
|
||||
- lambda: |-
|
||||
auto &s = screen_settings::current;
|
||||
const auto before = s;
|
||||
s.standby_seconds = (int)x;
|
||||
s.standby_brightness = std::min(s.standby_brightness,s.brightness);
|
||||
s.night_brightness = std::min(s.night_brightness,s.brightness);
|
||||
if (s == before) return;
|
||||
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
|
||||
runtime_tiles::setting_event("standby_seconds",s.standby_seconds);
|
||||
id(apply_screen_settings).execute();
|
||||
- lambda: 'settings_screen::set("standby_seconds", (int32_t) lround(x));'
|
||||
- platform: template
|
||||
id: setting_auto_home_seconds
|
||||
name: "Back to page 1 after"
|
||||
icon: "mdi:timer-outline"
|
||||
entity_category: config
|
||||
min_value: 30
|
||||
max_value: 3600
|
||||
step: 30
|
||||
mode: box
|
||||
unit_of_measurement: "s"
|
||||
update_interval: never
|
||||
lambda: return settings_screen::auto_home_seconds;
|
||||
set_action:
|
||||
- lambda: 'settings_screen::set("auto_home_seconds", (int32_t) lround(x));'
|
||||
|
||||
Reference in New Issue
Block a user