Colour the sliders of playing media players, covers and numbers (0.2.60)
Since firmware 0.2.42 a slider showed the tile's colour only while Tile::active() held, which is never true for a playing, paused or idle media player, a cover or a number, so their volume, position and value sliders stayed grey like a light that is off. Tile::slider_active() follows Home Assistant's tile sliders instead: grey for an unavailable entity, a light or fan that is off and a media player that is off or in standby; a closed cover keeps its colour as in HA's cover position feature, and a number with a value is active. The sliders of direct controls on wide tiles and the small sliders use it; Tile::active() and its other uses are unchanged. Checked with 282 Python and 15 C++ tests, generate_packages --check, ESP32 builds of both check profiles and host renders of both boards. Firmware 0.2.52. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -156,6 +156,17 @@ struct Tile {
|
||||
return state == "on" || state == "cleaning" || state == "active" || (domain() == "person" && state == "home") ||
|
||||
(domain() == "sun" && state == "above_horizon") || (domain() == "climate" && available() && state != "off");
|
||||
}
|
||||
// A slider shows the card's colour like Home Assistant's tile sliders: grey only while stateActive()
|
||||
// (frontend src/common/entity/state_active.ts) calls the entity inactive, such as an off light or fan and a
|
||||
// media player that is off or in standby. A number with a value is active there, and a closed cover's
|
||||
// position slider keeps the cover's colour so it never looks disabled (hui-cover-position-card-feature.ts).
|
||||
bool slider_active() const {
|
||||
if (!available()) return false;
|
||||
auto d = domain();
|
||||
if (d == "media_player") return state != "off" && state != "standby";
|
||||
if (d == "cover" || d == "number" || d == "input_number") return true;
|
||||
return active();
|
||||
}
|
||||
};
|
||||
// Slot position of a tile within the fixed two-column, three-row pages.
|
||||
struct Placement { uint8_t page = 0, slot = 0; };
|
||||
|
||||
@@ -2287,7 +2287,7 @@ inline void style_panel(Widgets &w,const Tile &t,lv_color_t accent,lv_color_t te
|
||||
}
|
||||
if(w.pill){set_color(w.pill,LV_STYLE_BG_COLOR,key_bg);set_color(w.pill_value,LV_STYLE_TEXT_COLOR,text);}
|
||||
if(w.control_slider){
|
||||
bool on=fresh() && t.active();
|
||||
bool on=fresh() && t.slider_active();
|
||||
lv_color_t fill=on?accent:lv_color_hex(0x9E9E9E);
|
||||
// The track is the fill colour at 20 % over the card, as in Home Assistant.
|
||||
set_color(w.control_slider,LV_STYLE_BG_COLOR,lv_color_mix(fill,card,51),LV_PART_MAIN);
|
||||
@@ -2462,8 +2462,10 @@ inline void render_slot(size_t slot) {
|
||||
lap(swipe_profile::CUSTOM);
|
||||
}
|
||||
bool on = fresh() && t.active();
|
||||
// Sliders colour more states than the card itself does, such as a playing media player (Tile::slider_active).
|
||||
bool slider_on = fresh() && t.slider_active();
|
||||
bool available=fresh() && t.available();
|
||||
int palette_state=(available?2:0)|(on?1:0);
|
||||
int palette_state=(available?2:0)|(on?1:0)|(slider_on?4:0);
|
||||
if (w.cached_active == palette_state && !w.panel_dirty) { lap(swipe_profile::GEOMETRY); return; }
|
||||
w.cached_active = palette_state;w.panel_dirty=false;
|
||||
uint32_t accent = domain_accent(t);
|
||||
@@ -2474,10 +2476,10 @@ inline void render_slot(size_t slot) {
|
||||
// Darken the foreground slightly: very pale bulbs still need a visible icon.
|
||||
auto icon_color=available?lv_color_mix(color,lv_color_hex(0x333333),205):lv_color_hex(0x9E9E9E);
|
||||
// Off: a grey track without fill or handle, as in Home Assistant.
|
||||
auto fill_color=on?color:lv_color_hex(0x9E9E9E);
|
||||
auto fill_color=slider_on?color:lv_color_hex(0x9E9E9E);
|
||||
set_color(w.slider,LV_STYLE_BG_COLOR,fill_color,LV_PART_INDICATOR);
|
||||
set_color(w.slider,LV_STYLE_BG_COLOR,lv_color_mix(fill_color,lv_color_hex(0xFFFFFF),51),LV_PART_MAIN);
|
||||
slider_bar(w.slider,slider_bar_shown(t,on));
|
||||
slider_bar(w.slider,slider_bar_shown(t,slider_on));
|
||||
set_color(w.tile,LV_STYLE_BG_COLOR,lv_color_hex(t.background ? t.background : 0xFFFFFF));
|
||||
set_number(w.tile,LV_STYLE_BORDER_WIDTH,1);
|
||||
// "Background: none" hides only the card; geometry and padding stay identical,
|
||||
|
||||
@@ -176,6 +176,19 @@ icons sit off-center in the browser); run `generate_packages.py` afterward.
|
||||
The `MDI_GLYPH_*` substitutions are retired: `TILEn_ICON` in manual
|
||||
profiles must come from the set. No changed preferences or keys.
|
||||
|
||||
### Compatibility 0.2.60 / firmware 0.2.52
|
||||
|
||||
`components/smart_display` and both board profiles; the app only raises `FIRMWARE_VERSION`. Storage, tile protocol,
|
||||
preferences and keys are unchanged.
|
||||
|
||||
- `Tile::slider_active()` (runtime_model.h) decides whether a slider shows the tile's colour, after Home Assistant's
|
||||
`stateActive()` and its tile features: grey for an unavailable entity, a light or fan that is off and a media player
|
||||
that is `off` or `standby`; a cover keeps its colour when closed (`hui-cover-position-card-feature.ts`) and a number
|
||||
with a value is active. `style_panel` (sliders of direct controls) and `render_slot` (small sliders, with a third bit
|
||||
in the palette cache key) use it. `Tile::active()` is unchanged: it still greys the circle of an off switch, person or
|
||||
timer and gives the value overlay its `was_on`.
|
||||
- Older firmware keeps the grey sliders; nothing else differs.
|
||||
|
||||
### Compatibility 0.2.59 / firmware 0.2.51
|
||||
|
||||
App, both board profiles and `components/smart_display`. Storage, tile protocol, preferences and keys are
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Test results app 0.2.60 / firmware 0.2.52 (2026-09-17)
|
||||
|
||||
Max saw grey sliders for a playing media player's volume and an open cover's position. Since 0.2.42 (a grey slider
|
||||
end for a light that is off) a slider showed the tile's colour only while `Tile::active()` held, and that rule knows
|
||||
`on`, `cleaning`, `active`, home, above the horizon and climate not off: never `playing` or `open`, and never a number.
|
||||
Home Assistant colours tile sliders by `stateActive()` (frontend `src/common/entity/state_active.ts`, read on GitHub):
|
||||
a media player is active unless off or standby, a cover unless closed, and its cover position feature keeps the open
|
||||
colour for a closed cover so the slider does not look disabled. Every caller of `active()` was checked (the grey circle
|
||||
of switches, people and timers, a light's own colour, the palette cache, both sliders, and `was_on` of the value
|
||||
overlay in both profiles, which covers and media players no longer reach; the top bar and cards don't use it).
|
||||
Widening `active()` would only have changed slider colours, but the closed cover needs a slider rule anyway, so the fix
|
||||
is a separate `Tile::slider_active()` and `active()` stays as it was. See CHANGELOG 0.2.60 and docs/RELEASING.md.
|
||||
|
||||
## Automated
|
||||
|
||||
- C++ (`clang++ -std=c++17 -Wall -Wextra -Werror -I. -I components/smart_display`): 15/15 PASS, on HEAD plus only this
|
||||
change in a separate worktree. `test_runtime_model` adds `test_slider_colours`: a light and a fan on and off; a media
|
||||
player playing, paused, idle, buffering and on (coloured) against off, standby, unavailable and unknown (grey); a cover
|
||||
open, opening, closing and closed (coloured) against unavailable; a number and an input_number with a value against
|
||||
unknown; a tile that has received nothing. The test fails on the old rule (compiled against
|
||||
`slider_active() { return active(); }`).
|
||||
- Python (`.venv-portal`): 282 tests OK in that worktree, with the version bump. `generate_packages.py --check` green.
|
||||
|
||||
## Builds (ESPHome 2026.6.2)
|
||||
|
||||
ESP32 check profiles with the generated packages and local components. They were built from the shared working tree,
|
||||
which also held other sessions' unfinished edits; the version bump alone was not built again.
|
||||
|
||||
| Profile | RAM | Flash |
|
||||
|---|---|---|
|
||||
| CYD | 23.5% (77,060 B) | 77.8% (1,427,919 B) |
|
||||
| Guition | 24.5% (80,436 B) | 18.8% (1,531,471 B) |
|
||||
|
||||
RAM is the same as in 0.2.59.
|
||||
|
||||
## Firmware on the Mac (host builds)
|
||||
|
||||
Guition and CYD host builds with a demo home that runs the real manager, on three pages: a playing Sonos (volume), an
|
||||
open blind (position) and a boiler setpoint (slider) on double-width tiles; a radio in standby, a closed blind and a
|
||||
paused speaker; small sliders for an idle speaker, a TV that is off, an open blind, a number, a lamp that is on and a
|
||||
hallway light that is off. Playing, paused and idle players show blue, blinds purple (the closed one as the short end
|
||||
at 0 %), numbers teal and the lamp amber; the radio in standby and the TV stay grey, and the light that is off shows
|
||||
its empty grey track as before. No compiler warnings from the smart_display headers.
|
||||
|
||||
## Not tested / for the owner
|
||||
|
||||
- Real hardware: nothing was flashed. After updating a screen, a playing speaker's volume slider and an open blind's
|
||||
position slider are coloured, and the speaker's slider turns grey in standby.
|
||||
- A media player that is off or in standby still shows its grey fill with the handle; Home Assistant hides the handle
|
||||
there. Left as it is.
|
||||
@@ -2,7 +2,7 @@
|
||||
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
|
||||
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.51"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.52"
|
||||
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
|
||||
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
|
||||
TOUCH_MOVE_LIMIT_PX: "0"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
############################################################
|
||||
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.51"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.52"
|
||||
# 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.
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
############################################################
|
||||
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.51"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.52"
|
||||
# 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.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
|
||||
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.51"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.52"
|
||||
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
|
||||
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
|
||||
TOUCH_MOVE_LIMIT_PX: "0"
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## 0.2.60 (firmware 0.2.52)
|
||||
|
||||
Sliders keep their colour while a speaker plays or a blind is open.
|
||||
|
||||
- **Coloured sliders for speakers, blinds and numbers.** Since 0.2.42 the volume slider of a playing speaker, the position slider of a blind or curtain and the slider of a number were grey, the colour of a light that is off. Sliders now follow Home Assistant's tile sliders: a speaker that plays, is paused or is idle shows its volume in blue, a cover its position in purple, also when it is closed, and a number its value in teal. A speaker that is off or in standby, and a light or fan that is off, stay grey. This goes for the sliders on double-width tiles and for the small sliders on single tiles.
|
||||
- Needs firmware 0.2.52: press **Update** on the screen.
|
||||
|
||||
## 0.2.59 (firmware 0.2.51)
|
||||
|
||||
A history card you can read: axes, an hour, a day or a week, and the value under your finger.
|
||||
|
||||
@@ -17,7 +17,7 @@ WEEKDAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
|
||||
REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
|
||||
REFS = {'cyd': 'main', 'guition': 'main'}
|
||||
# Firmware shipped with this app release; screens below it get an update offer.
|
||||
FIRMWARE_VERSION = '0.2.51'
|
||||
FIRMWARE_VERSION = '0.2.52'
|
||||
# The Auto standby switch a screen offers Home Assistant automations.
|
||||
AUTO_STANDBY_MIN_FIRMWARE = '0.2.41'
|
||||
# The settings page the screen opens itself, and the screen.settings tile that opens it.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ESP Screen Manager
|
||||
version: "0.2.59"
|
||||
version: "0.2.60"
|
||||
slug: esp_screen_manager
|
||||
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||
|
||||
@@ -132,3 +132,25 @@ static void test_extra() {
|
||||
assert(m.set_layout({"light.a"}, "Home", changed) && changed && !m.tiles[0].extra_ptr());
|
||||
}
|
||||
struct RunTileExtra { RunTileExtra() { test_extra(); } } run_tile_extra;
|
||||
// Sliders keep their colour the way Home Assistant's tile sliders do: grey for an off light or fan and a media
|
||||
// player that is off or in standby, coloured for a player that plays, a cover open or closed and a number.
|
||||
static void test_slider_colours() {
|
||||
using namespace runtime_tiles;
|
||||
Model m; bool changed = false;
|
||||
assert(m.set_layout({"light.a", "fan.b", "media_player.c", "cover.d", "number.e", "input_number.f"}, "Home", changed));
|
||||
auto &light = m.tiles[0], &fan = m.tiles[1], &player = m.tiles[2], &cover = m.tiles[3], &number = m.tiles[4], &setpoint = m.tiles[5];
|
||||
player.state = "playing"; assert(!player.slider_active()); // nothing received yet
|
||||
for (size_t i = 0; i < m.count; ++i) m.tiles[i].received = true;
|
||||
light.state = "on"; assert(light.slider_active());
|
||||
light.state = "off"; assert(!light.slider_active());
|
||||
fan.state = "on"; assert(fan.slider_active());
|
||||
fan.state = "off"; assert(!fan.slider_active());
|
||||
for (const char *state : {"playing", "paused", "idle", "buffering", "on"}) { player.state = state; assert(player.slider_active()); }
|
||||
for (const char *state : {"off", "standby", "unavailable", "unknown"}) { player.state = state; assert(!player.slider_active()); }
|
||||
for (const char *state : {"open", "opening", "closing", "closed"}) { cover.state = state; assert(cover.slider_active()); }
|
||||
cover.state = "unavailable"; assert(!cover.slider_active());
|
||||
number.state = "21.5"; assert(number.slider_active());
|
||||
number.state = "unknown"; assert(!number.slider_active());
|
||||
setpoint.state = "55.0"; assert(setpoint.slider_active());
|
||||
}
|
||||
struct RunSliderColours { RunSliderColours() { test_slider_colours(); } } run_slider_colours;
|
||||
|
||||
Reference in New Issue
Block a user