Add persistent per-screen brightness, standby and clock settings
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "runtime_model.h"
|
||||
#include "screen_settings.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "cyd_ui.h"
|
||||
#include "light_controls.h"
|
||||
#include "esphome/components/json/json_util.h"
|
||||
@@ -14,7 +16,30 @@ inline bool enabled = false;
|
||||
inline Model model;
|
||||
inline int active_index = -1;
|
||||
inline uint32_t last_received = 0;
|
||||
inline std::function<void()> layout_changed, refresh, dismiss;
|
||||
inline std::function<void()> layout_changed, refresh, dismiss, settings_changed;
|
||||
inline esphome::ESPPreferenceObject settings_preference;
|
||||
inline void load_settings() {
|
||||
settings_preference = esphome::global_preferences->make_preference<screen_settings::Settings>(0x53435231);
|
||||
screen_settings::Settings saved;
|
||||
if (settings_preference.load(&saved) && saved.valid()) screen_settings::current = saved;
|
||||
}
|
||||
inline bool parse_settings(JsonObject obj, screen_settings::Settings &s) {
|
||||
// Require the complete known schema; validate before touching any runtime state.
|
||||
if (obj.size() != 11) return false;
|
||||
const char *flags[] = {"standby_enabled", "night_enabled", "show_clock", "clock_24h", "home_on_standby"};
|
||||
int32_t *flag_values[] = {&s.standby_enabled, &s.night_enabled, &s.show_clock, &s.clock_24h, &s.home_on_standby};
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (!obj[flags[i]].is<bool>()) return false;
|
||||
*flag_values[i] = obj[flags[i]].as<bool>();
|
||||
}
|
||||
const char *numbers[] = {"standby_seconds", "brightness", "standby_brightness", "night_start", "night_end", "night_brightness"};
|
||||
int32_t *values[] = {&s.standby_seconds, &s.brightness, &s.standby_brightness, &s.night_start, &s.night_end, &s.night_brightness};
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (!obj[numbers[i]].is<int>() || obj[numbers[i]].is<bool>()) return false;
|
||||
*values[i] = obj[numbers[i]].as<int>();
|
||||
}
|
||||
return s.valid();
|
||||
}
|
||||
inline std::function<void(Tile &)> detail, detail_update;
|
||||
struct Widgets { lv_obj_t *tile{}, *title{}, *value{}, *circle{}, *icon{}; size_t index{}; int cached_active = -1; };
|
||||
inline std::array<Widgets, MAX_TILES> widgets;
|
||||
@@ -48,6 +73,11 @@ inline std::string receive(const std::string &payload) {
|
||||
auto op = string(root["op"]);
|
||||
if (op == "layout") {
|
||||
if (!root["entities"].is<JsonArray>() || !root["title"].is<const char *>()) return false;
|
||||
auto settings = screen_settings::current;
|
||||
if (!root["settings"].isNull() && (!root["settings"].is<JsonObject>() ||
|
||||
!parse_settings(root["settings"].as<JsonObject>(), settings))) {
|
||||
result = "Fout: scherminstellingen"; return false;
|
||||
}
|
||||
std::vector<std::string> entities;
|
||||
for (JsonVariant entity : root["entities"].as<JsonArray>()) {
|
||||
if (!entity.is<const char *>() || entities.size() == MAX_TILES) return false;
|
||||
@@ -55,6 +85,11 @@ inline std::string receive(const std::string &payload) {
|
||||
}
|
||||
bool changed = false;
|
||||
if (!model.set_layout(entities, string(root["title"], 96), changed)) return false;
|
||||
if (!(settings == screen_settings::current)) {
|
||||
screen_settings::current = settings;
|
||||
settings_preference.save(&settings); // ESPHome batches flash writes; no write on keepalive.
|
||||
if (settings_changed) settings_changed();
|
||||
}
|
||||
if (changed) { active_index = -1; for (auto &w : widgets) w.cached_active = -1; if (dismiss) dismiss(); }
|
||||
last_received = esphome::millis();
|
||||
if (layout_changed) layout_changed();
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace screen_settings {
|
||||
// Stable preference format. Change its version/key only with a migration.
|
||||
struct Settings {
|
||||
uint32_t version = 1;
|
||||
int32_t standby_enabled = 1, standby_seconds = 600, brightness = 100;
|
||||
int32_t standby_brightness = 20, night_enabled = 1, night_start = 1320;
|
||||
int32_t night_end = 420, night_brightness = 10, show_clock = 1;
|
||||
int32_t clock_24h = 1, home_on_standby = 0;
|
||||
bool valid() const {
|
||||
return version == 1 && (standby_enabled == 0 || standby_enabled == 1) &&
|
||||
standby_seconds >= 60 && standby_seconds <= 86400 &&
|
||||
brightness >= 5 && brightness <= 100 && standby_brightness >= 0 &&
|
||||
standby_brightness <= brightness && (night_enabled == 0 || night_enabled == 1) &&
|
||||
night_start >= 0 && night_start < 1440 && night_end >= 0 && night_end < 1440 &&
|
||||
night_brightness >= 0 && night_brightness <= brightness &&
|
||||
(show_clock == 0 || show_clock == 1) && (clock_24h == 0 || clock_24h == 1) &&
|
||||
(home_on_standby == 0 || home_on_standby == 1);
|
||||
}
|
||||
bool operator==(const Settings &b) const {
|
||||
return version == b.version && standby_enabled == b.standby_enabled &&
|
||||
standby_seconds == b.standby_seconds && brightness == b.brightness &&
|
||||
standby_brightness == b.standby_brightness && night_enabled == b.night_enabled &&
|
||||
night_start == b.night_start && night_end == b.night_end &&
|
||||
night_brightness == b.night_brightness && show_clock == b.show_clock &&
|
||||
clock_24h == b.clock_24h && home_on_standby == b.home_on_standby;
|
||||
}
|
||||
bool night(int minute) const {
|
||||
if (!night_enabled || minute < 0 || minute >= 1440 || night_start == night_end) return false;
|
||||
return night_start < night_end ? minute >= night_start && minute < night_end
|
||||
: minute >= night_start || minute < night_end;
|
||||
}
|
||||
int dim_level(int minute) const { return night(minute) ? night_brightness : standby_brightness; }
|
||||
};
|
||||
inline Settings current;
|
||||
}
|
||||
@@ -162,3 +162,45 @@ een los openbaar portal.
|
||||
Gebruikte HA-mechanismen: [Ingress](https://developers.home-assistant.io/docs/apps/presentation/),
|
||||
[interne HA-API](https://developers.home-assistant.io/docs/apps/communication/) en
|
||||
[ESPHome-pakketten](https://esphome.io/components/packages/).
|
||||
|
||||
|
||||
## Scherminstellingen aanpassen (vanaf 0.1.2)
|
||||
|
||||
Update ESP Screen Manager naar 0.1.2 en installeer één keer de nieuwe firmware
|
||||
via je **bestaande** ESPHome-apparaat → Install → Wirelessly. Behoud de eigen YAML
|
||||
met wifi en sleutels. Open daarna het scherm in ESP Screen Manager en klap
|
||||
**Scherminstellingen** open. Klik na aanpassen op **Opslaan & naar scherm**.
|
||||
Hierna vragen wijzigingen aan deze instellingen geen nieuwe firmwareflash.
|
||||
|
||||
| Instelling | Mogelijkheden | Standaard |
|
||||
|---|---|---|
|
||||
| Automatisch standby | Aan/uit | Aan |
|
||||
| Standby na | 1–1440 minuten na de laatste aanraking | 10 minuten |
|
||||
| Helderheid normaal | 5–100% | 100% |
|
||||
| Helderheid standby | 0–100%, maximaal normale helderheid | 20% |
|
||||
| Nachtstand | Aan/uit; geldt tijdens standby | Aan |
|
||||
| Begin/einde nacht | Uur en minuut, ook over middernacht | 22:00–07:00 |
|
||||
| Helderheid nacht | 0–100%, maximaal normale helderheid | 10% |
|
||||
| Klok tonen | Aan/uit | Aan |
|
||||
| Tijdnotatie | 24 of 12 uur, zonder AM/PM | 24 uur |
|
||||
| Terug naar pagina 1 | Bij standby; anders huidige pagina onthouden | Uit |
|
||||
|
||||
Nachturen gebruiken de tijdzone van het ESPHome-apparaat en de tijd uit HA.
|
||||
Zonder geldige tijd gebruikt het scherm de gewone standbyhelderheid; gelijke
|
||||
begin- en eindtijd schakelen het nachtvenster uit. Op 0% gaat alleen de
|
||||
achtergrondverlichting uit: dit is geen deep sleep en geen schermbeveiliging.
|
||||
De eerste tik wekt het scherm zonder een apparaat te bedienen.
|
||||
|
||||
De add-on bewaart alles per scherm in zijn permanente gegevens. Het scherm bewaart
|
||||
de laatst ontvangen instellingen ook in preferences; ESPHome bundelt die
|
||||
schrijfacties (normaal maximaal een minuut). Trek daarom niet direct na opslaan
|
||||
de voeding los. Bestaande CYD-kalibratie, tegels, API- en OTA-sleutels blijven staan.
|
||||
Een offline scherm krijgt wijzigingen zodra het terugkomt. Gewone HA-statusupdates
|
||||
wekken het scherm niet en veranderen de standbytimer niet.
|
||||
|
||||
Bij oudere firmware toont de beheerpagina dat eerst een update nodig is.
|
||||
De tegels blijven bruikbaar. Handmatige YAML-profielen blijven hun substitutions
|
||||
gebruiken; voor beheer via deze app gebruik je het Easy Setup-pakket.
|
||||
|
||||
De gemelde Guition-paneelstrepen worden apart onderzocht. Deze instellingenupdate
|
||||
wijzigt geen displaytimings of driver en is geen bewezen oplossing voor die strepen.
|
||||
|
||||
@@ -56,3 +56,17 @@ Een tijdelijke ontwikkelserver ondersteunt `SCREEN_DEV=1`, `HA_API` (eindigt op
|
||||
`/api`), `HA_TOKEN_FILE` en `SCREEN_DATA`. Hij bindt alleen op localhost. Zet een
|
||||
token nooit in broncode, URL's of Git. Productie gebruikt Supervisor en accepteert
|
||||
alleen het Ingress-proxyadres; er is geen extra publieke poort.
|
||||
|
||||
|
||||
### Instellingencompatibiliteit 0.1.2
|
||||
|
||||
`settings` is een optioneel object binnen een schermindeling en binnen het bestaande
|
||||
`v: 1, op: layout`-bericht. Een ontbrekend object behoudt eerder gedrag. Oude
|
||||
firmware negeert dit extra veld en blijft tegels ontvangen. De beheerpagina meldt
|
||||
dat nieuwe firmware nodig is. Een oude browser die alleen tegels opslaat, wist
|
||||
opgeslagen instellingen niet. Onbekende instellingen worden geweigerd.
|
||||
|
||||
Firmware gebruikt een afzonderlijke preferences-key `0x53435231` met een vaste
|
||||
versie-1 structuur (elf int32-velden en een uint32-versie). Wijzig key, structuur
|
||||
of versie niet zonder migratie. Alleen gewijzigde instellingen worden opgeslagen;
|
||||
periodieke herhaling veroorzaakt geen flashwrites en geen nieuwe idle-timer.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Scherminstellingen 0.1.2 — 12 september 2026
|
||||
|
||||
- 33 Python-tests geslaagd: invoergrenzen, oude indelingen, behoud van instellingen
|
||||
bij herstart en opslaan vanuit een oudere UI, protocol en HTTP/CSRF.
|
||||
- Zeven native C++-tests geslaagd, inclusief grenzen, nachttijden over middernacht,
|
||||
ontbreken van tijdsync en ongeldige opgeslagen settings.
|
||||
- Easy Setup-firmware voor CYD en Guition gebouwd met ESPHome 2026.6.2.
|
||||
- Beide handmatige YAML-profielen gevalideerd en volledig gebouwd.
|
||||
- Browser: standby 20 minuten opslaan en herladen; helderheid naar minimum zetten
|
||||
verlaagt hogere dimniveaus mee; waarden blijven behouden na herladen.
|
||||
- Guition via bestaande versleutelde OTA bijgewerkt naar 0.1.2.
|
||||
- Op het echte scherm: 55% helderheid direct toegepast; eigenaar bevestigt fysiek
|
||||
dimmen. Standby na 60 seconden ondanks herhaalde indelingsberichten. Ongeldige
|
||||
helderheid 0 geweigerd zonder de geldige instellingen te veranderen.
|
||||
- Guition herstart: 55%, 60 seconden en verborgen klok uit preferences teruggelezen.
|
||||
Daarna oorspronkelijke instellingen teruggezet (100%, 600 seconden).
|
||||
|
||||
CYD was niet aangesloten; de fysieke instellingenacceptatie betreft alleen Guition.
|
||||
Dit is geen lange stabiliteitstest en geen oplossing voor de bestaande paneelstrepen.
|
||||
De RGB-driver is bytegelijk aan de oorspronkelijke ESPHome-driver; experimentele
|
||||
wijzigingen aan timing, spiegeling en reset zijn niet onderdeel van deze release.
|
||||
+52
-59
@@ -582,7 +582,7 @@ esphome:
|
||||
min_version: 2026.6.2
|
||||
project:
|
||||
name: maxgramser.esp_screen
|
||||
version: "0.1.0"
|
||||
version: "0.1.2"
|
||||
platformio_options:
|
||||
build_flags:
|
||||
- -DLV_USE_SNAPSHOT=1
|
||||
@@ -603,8 +603,19 @@ esphome:
|
||||
lv_obj_set_style_pad_row(id(vacuum_speeds), 4, 0);
|
||||
lv_obj_set_style_pad_column(id(vacuum_speeds), 4, 0);
|
||||
- lambda: |-
|
||||
auto &settings = screen_settings::current;
|
||||
settings.standby_enabled = std::string("${AUTO_DIM}") == "true";
|
||||
settings.standby_seconds = atoi("${AUTO_DIM_TIMEOUT}");
|
||||
settings.brightness = atoi("${MAX_BRIGHTNESS}");
|
||||
settings.standby_brightness = atoi("${DIM_BRIGHTNESS}");
|
||||
settings.night_enabled = std::string("${NIGHT_MODE}") == "true";
|
||||
settings.night_start = atoi("${NIGHT_START_HOUR}") * 60;
|
||||
settings.night_end = atoi("${NIGHT_END_HOUR}") * 60;
|
||||
settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}");
|
||||
runtime_tiles::enabled = std::string("${DYNAMIC_TILES}") == "true";
|
||||
if (!runtime_tiles::enabled) return;
|
||||
runtime_tiles::load_settings();
|
||||
runtime_tiles::settings_changed = []() { id(apply_screen_settings).execute(); };
|
||||
runtime_tiles::dismiss = []() {
|
||||
id(active_entity).clear();
|
||||
lv_obj_add_flag(id(brightness_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -678,7 +689,7 @@ esphome:
|
||||
light_controls::commits[2] = [](int value) { id(color_brightness_commit).execute(value); };
|
||||
- light.turn_on:
|
||||
id: back_light
|
||||
brightness: !lambda "return atoi(\"${MAX_BRIGHTNESS}\") / 100.0f;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- script.execute: show_tile_page
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
- if:
|
||||
@@ -748,6 +759,12 @@ api:
|
||||
- action: ui_state
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &cfg = screen_settings::current;
|
||||
ESP_LOGI("ui_diag", "settings standby=%d timeout=%d brightness=%d dim=%d night=%d start=%d end=%d night_dim=%d clock=%d clock24=%d home=%d idle_ms=%u dimmed=%d",
|
||||
cfg.standby_enabled, cfg.standby_seconds, cfg.brightness, cfg.standby_brightness,
|
||||
cfg.night_enabled, cfg.night_start, cfg.night_end, cfg.night_brightness,
|
||||
cfg.show_clock, cfg.clock_24h, cfg.home_on_standby,
|
||||
(unsigned)(millis() - id(last_touch_ms)), id(display_dimmed));
|
||||
lv_area_t nav, grid;
|
||||
lv_obj_update_layout(id(home_page)->obj);
|
||||
lv_obj_get_coords(id(page_next), &nav);
|
||||
@@ -788,9 +805,9 @@ interval:
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
if (std::string("${AUTO_DIM}") != "true") return false;
|
||||
if (!screen_settings::current.standby_enabled) return false;
|
||||
if (id(display_dimmed) || id(touch_down) || id(calibration_active)) return false;
|
||||
return (millis() - id(last_touch_ms)) > (atoi("${AUTO_DIM_TIMEOUT}") * 1000UL);
|
||||
return (millis() - id(last_touch_ms)) > (screen_settings::current.standby_seconds * 1000UL);
|
||||
then:
|
||||
- script.execute: dim_display
|
||||
|
||||
@@ -801,33 +818,15 @@ time:
|
||||
update_interval: 60s
|
||||
on_time_sync:
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /1
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
|
||||
- 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: rendered_frames
|
||||
type: uint32_t
|
||||
@@ -1312,7 +1311,7 @@ text_sensor:
|
||||
- platform: template
|
||||
name: "Schermfirmware"
|
||||
entity_category: diagnostic
|
||||
lambda: 'return {"0.1.0"};'
|
||||
lambda: 'return {"0.1.2"};'
|
||||
update_interval: 60s
|
||||
- platform: template
|
||||
id: smartdisplay_action
|
||||
@@ -4448,6 +4447,25 @@ script:
|
||||
lv_obj_add_state(id(page_next), LV_STATE_DISABLED);
|
||||
}
|
||||
|
||||
# Runtime changes do not count as touches and do not reset the idle timer.
|
||||
- id: apply_screen_settings
|
||||
mode: restart
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &settings = screen_settings::current;
|
||||
if (id(display_dimmed) && !settings.standby_enabled) {
|
||||
id(display_dimmed) = false;
|
||||
}
|
||||
auto now = id(homeassistant_time).now();
|
||||
const int minute = now.is_valid() ? now.hour * 60 + now.minute : -1;
|
||||
const int level = id(display_dimmed) ? settings.dim_level(minute) : settings.brightness;
|
||||
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);
|
||||
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
|
||||
|
||||
- id: wake_display
|
||||
mode: restart
|
||||
then:
|
||||
@@ -4459,7 +4477,7 @@ script:
|
||||
# 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;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- lvgl.widget.hide: dim_wake_overlay
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.hide: climate_detail_overlay
|
||||
@@ -4471,41 +4489,14 @@ script:
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return std::string("${AUTO_DIM}") == "true";'
|
||||
lambda: 'return screen_settings::current.standby_enabled;'
|
||||
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();
|
||||
// Vóór de eerste tijd-sync (zie time: hierboven) is now()
|
||||
// epoch 0 — dan geen nachtmodus-beslissing nemen op basis
|
||||
// van een ongeldig uur, gewoon normale DIM_BRIGHTNESS.
|
||||
if (now.is_valid()) {
|
||||
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);
|
||||
id(apply_screen_settings).execute();
|
||||
if (screen_settings::current.home_on_standby) {
|
||||
id(tile_page) = 0;
|
||||
id(show_tile_page).execute();
|
||||
}
|
||||
|
||||
# Terug naar het basisscherm bij standby. Bewust via de ESPHome
|
||||
@@ -5394,7 +5385,9 @@ script:
|
||||
if (runtime_tiles::enabled) {
|
||||
runtime_tiles::render(id(lbl_room));
|
||||
auto now = id(homeassistant_time).now();
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime("%H:%M"));
|
||||
if (screen_settings::current.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime(screen_settings::current.clock_24h ? "%H:%M" : "%I:%M"));
|
||||
return;
|
||||
}
|
||||
auto set_lbl = [](lv_obj_t* lbl, const std::string& s) {
|
||||
|
||||
+52
-59
@@ -777,7 +777,7 @@ esphome:
|
||||
min_version: 2026.6.2
|
||||
project:
|
||||
name: maxgramser.esp_screen
|
||||
version: "0.1.0"
|
||||
version: "0.1.2"
|
||||
includes:
|
||||
- cyd_ui.h
|
||||
- light_controls.h
|
||||
@@ -796,8 +796,19 @@ esphome:
|
||||
lv_obj_set_style_pad_row(id(vacuum_speeds), 4, 0);
|
||||
lv_obj_set_style_pad_column(id(vacuum_speeds), 4, 0);
|
||||
- lambda: |-
|
||||
auto &settings = screen_settings::current;
|
||||
settings.standby_enabled = std::string("${AUTO_DIM}") == "true";
|
||||
settings.standby_seconds = atoi("${AUTO_DIM_TIMEOUT}");
|
||||
settings.brightness = atoi("${MAX_BRIGHTNESS}");
|
||||
settings.standby_brightness = atoi("${DIM_BRIGHTNESS}");
|
||||
settings.night_enabled = std::string("${NIGHT_MODE}") == "true";
|
||||
settings.night_start = atoi("${NIGHT_START_HOUR}") * 60;
|
||||
settings.night_end = atoi("${NIGHT_END_HOUR}") * 60;
|
||||
settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}");
|
||||
runtime_tiles::enabled = std::string("${DYNAMIC_TILES}") == "true";
|
||||
if (!runtime_tiles::enabled) return;
|
||||
runtime_tiles::load_settings();
|
||||
runtime_tiles::settings_changed = []() { id(apply_screen_settings).execute(); };
|
||||
runtime_tiles::dismiss = []() {
|
||||
id(active_entity).clear();
|
||||
lv_obj_add_flag(id(brightness_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -879,7 +890,7 @@ esphome:
|
||||
${TOUCH_CAL_X_MIN}, ${TOUCH_CAL_X_MAX}, ${TOUCH_CAL_Y_MIN}, ${TOUCH_CAL_Y_MAX});
|
||||
- light.turn_on:
|
||||
id: back_light
|
||||
brightness: !lambda "return atoi(\"${MAX_BRIGHTNESS}\") / 100.0f;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- script.execute: show_tile_page
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
- if:
|
||||
@@ -940,6 +951,12 @@ api:
|
||||
- action: ui_state
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &cfg = screen_settings::current;
|
||||
ESP_LOGI("ui_diag", "settings standby=%d timeout=%d brightness=%d dim=%d night=%d start=%d end=%d night_dim=%d clock=%d clock24=%d home=%d idle_ms=%u dimmed=%d",
|
||||
cfg.standby_enabled, cfg.standby_seconds, cfg.brightness, cfg.standby_brightness,
|
||||
cfg.night_enabled, cfg.night_start, cfg.night_end, cfg.night_brightness,
|
||||
cfg.show_clock, cfg.clock_24h, cfg.home_on_standby,
|
||||
(unsigned)(millis() - id(last_touch_ms)), id(display_dimmed));
|
||||
lv_area_t nav, grid;
|
||||
lv_obj_update_layout(id(home_page)->obj);
|
||||
lv_obj_get_coords(id(page_next), &nav);
|
||||
@@ -980,9 +997,9 @@ interval:
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
if (std::string("${AUTO_DIM}") != "true") return false;
|
||||
if (!screen_settings::current.standby_enabled) return false;
|
||||
if (id(display_dimmed) || id(touch_down) || id(calibration_active)) return false;
|
||||
return (millis() - id(last_touch_ms)) > (atoi("${AUTO_DIM_TIMEOUT}") * 1000UL);
|
||||
return (millis() - id(last_touch_ms)) > (screen_settings::current.standby_seconds * 1000UL);
|
||||
then:
|
||||
- script.execute: dim_display
|
||||
|
||||
@@ -993,33 +1010,15 @@ time:
|
||||
update_interval: 60s
|
||||
on_time_sync:
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /1
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
|
||||
- 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: rendered_frames
|
||||
type: uint32_t
|
||||
@@ -1499,7 +1498,7 @@ text_sensor:
|
||||
- platform: template
|
||||
name: "Schermfirmware"
|
||||
entity_category: diagnostic
|
||||
lambda: 'return {"0.1.0"};'
|
||||
lambda: 'return {"0.1.2"};'
|
||||
update_interval: 60s
|
||||
- platform: template
|
||||
id: smartdisplay_action
|
||||
@@ -4636,6 +4635,25 @@ script:
|
||||
lv_obj_add_state(id(page_next), LV_STATE_DISABLED);
|
||||
}
|
||||
|
||||
# Runtime changes do not count as touches and do not reset the idle timer.
|
||||
- id: apply_screen_settings
|
||||
mode: restart
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &settings = screen_settings::current;
|
||||
if (id(display_dimmed) && !settings.standby_enabled) {
|
||||
id(display_dimmed) = false;
|
||||
}
|
||||
auto now = id(homeassistant_time).now();
|
||||
const int minute = now.is_valid() ? now.hour * 60 + now.minute : -1;
|
||||
const int level = id(display_dimmed) ? settings.dim_level(minute) : settings.brightness;
|
||||
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);
|
||||
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
|
||||
|
||||
- id: wake_display
|
||||
mode: restart
|
||||
then:
|
||||
@@ -4647,7 +4665,7 @@ script:
|
||||
# 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;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- lvgl.widget.hide: dim_wake_overlay
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.hide: climate_detail_overlay
|
||||
@@ -4659,41 +4677,14 @@ script:
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return std::string("${AUTO_DIM}") == "true";'
|
||||
lambda: 'return screen_settings::current.standby_enabled;'
|
||||
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();
|
||||
// Vóór de eerste tijd-sync (zie time: hierboven) is now()
|
||||
// epoch 0 — dan geen nachtmodus-beslissing nemen op basis
|
||||
// van een ongeldig uur, gewoon normale DIM_BRIGHTNESS.
|
||||
if (now.is_valid()) {
|
||||
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);
|
||||
id(apply_screen_settings).execute();
|
||||
if (screen_settings::current.home_on_standby) {
|
||||
id(tile_page) = 0;
|
||||
id(show_tile_page).execute();
|
||||
}
|
||||
|
||||
# Terug naar het basisscherm bij standby. Bewust via de ESPHome
|
||||
@@ -5582,7 +5573,9 @@ script:
|
||||
if (runtime_tiles::enabled) {
|
||||
runtime_tiles::render(id(lbl_room));
|
||||
auto now = id(homeassistant_time).now();
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime("%H:%M"));
|
||||
if (screen_settings::current.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime(screen_settings::current.clock_24h ? "%H:%M" : "%I:%M"));
|
||||
return;
|
||||
}
|
||||
auto set_lbl = [](lv_obj_t* lbl, const std::string& s) {
|
||||
|
||||
+52
-59
@@ -781,7 +781,7 @@ esphome:
|
||||
min_version: 2026.6.2
|
||||
project:
|
||||
name: maxgramser.esp_screen
|
||||
version: "0.1.0"
|
||||
version: "0.1.2"
|
||||
includes:
|
||||
- <esphome/components/smart_display/cyd_ui.h>
|
||||
- <esphome/components/smart_display/light_controls.h>
|
||||
@@ -800,8 +800,19 @@ esphome:
|
||||
lv_obj_set_style_pad_row(id(vacuum_speeds), 4, 0);
|
||||
lv_obj_set_style_pad_column(id(vacuum_speeds), 4, 0);
|
||||
- lambda: |-
|
||||
auto &settings = screen_settings::current;
|
||||
settings.standby_enabled = std::string("${AUTO_DIM}") == "true";
|
||||
settings.standby_seconds = atoi("${AUTO_DIM_TIMEOUT}");
|
||||
settings.brightness = atoi("${MAX_BRIGHTNESS}");
|
||||
settings.standby_brightness = atoi("${DIM_BRIGHTNESS}");
|
||||
settings.night_enabled = std::string("${NIGHT_MODE}") == "true";
|
||||
settings.night_start = atoi("${NIGHT_START_HOUR}") * 60;
|
||||
settings.night_end = atoi("${NIGHT_END_HOUR}") * 60;
|
||||
settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}");
|
||||
runtime_tiles::enabled = std::string("${DYNAMIC_TILES}") == "true";
|
||||
if (!runtime_tiles::enabled) return;
|
||||
runtime_tiles::load_settings();
|
||||
runtime_tiles::settings_changed = []() { id(apply_screen_settings).execute(); };
|
||||
runtime_tiles::dismiss = []() {
|
||||
id(active_entity).clear();
|
||||
lv_obj_add_flag(id(brightness_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -883,7 +894,7 @@ esphome:
|
||||
${TOUCH_CAL_X_MIN}, ${TOUCH_CAL_X_MAX}, ${TOUCH_CAL_Y_MIN}, ${TOUCH_CAL_Y_MAX});
|
||||
- light.turn_on:
|
||||
id: back_light
|
||||
brightness: !lambda "return atoi(\"${MAX_BRIGHTNESS}\") / 100.0f;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- script.execute: show_tile_page
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
- if:
|
||||
@@ -944,6 +955,12 @@ api:
|
||||
- action: ui_state
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &cfg = screen_settings::current;
|
||||
ESP_LOGI("ui_diag", "settings standby=%d timeout=%d brightness=%d dim=%d night=%d start=%d end=%d night_dim=%d clock=%d clock24=%d home=%d idle_ms=%u dimmed=%d",
|
||||
cfg.standby_enabled, cfg.standby_seconds, cfg.brightness, cfg.standby_brightness,
|
||||
cfg.night_enabled, cfg.night_start, cfg.night_end, cfg.night_brightness,
|
||||
cfg.show_clock, cfg.clock_24h, cfg.home_on_standby,
|
||||
(unsigned)(millis() - id(last_touch_ms)), id(display_dimmed));
|
||||
lv_area_t nav, grid;
|
||||
lv_obj_update_layout(id(home_page)->obj);
|
||||
lv_obj_get_coords(id(page_next), &nav);
|
||||
@@ -971,9 +988,9 @@ interval:
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
if (std::string("${AUTO_DIM}") != "true") return false;
|
||||
if (!screen_settings::current.standby_enabled) return false;
|
||||
if (id(display_dimmed) || id(touch_down) || id(calibration_active)) return false;
|
||||
return (millis() - id(last_touch_ms)) > (atoi("${AUTO_DIM_TIMEOUT}") * 1000UL);
|
||||
return (millis() - id(last_touch_ms)) > (screen_settings::current.standby_seconds * 1000UL);
|
||||
then:
|
||||
- script.execute: dim_display
|
||||
|
||||
@@ -984,33 +1001,15 @@ time:
|
||||
update_interval: 60s
|
||||
on_time_sync:
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /1
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
|
||||
- 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: rendered_frames
|
||||
type: uint32_t
|
||||
@@ -1452,7 +1451,7 @@ text_sensor:
|
||||
- platform: template
|
||||
name: "Schermfirmware"
|
||||
entity_category: diagnostic
|
||||
lambda: 'return {"0.1.0"};'
|
||||
lambda: 'return {"0.1.2"};'
|
||||
update_interval: 60s
|
||||
- platform: template
|
||||
id: smartdisplay_action
|
||||
@@ -4569,6 +4568,25 @@ script:
|
||||
lv_obj_add_state(id(page_next), LV_STATE_DISABLED);
|
||||
}
|
||||
|
||||
# Runtime changes do not count as touches and do not reset the idle timer.
|
||||
- id: apply_screen_settings
|
||||
mode: restart
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &settings = screen_settings::current;
|
||||
if (id(display_dimmed) && !settings.standby_enabled) {
|
||||
id(display_dimmed) = false;
|
||||
}
|
||||
auto now = id(homeassistant_time).now();
|
||||
const int minute = now.is_valid() ? now.hour * 60 + now.minute : -1;
|
||||
const int level = id(display_dimmed) ? settings.dim_level(minute) : settings.brightness;
|
||||
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);
|
||||
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
|
||||
|
||||
- id: wake_display
|
||||
mode: restart
|
||||
then:
|
||||
@@ -4580,7 +4598,7 @@ script:
|
||||
# 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;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- lvgl.widget.hide: dim_wake_overlay
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.hide: climate_detail_overlay
|
||||
@@ -4592,41 +4610,14 @@ script:
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return std::string("${AUTO_DIM}") == "true";'
|
||||
lambda: 'return screen_settings::current.standby_enabled;'
|
||||
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();
|
||||
// Vóór de eerste tijd-sync (zie time: hierboven) is now()
|
||||
// epoch 0 — dan geen nachtmodus-beslissing nemen op basis
|
||||
// van een ongeldig uur, gewoon normale DIM_BRIGHTNESS.
|
||||
if (now.is_valid()) {
|
||||
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);
|
||||
id(apply_screen_settings).execute();
|
||||
if (screen_settings::current.home_on_standby) {
|
||||
id(tile_page) = 0;
|
||||
id(show_tile_page).execute();
|
||||
}
|
||||
|
||||
# Terug naar het basisscherm bij standby. Bewust via de ESPHome
|
||||
@@ -5515,7 +5506,9 @@ script:
|
||||
if (runtime_tiles::enabled) {
|
||||
runtime_tiles::render(id(lbl_room));
|
||||
auto now = id(homeassistant_time).now();
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime("%H:%M"));
|
||||
if (screen_settings::current.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime(screen_settings::current.clock_24h ? "%H:%M" : "%I:%M"));
|
||||
return;
|
||||
}
|
||||
auto set_lbl = [](lv_obj_t* lbl, const std::string& s) {
|
||||
|
||||
+52
-59
@@ -586,7 +586,7 @@ esphome:
|
||||
min_version: 2026.6.2
|
||||
project:
|
||||
name: maxgramser.esp_screen
|
||||
version: "0.1.0"
|
||||
version: "0.1.2"
|
||||
platformio_options:
|
||||
build_flags:
|
||||
- -DLV_USE_SNAPSHOT=1
|
||||
@@ -607,8 +607,19 @@ esphome:
|
||||
lv_obj_set_style_pad_row(id(vacuum_speeds), 4, 0);
|
||||
lv_obj_set_style_pad_column(id(vacuum_speeds), 4, 0);
|
||||
- lambda: |-
|
||||
auto &settings = screen_settings::current;
|
||||
settings.standby_enabled = std::string("${AUTO_DIM}") == "true";
|
||||
settings.standby_seconds = atoi("${AUTO_DIM_TIMEOUT}");
|
||||
settings.brightness = atoi("${MAX_BRIGHTNESS}");
|
||||
settings.standby_brightness = atoi("${DIM_BRIGHTNESS}");
|
||||
settings.night_enabled = std::string("${NIGHT_MODE}") == "true";
|
||||
settings.night_start = atoi("${NIGHT_START_HOUR}") * 60;
|
||||
settings.night_end = atoi("${NIGHT_END_HOUR}") * 60;
|
||||
settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}");
|
||||
runtime_tiles::enabled = std::string("${DYNAMIC_TILES}") == "true";
|
||||
if (!runtime_tiles::enabled) return;
|
||||
runtime_tiles::load_settings();
|
||||
runtime_tiles::settings_changed = []() { id(apply_screen_settings).execute(); };
|
||||
runtime_tiles::dismiss = []() {
|
||||
id(active_entity).clear();
|
||||
lv_obj_add_flag(id(brightness_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -682,7 +693,7 @@ esphome:
|
||||
light_controls::commits[2] = [](int value) { id(color_brightness_commit).execute(value); };
|
||||
- light.turn_on:
|
||||
id: back_light
|
||||
brightness: !lambda "return atoi(\"${MAX_BRIGHTNESS}\") / 100.0f;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- script.execute: show_tile_page
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
- if:
|
||||
@@ -752,6 +763,12 @@ api:
|
||||
- action: ui_state
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &cfg = screen_settings::current;
|
||||
ESP_LOGI("ui_diag", "settings standby=%d timeout=%d brightness=%d dim=%d night=%d start=%d end=%d night_dim=%d clock=%d clock24=%d home=%d idle_ms=%u dimmed=%d",
|
||||
cfg.standby_enabled, cfg.standby_seconds, cfg.brightness, cfg.standby_brightness,
|
||||
cfg.night_enabled, cfg.night_start, cfg.night_end, cfg.night_brightness,
|
||||
cfg.show_clock, cfg.clock_24h, cfg.home_on_standby,
|
||||
(unsigned)(millis() - id(last_touch_ms)), id(display_dimmed));
|
||||
lv_area_t nav, grid;
|
||||
lv_obj_update_layout(id(home_page)->obj);
|
||||
lv_obj_get_coords(id(page_next), &nav);
|
||||
@@ -779,9 +796,9 @@ interval:
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
if (std::string("${AUTO_DIM}") != "true") return false;
|
||||
if (!screen_settings::current.standby_enabled) return false;
|
||||
if (id(display_dimmed) || id(touch_down) || id(calibration_active)) return false;
|
||||
return (millis() - id(last_touch_ms)) > (atoi("${AUTO_DIM_TIMEOUT}") * 1000UL);
|
||||
return (millis() - id(last_touch_ms)) > (screen_settings::current.standby_seconds * 1000UL);
|
||||
then:
|
||||
- script.execute: dim_display
|
||||
|
||||
@@ -792,33 +809,15 @@ time:
|
||||
update_interval: 60s
|
||||
on_time_sync:
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /1
|
||||
then:
|
||||
- script.execute: apply_screen_settings
|
||||
- lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
|
||||
|
||||
- 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: rendered_frames
|
||||
type: uint32_t
|
||||
@@ -1265,7 +1264,7 @@ text_sensor:
|
||||
- platform: template
|
||||
name: "Schermfirmware"
|
||||
entity_category: diagnostic
|
||||
lambda: 'return {"0.1.0"};'
|
||||
lambda: 'return {"0.1.2"};'
|
||||
update_interval: 60s
|
||||
- platform: template
|
||||
id: smartdisplay_action
|
||||
@@ -4381,6 +4380,25 @@ script:
|
||||
lv_obj_add_state(id(page_next), LV_STATE_DISABLED);
|
||||
}
|
||||
|
||||
# Runtime changes do not count as touches and do not reset the idle timer.
|
||||
- id: apply_screen_settings
|
||||
mode: restart
|
||||
then:
|
||||
- lambda: |-
|
||||
const auto &settings = screen_settings::current;
|
||||
if (id(display_dimmed) && !settings.standby_enabled) {
|
||||
id(display_dimmed) = false;
|
||||
}
|
||||
auto now = id(homeassistant_time).now();
|
||||
const int minute = now.is_valid() ? now.hour * 60 + now.minute : -1;
|
||||
const int level = id(display_dimmed) ? settings.dim_level(minute) : settings.brightness;
|
||||
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);
|
||||
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
|
||||
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
|
||||
|
||||
- id: wake_display
|
||||
mode: restart
|
||||
then:
|
||||
@@ -4392,7 +4410,7 @@ script:
|
||||
# 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;"
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- lvgl.widget.hide: dim_wake_overlay
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.hide: climate_detail_overlay
|
||||
@@ -4404,41 +4422,14 @@ script:
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return std::string("${AUTO_DIM}") == "true";'
|
||||
lambda: 'return screen_settings::current.standby_enabled;'
|
||||
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();
|
||||
// Vóór de eerste tijd-sync (zie time: hierboven) is now()
|
||||
// epoch 0 — dan geen nachtmodus-beslissing nemen op basis
|
||||
// van een ongeldig uur, gewoon normale DIM_BRIGHTNESS.
|
||||
if (now.is_valid()) {
|
||||
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);
|
||||
id(apply_screen_settings).execute();
|
||||
if (screen_settings::current.home_on_standby) {
|
||||
id(tile_page) = 0;
|
||||
id(show_tile_page).execute();
|
||||
}
|
||||
|
||||
# Terug naar het basisscherm bij standby. Bewust via de ESPHome
|
||||
@@ -5327,7 +5318,9 @@ script:
|
||||
if (runtime_tiles::enabled) {
|
||||
runtime_tiles::render(id(lbl_room));
|
||||
auto now = id(homeassistant_time).now();
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime("%H:%M"));
|
||||
if (screen_settings::current.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
|
||||
if (now.is_valid()) runtime_tiles::label(id(lbl_time), now.strftime(screen_settings::current.clock_24h ? "%H:%M" : "%I:%M"));
|
||||
return;
|
||||
}
|
||||
auto set_lbl = [](lv_obj_t* lbl, const std::string& s) {
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
# 0.1.2
|
||||
|
||||
- Per scherm standbyduur, normale/standby/nachthelderheid en nachturen instellen.
|
||||
- Klok aan/uit, 12/24 uur en terug naar pagina 1 na standby.
|
||||
- Instellingen direct doorsturen; firmware 0.1.2 bewaart ze in preferences.
|
||||
- Bestaande tegels en sleutels blijven behouden. Eenmalig firmware bijwerken.
|
||||
- De Guition-displaydriver is ongewijzigd; dit is geen oplossing voor paneelstrepen.
|
||||
|
||||
# 0.1.1
|
||||
|
||||
- Duidelijke foutmelding bij een ongeldige naam, te veel tegels of dubbele entiteiten.
|
||||
|
||||
@@ -10,6 +10,38 @@ REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
|
||||
REFS = {'cyd': 'main', 'guition': 'main'}
|
||||
ATTRS = frozenset('brightness percentage current_position current_temperature temperature current_humidity min_temp max_temp target_temp_step supported_color_modes hvac_modes hs_color color_temp_kelvin min_color_temp_kelvin max_color_temp_kelvin fan_speed_list unit_of_measurement'.split())
|
||||
|
||||
# Additive schema 1 extension. An absent object retains old firmware/YAML defaults.
|
||||
SETTING_RULES = {
|
||||
'standby_enabled': (True, None, None),
|
||||
'standby_seconds': (600, 60, 86400),
|
||||
'brightness': (100, 5, 100),
|
||||
'standby_brightness': (20, 0, 100),
|
||||
'night_enabled': (True, None, None),
|
||||
'night_start': (1320, 0, 1439),
|
||||
'night_end': (420, 0, 1439),
|
||||
'night_brightness': (10, 0, 100),
|
||||
'show_clock': (True, None, None),
|
||||
'clock_24h': (True, None, None),
|
||||
'home_on_standby': (False, None, None),
|
||||
}
|
||||
|
||||
def validate_settings(data):
|
||||
if not isinstance(data, dict) or set(data) - SETTING_RULES.keys():
|
||||
raise ValueError('Onbekende scherminstellingen; vernieuw de beheerpagina.')
|
||||
clean = {}
|
||||
for key, (default, minimum, maximum) in SETTING_RULES.items():
|
||||
value = data.get(key, default)
|
||||
if minimum is None:
|
||||
valid = type(value) is bool
|
||||
else:
|
||||
valid = type(value) is int and minimum <= value <= maximum
|
||||
if not valid:
|
||||
raise ValueError(f'Ongeldige waarde voor {key}.')
|
||||
clean[key] = value
|
||||
if max(clean['standby_brightness'], clean['night_brightness']) > clean['brightness']:
|
||||
raise ValueError('Helderheid in standby en nacht mag niet hoger zijn dan normaal.')
|
||||
return clean
|
||||
|
||||
def entity_id(value):
|
||||
return isinstance(value, str) and len(value) <= 120 and re.fullmatch(r'[a-z0-9_]+\.[a-z0-9_]+', value) and value.split('.')[0] in DOMAINS
|
||||
|
||||
@@ -35,7 +67,10 @@ def validate_layout(data):
|
||||
raise ValueError('Een tegelnaam mag maximaal 80 bytes bevatten.')
|
||||
seen.add(tile['entity'])
|
||||
clean.append({'entity': tile['entity'], 'name': name.strip()})
|
||||
return {'title': title.strip(), 'tiles': clean}
|
||||
result = {'title': title.strip(), 'tiles': clean}
|
||||
if 'settings' in data:
|
||||
result['settings'] = validate_settings(data['settings'])
|
||||
return result
|
||||
|
||||
def state_message(index, tile, states):
|
||||
state = states.get(tile['entity'], {})
|
||||
|
||||
@@ -130,6 +130,9 @@ class Manager:
|
||||
if inbox not in {s['id'] for s in self.inventory()[0]}:
|
||||
raise ValueError('Dit is geen gekoppeld ESP-scherm. Vernieuw het overzicht.')
|
||||
layout = validate_layout(data)
|
||||
# A still-open older UI may save tiles without the new optional settings.
|
||||
if 'settings' not in layout and 'settings' in self.layouts.get(inbox, {}):
|
||||
layout['settings'] = self.layouts[inbox]['settings'].copy()
|
||||
known = {e['id'] for e in self.inventory()[1]}
|
||||
if any(t['entity'] not in known for t in layout['tiles']):
|
||||
raise ValueError('Een gekozen entiteit bestaat niet meer. Zoek de nieuwe entiteit op.')
|
||||
@@ -149,6 +152,8 @@ class Manager:
|
||||
|
||||
async def sync_one(self, inbox, layout, force=False):
|
||||
messages = [{'v': 1, 'op': 'layout', 'title': layout['title'], 'entities': [t['entity'] for t in layout['tiles']]}]
|
||||
if 'settings' in layout:
|
||||
messages[0]['settings'] = layout['settings']
|
||||
messages += [state_message(i, tile, self.ha.states) for i, tile in enumerate(layout['tiles'])]
|
||||
previous = self.sent.get(inbox, [])
|
||||
force = force or not previous or messages[0] != previous[0]
|
||||
|
||||
@@ -65,6 +65,99 @@ function select(id) {
|
||||
renderScreens();
|
||||
renderTiles();
|
||||
renderResults();
|
||||
renderSettings();
|
||||
}
|
||||
const settingDefinitions = [
|
||||
["standby_enabled", "Automatisch standby", "check", true],
|
||||
["standby_seconds", "Standby na", "minutes", 600, 1, 1440],
|
||||
["brightness", "Helderheid normaal", "range", 100, 5, 100],
|
||||
["standby_brightness", "Helderheid in standby", "range", 20, 0, 100],
|
||||
["night_enabled", "Nachtstand gebruiken", "check", true],
|
||||
["night_brightness", "Helderheid in nachtstand", "range", 10, 0, 100],
|
||||
["night_start", "Nachtstand vanaf", "time", 1320],
|
||||
["night_end", "Nachtstand tot", "time", 420],
|
||||
["show_clock", "Klok tonen", "check", true],
|
||||
["clock_24h", "24-uursklok (uit = 12 uur)", "check", true],
|
||||
["home_on_standby", "Na standby terug naar pagina 1", "check", false],
|
||||
];
|
||||
function renderSettings() {
|
||||
const values = {
|
||||
...Object.fromEntries(
|
||||
settingDefinitions.map(([key, , , value]) => [key, value]),
|
||||
),
|
||||
...layout.settings,
|
||||
};
|
||||
const container = $("#settings-fields");
|
||||
container.replaceChildren();
|
||||
for (const [key, title, kind, , min, max] of settingDefinitions) {
|
||||
const label = node(
|
||||
"label",
|
||||
undefined,
|
||||
`setting ${kind === "check" ? "setting-check" : ""}`,
|
||||
);
|
||||
const caption = node("span", title),
|
||||
input = node("input"),
|
||||
output = node("output");
|
||||
input.id = `setting-${key}`;
|
||||
input.type =
|
||||
kind === "check" ? "checkbox" : kind === "minutes" ? "number" : kind;
|
||||
input.setAttribute("aria-label", title);
|
||||
input.required = kind === "minutes" || kind === "time";
|
||||
if (kind === "check") input.checked = values[key];
|
||||
else if (kind === "time")
|
||||
input.value = `${String(Math.floor(values[key] / 60)).padStart(2, "0")}:${String(values[key] % 60).padStart(2, "0")}`;
|
||||
else {
|
||||
input.min = min;
|
||||
input.max = max;
|
||||
input.step = 1;
|
||||
input.value = kind === "minutes" ? values[key] / 60 : values[key];
|
||||
}
|
||||
const updateOutput = () =>
|
||||
(output.textContent =
|
||||
kind === "range"
|
||||
? `${input.value}%`
|
||||
: kind === "minutes"
|
||||
? "minuten"
|
||||
: "");
|
||||
updateOutput();
|
||||
input.oninput = () => {
|
||||
if (!input.checkValidity() || input.value === "") return;
|
||||
let value = kind === "check" ? input.checked : Number(input.value);
|
||||
if (kind === "time") {
|
||||
const [h, m] = input.value.split(":").map(Number);
|
||||
value = h * 60 + m;
|
||||
}
|
||||
if (kind === "minutes") value *= 60;
|
||||
layout.settings = { ...values, ...layout.settings, [key]: value };
|
||||
// A lower normal brightness also lowers any higher standby levels.
|
||||
if (key === "brightness")
|
||||
for (const dim of ["standby_brightness", "night_brightness"]) {
|
||||
layout.settings[dim] = Math.min(layout.settings[dim], value);
|
||||
const other = $(`#setting-${dim}`);
|
||||
other.value = layout.settings[dim];
|
||||
other.parentElement.querySelector("output").textContent =
|
||||
`${other.value}%`;
|
||||
}
|
||||
updateOutput();
|
||||
markDirty();
|
||||
};
|
||||
label.append(caption, input, output);
|
||||
container.append(label);
|
||||
}
|
||||
renderSettingsSupport();
|
||||
}
|
||||
function renderSettingsSupport() {
|
||||
const version =
|
||||
inventory.screens.find((s) => s.id === selected)?.firmware || "";
|
||||
const parts = /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(version);
|
||||
const supported =
|
||||
parts &&
|
||||
(Number(parts[1]) > 0 ||
|
||||
Number(parts[2]) > 1 ||
|
||||
(Number(parts[2]) === 1 && Number(parts[3]) >= 2));
|
||||
$("#settings-support").textContent = supported
|
||||
? "Opslaan stuurt je instellingen direct naar dit scherm. Ze blijven ook na een herstart bewaard."
|
||||
: "Eenmalig firmware 0.1.2 of nieuwer installeren via ESPHome. Je kunt de instellingen alvast bewaren; oudere firmware gebruikt ze nog niet.";
|
||||
}
|
||||
function renderScreens() {
|
||||
$("#screens").replaceChildren();
|
||||
@@ -220,6 +313,7 @@ async function refresh() {
|
||||
: "Verbinding met Home Assistant herstellen…";
|
||||
$("#connection").classList.toggle("online", inventory.connected);
|
||||
renderScreens();
|
||||
if (selected) renderSettingsSupport();
|
||||
if (!selected && inventory.screens.length) select(inventory.screens[0].id);
|
||||
} catch {
|
||||
$("#connection").textContent =
|
||||
@@ -228,6 +322,9 @@ async function refresh() {
|
||||
}
|
||||
$("#save").onclick = async () => {
|
||||
if (busy) return;
|
||||
for (const input of document.querySelectorAll("#settings-fields input")) {
|
||||
if (!input.reportValidity()) return;
|
||||
}
|
||||
busy = true;
|
||||
$("#save").disabled = true;
|
||||
try {
|
||||
|
||||
@@ -86,6 +86,19 @@
|
||||
<div id="results" aria-live="polite"></div>
|
||||
</div>
|
||||
</div>
|
||||
<details class="screen-settings" open>
|
||||
<summary>
|
||||
Scherminstellingen
|
||||
<span class="hint">Helderheid, rustmomenten en weergave</span>
|
||||
</summary>
|
||||
<p class="hint" id="settings-support"></p>
|
||||
<div class="settings-grid" id="settings-fields"></div>
|
||||
<p class="hint">
|
||||
Nachturen gelden voor de helderheid tijdens standby. De eerste
|
||||
aanraking maakt het scherm wakker; de volgende bedient je huis.
|
||||
Gelijke begin- en eindtijd schakelen het nachtvenster uit.
|
||||
</p>
|
||||
</details>
|
||||
<footer class="savebar">
|
||||
<div>
|
||||
<strong id="dirty">Alles opgeslagen</strong
|
||||
|
||||
@@ -563,3 +563,56 @@ textarea {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.screen-settings {
|
||||
margin: 28px 0 20px;
|
||||
border-top: 1px solid var(--line);
|
||||
padding-top: 20px;
|
||||
}
|
||||
.screen-settings summary {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px 28px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.setting {
|
||||
padding: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.setting output {
|
||||
display: block;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
.setting input[type="range"] {
|
||||
padding: 0;
|
||||
accent-color: var(--accent);
|
||||
min-height: 32px;
|
||||
}
|
||||
.setting-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
.setting-check input {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
accent-color: var(--accent);
|
||||
}
|
||||
.setting-check output {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.settings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ESP Screen Manager
|
||||
version: "0.1.1"
|
||||
version: "0.1.2"
|
||||
slug: esp_screen_manager
|
||||
description: Kies tegels voor je CYD en Guition touchscreen, zonder opnieuw flashen.
|
||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||
|
||||
+42
-1
@@ -8,7 +8,7 @@ import tempfile
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'screen_manager/app'))
|
||||
from core import discover, installation_yaml, packets, state_message, validate_layout
|
||||
from core import discover, installation_yaml, packets, state_message, validate_layout, validate_settings
|
||||
|
||||
class ProtocolTests(unittest.TestCase):
|
||||
def test_unicode_chunks_and_limits(self):
|
||||
@@ -24,6 +24,22 @@ class ProtocolTests(unittest.TestCase):
|
||||
with self.assertRaises(ValueError): validate_layout({'title':'Thuis','tiles':tiles})
|
||||
self.assertEqual(validate_layout({'title':'Thuis','tiles':[]})['tiles'],[])
|
||||
|
||||
def test_settings_defaults_ranges_and_old_layout(self):
|
||||
self.assertNotIn('settings', validate_layout({'title':'Thuis','tiles':[]}))
|
||||
defaults = validate_settings({})
|
||||
self.assertEqual(defaults['standby_seconds'], 600)
|
||||
self.assertEqual(defaults['night_start'], 22*60)
|
||||
for patch in [{'standby_seconds':True}, {'brightness':0}, {'night_start':1440},
|
||||
{'show_clock':1}, {'standby_seconds':600.5}, {'new_unknown':1},
|
||||
{'brightness':10,'standby_brightness':20}]:
|
||||
with self.assertRaises(ValueError): validate_settings(patch)
|
||||
for bad in [None, [], 'wrong']:
|
||||
with self.assertRaises(ValueError): validate_settings(bad)
|
||||
settings = validate_settings({'brightness':40,'standby_brightness':0,'night_brightness':0})
|
||||
self.assertEqual(settings['brightness'],40)
|
||||
message={'v':1,'op':'layout','title':'Thuis','entities':[], 'settings':settings}
|
||||
self.assertTrue(all(len(p)<=255 for p in packets(message)))
|
||||
|
||||
def test_message_has_only_bounded_display_attributes(self):
|
||||
state={'state':'on','attributes':{'friendly_name':'é'*90,'access_token':'private','hs_color':[float('nan'),300], 'brightness':float('inf')}}
|
||||
msg=state_message(0,{'entity':'light.a','name':''},{'light.a':state})
|
||||
@@ -87,6 +103,31 @@ class ManagerTests(unittest.IsolatedAsyncioTestCase):
|
||||
with self.assertRaises(ValueError): self.setup_manager(path)
|
||||
self.assertIn('99',path.read_text(),'unknown schema must never be overwritten')
|
||||
|
||||
async def test_settings_survive_update_and_old_client_save(self):
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
path=Path(temp)/'screens.json'; m=self.setup_manager(path)
|
||||
legacy={'title':'Thuis','tiles':[{'entity':'light.a','name':'Mijn lamp'}]}
|
||||
path.write_text(json.dumps({'version':1,'screens':{'text.screen':legacy}}))
|
||||
m=self.setup_manager(path)
|
||||
self.assertEqual(m.layouts['text.screen'],legacy)
|
||||
settings=validate_settings({'brightness':55,'standby_seconds':1200})
|
||||
m.save('text.screen',{**legacy,'settings':settings})
|
||||
fresh=self.setup_manager(path)
|
||||
self.assertEqual(fresh.layouts['text.screen']['settings'],settings)
|
||||
fresh.save('text.screen',{**legacy,'title':'Andere titel'})
|
||||
saved=fresh.layouts['text.screen']
|
||||
self.assertEqual(saved['settings'],settings)
|
||||
self.assertEqual(saved['tiles'],legacy['tiles'])
|
||||
before=path.read_bytes()
|
||||
with self.assertRaises(ValueError): fresh.save('text.screen',{**saved,'settings':{'brightness':0}})
|
||||
self.assertEqual(path.read_bytes(),before)
|
||||
await fresh.sync_one('text.screen',saved)
|
||||
self.assertEqual(fresh.ha.messages[0][1]['settings'],settings)
|
||||
await fresh.sync_one('text.screen',saved)
|
||||
self.assertEqual(len(fresh.ha.messages),2)
|
||||
await fresh.sync_one('text.screen',saved,True)
|
||||
self.assertEqual(fresh.ha.messages[2][1]['settings'],settings)
|
||||
|
||||
async def test_reorder_aborts_old_batch(self):
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
m=self.setup_manager(Path(temp)/'screens.json');layout={'title':'Thuis','tiles':[{'entity':'light.a','name':''}]}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "components/smart_display/screen_settings.h"
|
||||
#include <cassert>
|
||||
int main() {
|
||||
using screen_settings::Settings;
|
||||
Settings s;
|
||||
assert(s.valid() && s.standby_seconds == 600);
|
||||
assert(s.dim_level(-1) == 20); // No time sync must not accidentally select night.
|
||||
assert(s.dim_level(1319) == 20 && s.dim_level(1320) == 10);
|
||||
assert(s.dim_level(419) == 10 && s.dim_level(420) == 20);
|
||||
s.night_start = 60; s.night_end = 120;
|
||||
assert(!s.night(59) && s.night(60) && !s.night(120));
|
||||
s.night_end = 60; assert(!s.night(60) && !s.night(0));
|
||||
auto copy = s; assert(copy == s);
|
||||
copy.clock_24h = 0; assert(!(copy == s));
|
||||
s.brightness = 5; assert(!s.valid());
|
||||
s.standby_brightness = s.night_brightness = 0; assert(s.valid());
|
||||
s.standby_seconds = 86401; assert(!s.valid());
|
||||
s.standby_seconds = 60; s.show_clock = 2; assert(!s.valid());
|
||||
s.show_clock = 1; s.version = 2; assert(!s.valid());
|
||||
}
|
||||
Reference in New Issue
Block a user