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:
@@ -765,46 +765,149 @@ textarea {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px 28px;
|
||||
margin: 20px 0;
|
||||
/* Screen settings: the groups and rows of the settings page on the screen itself (app 0.2.57). */
|
||||
.settings-title {
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 16px;
|
||||
}
|
||||
.setting {
|
||||
padding: 14px;
|
||||
.settings-title h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
/* Columns, not a grid: the cards fill them from the top, so a short group leaves no hole beside a long one. */
|
||||
.settings-cards {
|
||||
column-width: 290px;
|
||||
column-gap: 14px;
|
||||
margin: 16px 0 4px;
|
||||
}
|
||||
.settings-card {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
background: white;
|
||||
padding: 14px 16px 8px;
|
||||
}
|
||||
.setting output {
|
||||
display: block;
|
||||
.settings-card h4 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 0 6px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.settings-card h4 .mdi {
|
||||
font-size: 20px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
.setting input[type="range"] {
|
||||
padding: 0;
|
||||
accent-color: var(--accent);
|
||||
min-height: 32px;
|
||||
}
|
||||
.setting-check {
|
||||
.setting-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
min-height: 48px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.setting-check input {
|
||||
.setting-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
.setting-label {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.setting-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex: none;
|
||||
}
|
||||
.setting-row.inactive .setting-label,
|
||||
.setting-row.inactive .setting-control {
|
||||
opacity: 0.45;
|
||||
}
|
||||
#general-settings.offline .settings-cards {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.switch {
|
||||
position: relative;
|
||||
width: 46px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border-radius: 14px;
|
||||
background: #c9d0d9;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.switch::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
accent-color: var(--accent);
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
box-shadow: 0 1px 2px #17223233;
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
.setting-check output {
|
||||
display: none;
|
||||
.switch[aria-checked="true"] {
|
||||
background: var(--accent);
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.settings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
.switch[aria-checked="true"]::after {
|
||||
transform: translateX(18px);
|
||||
}
|
||||
/* Neither on nor off: Home Assistant has no value (the screen is offline, or the entity is off). */
|
||||
.switch.unknown::after {
|
||||
transform: translateX(9px);
|
||||
}
|
||||
.step {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
background: #eef2f7;
|
||||
color: var(--ink);
|
||||
touch-action: manipulation;
|
||||
user-select: none;
|
||||
}
|
||||
.step .mdi {
|
||||
font-size: 18px;
|
||||
}
|
||||
.step:hover:not(:disabled) {
|
||||
filter: none;
|
||||
background: #e1e8f1;
|
||||
}
|
||||
.step:active:not(:disabled) {
|
||||
background: var(--soft);
|
||||
color: var(--accent);
|
||||
}
|
||||
.setting-value {
|
||||
min-width: 72px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.setting-choices.segmented {
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
}
|
||||
.setting-choices.segmented button {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
/* An end of a range greys its key; a row that waits on its switch fades as a whole instead. */
|
||||
.setting-row .step:disabled {
|
||||
opacity: 0.35;
|
||||
}
|
||||
.setting-row.inactive button:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.switch,
|
||||
.switch::after {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user