Second hand on the analog clock, sliders like Home Assistant's, one top bar on every card (0.2.43)
Firmware 0.2.37 for both boards: - The analog clock card gets a thin red second hand that moves every second while the screen is awake (runtime_tiles::screen_awake, set by the profiles from display_dimmed); in standby it hides and the clock keeps its once-a-minute redraw. - Card sliders follow HA's control slider: 12/42 and 8/42 corners, a 4 px handle an eighth of the height from the fill end, a stub of a third of the height at 0 or 1 %, the track in the fill colour at 20 %. An off light or fan shows only the empty grey track, also in the colour card's brightness row. A light's slider floors at 1 %. - Every overlay card shares one top bar: a round back arrow at the left (60/40 px), the name centred, and only the climate power as a right-hand action. The "done"/"ok" buttons of the colour and vacuum cards are gone; the brightness card gets its arrow. - The top bar reads "Connecting to Home Assistant..." / "Waiting for ESP Screens..." until the first layout, instead of "Choose tiles in HA". Checked with the host renderer for both boards and on the Guition over USB. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -40,7 +40,7 @@ namespace light_controls {
|
||||
inline State states[8];
|
||||
inline State fallback;
|
||||
inline State *active = &fallback;
|
||||
struct Row { lv_obj_t *box{}, *slider{}, *value{}; bool dirty = false; unsigned index{}; };
|
||||
struct Row { lv_obj_t *box{}, *slider{}, *value{}; bool dirty = false; unsigned index{}; bool off = false; };
|
||||
inline Row rows[3];
|
||||
inline std::function<void(int)> commits[3];
|
||||
inline bool ready = false;
|
||||
@@ -81,15 +81,20 @@ inline void preview(Row &row) {
|
||||
lv_label_set_text_fmt(row.value, "%d K", value);
|
||||
lv_obj_set_style_bg_color(row.slider, kelvin_color(value, lv_slider_get_min_value(row.slider), lv_slider_get_max_value(row.slider)), LV_PART_KNOB);
|
||||
} else {
|
||||
// The range reaches below 1 only to draw the round end that holds the handle at 1 %.
|
||||
// The range reaches below 1 only to draw the short stub that holds the handle at 1 %.
|
||||
if (value < 1) { lv_slider_set_value(row.slider, 1, LV_ANIM_OFF); value = 1; }
|
||||
lv_label_set_text_fmt(row.value, "%d %%", value);
|
||||
// An off light shows only the track, as in Home Assistant, until the slider moves.
|
||||
lv_obj_set_style_bg_opa(row.slider, row.off ? LV_OPA_TRANSP : LV_OPA_COVER, LV_PART_INDICATOR);
|
||||
lv_obj_set_style_bg_opa(row.slider, row.off ? LV_OPA_TRANSP : LV_OPA_COVER, LV_PART_KNOB);
|
||||
lv_obj_set_style_bg_color(row.slider, lv_color_hex(row.off ? 0xECECEC : 0xFDEFC3), LV_PART_MAIN);
|
||||
if (row.off) lv_label_set_text(row.value, "Off");
|
||||
else lv_label_set_text_fmt(row.value, "%d %%", value);
|
||||
}
|
||||
}
|
||||
inline void event(lv_event_t *e) {
|
||||
auto &row = *static_cast<Row *>(lv_event_get_user_data(e));
|
||||
auto code = lv_event_get_code(e);
|
||||
if (code == LV_EVENT_VALUE_CHANGED) { row.dirty = true; preview(row); }
|
||||
if (code == LV_EVENT_VALUE_CHANGED) { row.dirty = true; row.off = false; preview(row); }
|
||||
if (code == LV_EVENT_PRESS_LOST) row.dirty = false;
|
||||
if (code == LV_EVENT_RELEASED && row.dirty) {
|
||||
row.dirty = false;
|
||||
@@ -187,15 +192,16 @@ inline void setup(lv_obj_t *parent, const lv_font_t *font, int width, int height
|
||||
lv_obj_set_style_outline_opa(row.slider, LV_OPA_20, LV_PART_KNOB);
|
||||
lv_slider_set_range(row.slider, i == 0 ? 0 : 2000, i == 0 ? 360 : 6500);
|
||||
} else {
|
||||
// Track and fill corners, handle and shortest fill as in Home Assistant's 42 px control slider.
|
||||
lv_obj_set_pos(row.slider, track_x, track_y); lv_obj_set_size(row.slider, track_w, track_h);
|
||||
lv_obj_set_style_radius(row.slider, LV_RADIUS_CIRCLE, LV_PART_MAIN);
|
||||
lv_obj_set_style_radius(row.slider, LV_RADIUS_CIRCLE, LV_PART_INDICATOR);
|
||||
lv_obj_set_style_radius(row.slider, track_h * 12 / 42, LV_PART_MAIN);
|
||||
lv_obj_set_style_radius(row.slider, track_h * 8 / 42, LV_PART_INDICATOR);
|
||||
lv_obj_set_style_bg_opa(row.slider, LV_OPA_COVER, LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_opa(row.slider, LV_OPA_COVER, LV_PART_INDICATOR);
|
||||
lv_obj_set_style_bg_color(row.slider, lv_color_hex(0xFDEFC3), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(row.slider, lv_color_hex(0xFFC107), LV_PART_INDICATOR);
|
||||
// White handle inside the rounded end of the fill, as on the tiles (runtime_tiles::slider_handle).
|
||||
int handle = large ? 6 : 4, back = track_h / 4 + handle / 2, half = track_h >> 1;
|
||||
// White handle inside the end of the fill, as on the tiles (runtime_tiles::slider_handle).
|
||||
int handle = large ? 4 : 3, back = std::max(1, track_h / 8) + handle / 2, half = track_h >> 1;
|
||||
lv_obj_set_style_bg_opa(row.slider, LV_OPA_COVER, LV_PART_KNOB);
|
||||
lv_obj_set_style_bg_color(row.slider, lv_color_hex(0xFFFFFF), LV_PART_KNOB);
|
||||
lv_obj_set_style_radius(row.slider, 2, LV_PART_KNOB);
|
||||
@@ -203,8 +209,8 @@ inline void setup(lv_obj_t *parent, const lv_font_t *font, int width, int height
|
||||
lv_obj_set_style_pad_right(row.slider, handle / 2 - back - (track_h - half), LV_PART_KNOB);
|
||||
lv_obj_set_style_pad_top(row.slider, -(track_h / 4), LV_PART_KNOB);
|
||||
lv_obj_set_style_pad_bottom(row.slider, -(track_h / 4), LV_PART_KNOB);
|
||||
// 1 % still shows one round end: the range starts that far below 1.
|
||||
int below = track_w > track_h ? 99 * track_h / (track_w - track_h) : 0;
|
||||
// 1 % still shows a stub a third of the height wide: the range starts that far below 1.
|
||||
int stub = std::max(track_h / 3, 2 * std::max(1, track_h / 8) + handle), below = track_w > stub ? 99 * stub / (track_w - stub) : 0;
|
||||
lv_slider_set_range(row.slider, 1 - below, 100);
|
||||
}
|
||||
lv_obj_add_event_cb(row.slider, event, LV_EVENT_ALL, &row);
|
||||
@@ -235,6 +241,7 @@ inline void open(const std::string &entity, bool color, bool temperature, int br
|
||||
if (i == 1 && !active->temperature_ready()) lv_obj_add_state(row.slider, LV_STATE_DISABLED);
|
||||
if (i == 1 && active->temperature_ready()) lv_slider_set_range(row.slider, active->minimum, active->maximum);
|
||||
lv_slider_set_value(row.slider, i == 0 ? active->hue : i == 1 ? active->kelvin : clamp(brightness, 1, 100), LV_ANIM_OFF);
|
||||
row.off = i == 2 && brightness <= 0;
|
||||
preview(row); row.dirty = false;
|
||||
if (i == 1 && !active->temperature_ready()) lv_label_set_text(row.value, "Waiting...");
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ inline lv_obj_t *time_label = nullptr;
|
||||
inline const lv_font_t *header_text_font = nullptr, *header_icon_font = nullptr;
|
||||
inline void render_header();
|
||||
inline std::function<esphome::ESPTime()> now_time;
|
||||
// True while the screen is in use: the profile reports standby (also the night level) as not awake.
|
||||
// The analog clock's second hand runs only then; a standby screen stays on its minute redraws.
|
||||
inline std::function<bool()> screen_awake;
|
||||
inline bool awake() { return !screen_awake || screen_awake(); }
|
||||
inline uint32_t now_epoch() { if (!now_time) return 0; auto t = now_time(); return t.is_valid() ? static_cast<uint32_t>(t.timestamp) : 0; }
|
||||
inline void tick();
|
||||
inline void refresh_tile(size_t index);
|
||||
@@ -101,6 +105,8 @@ struct Widgets {
|
||||
int title_x=0,title_y=0,value_x=0,value_y=0; const lv_font_t *value_font{}, *icon_font{};
|
||||
// Wide cards span both columns; custom cards (clock, forecast, graph) draw into `extra`.
|
||||
bool wide=false; int base_width=0; lv_obj_t *extra{}; std::string extra_mode; std::array<lv_obj_t *, 20> parts{}; lv_point_precise_t *points{};
|
||||
// Analog clock: centre and radius of the dial, so the second hand can move without a card redraw.
|
||||
int hand_cx=0, hand_cy=0, hand_r=0, hand_width=1;
|
||||
// Soft area under a polyline (graph, sun path), painted by the extra container's draw event.
|
||||
const lv_point_precise_t *fill_points{}; unsigned fill_count=0; int fill_x=0, fill_y=0, fill_base=0; lv_color_t fill_color{}; lv_opa_t fill_opa=0;
|
||||
// Direct controls on a wide card: a panel at the right with pill keys, a -/+ pill,
|
||||
@@ -447,7 +453,8 @@ inline void slider_event(lv_event_t *e);
|
||||
inline void commit_slider(unsigned i,int raw){
|
||||
if(i>=model.count || !fresh())return;auto &t=model.tiles[i];if(!t.available() || t.loading(esphome::millis()))return;
|
||||
float value=std::clamp(raw,0,1000)/1000.0f;auto d=t.domain();
|
||||
if(d=="light")action("light.turn_on",t.entity,"brightness",std::to_string((int)std::lround(value*255)));
|
||||
// A light's slider stops at 1 %, as in Home Assistant; tapping the card turns it off.
|
||||
if(d=="light")action("light.turn_on",t.entity,"brightness",std::to_string(std::max(3,(int)std::lround(value*255))));
|
||||
if(d=="fan")action("fan.set_percentage",t.entity,"percentage",std::to_string((int)std::lround(value*100)));
|
||||
if(d=="cover")action("cover.set_cover_position",t.entity,"position",std::to_string((int)std::lround(value*100)));
|
||||
if(d=="media_player")action("media_player.volume_set",t.entity,"volume_level",std::to_string(value));
|
||||
@@ -601,10 +608,15 @@ inline void show_detail(unsigned index){
|
||||
lv_obj_set_style_bg_color(detail_root,lv_color_hex(0xE7E7E7),0);lv_obj_set_style_bg_opa(detail_root,LV_OPA_COVER,0);
|
||||
int width=lv_display_get_horizontal_resolution(lv_display_get_default()), height=lv_display_get_vertical_resolution(lv_display_get_default());
|
||||
bool large=width>=480;int pad=large?20:10, top=large?100:62, gap=large?12:6,bh=large?58:34,cw=(width-pad*2-gap)/2;
|
||||
auto *heading=detail_label(detail_root,t.name,pad,large?24:12,width-80);if(watch_font){lv_obj_set_style_text_font(heading,watch_font,0);lv_obj_set_height(heading,lv_font_get_line_height(watch_font));}
|
||||
detail_button("X",width-58,8,48,40,-1);
|
||||
// The same top bar as the board's own cards: a round back arrow at the left, the name centred.
|
||||
int bar=large?60:40,bar_x=large?16:10,bar_y=large?16:8;
|
||||
auto *back=detail_button("",bar_x,bar_y,bar,bar,-1);lv_obj_set_style_radius(back,LV_RADIUS_CIRCLE,0);lv_obj_set_style_bg_color(back,lv_color_hex(0xEEEEEE),0);
|
||||
auto *arrow=lv_obj_get_child(back,0);if(mini_icon_font)lv_obj_set_style_text_font(arrow,mini_icon_font,0);lv_label_set_text(arrow,"\U000F004D");lv_obj_set_size(arrow,LV_SIZE_CONTENT,LV_SIZE_CONTENT);lv_obj_center(arrow);
|
||||
const lv_font_t *title_font=watch_font?watch_font:detail_font;
|
||||
auto *heading=detail_label(detail_root,t.name,bar_x+bar+8,bar_y+(bar-lv_font_get_line_height(title_font))/2,width-2*(bar_x+bar+8));
|
||||
lv_obj_set_style_text_font(heading,title_font,0);lv_obj_set_height(heading,lv_font_get_line_height(title_font));lv_obj_set_style_text_align(heading,LV_TEXT_ALIGN_CENTER,0);
|
||||
std::string state=detail_state(t);
|
||||
detail_status=detail_label(detail_root,state+(t.unit.empty()?"":" "+t.unit),pad,large?60:35,width-2*pad);
|
||||
detail_status=detail_label(detail_root,state+(t.unit.empty()?"":" "+t.unit),pad,large?80:50,width-2*pad);lv_obj_set_style_text_align(detail_status,LV_TEXT_ALIGN_CENTER,0);lv_obj_set_style_text_color(detail_status,lv_color_hex(0x616161),0);
|
||||
auto d=t.domain();
|
||||
if(t.is_switch()){
|
||||
detail_label(detail_root,"Tap to toggle",pad,top,width-2*pad);
|
||||
@@ -851,19 +863,31 @@ inline void set_number(lv_obj_t *obj, lv_style_prop_t prop, int32_t number, lv_s
|
||||
lv_style_value_t value{}; value.num = number;
|
||||
lv_obj_set_local_style_prop(obj, prop, value, selector);
|
||||
}
|
||||
// The white handle of a card slider sits inside the rounded end of the fill, like Home Assistant's
|
||||
// slider. LVGL centres the knob on the end of the fill, so the knob is narrowed and moved back by a
|
||||
// quarter of the height; the range starts below zero, so at 0 the fill is still one round end that
|
||||
// holds the handle. Values below zero never leave the slider (see slider_event).
|
||||
// Card sliders follow Home Assistant's control slider (a 42 px track there): corners of 12 and
|
||||
// 8 px on track and fill, a white handle 4 px wide and half the height, an eighth of the height in
|
||||
// from the end of the fill, and a fill that never gets shorter than a third of the height. LVGL
|
||||
// centres the knob on the end of the fill, so the knob is narrowed and moved back; the range starts
|
||||
// below zero, so at 0 the fill is still that short stub holding the handle. Values below zero never
|
||||
// leave the slider (see slider_event). Small strips keep round ends: the corners would not show.
|
||||
inline int slider_handle_width(int height) { return height > 20 ? 4 : 2; }
|
||||
inline int slider_stub(int height) { return std::max(height / 3, 2 * std::max(1, height / 8) + slider_handle_width(height)); }
|
||||
inline void slider_handle(lv_obj_t *slider, int width, int height) {
|
||||
int handle = height > 20 ? 6 : 4, inset = height / 4 + handle / 2, half = height >> 1;
|
||||
int handle = slider_handle_width(height), inset = std::max(1, height / 8) + handle / 2, half = height >> 1;
|
||||
set_number(slider, LV_STYLE_RADIUS, height > 20 ? height * 12 / 42 : LV_RADIUS_CIRCLE, LV_PART_MAIN);
|
||||
set_number(slider, LV_STYLE_RADIUS, height > 20 ? height * 8 / 42 : LV_RADIUS_CIRCLE, LV_PART_INDICATOR);
|
||||
set_number(slider, LV_STYLE_PAD_LEFT, inset + handle / 2 - half, LV_PART_KNOB);
|
||||
set_number(slider, LV_STYLE_PAD_RIGHT, handle / 2 - inset - (height - half), LV_PART_KNOB);
|
||||
set_number(slider, LV_STYLE_PAD_TOP, -(height / 4), LV_PART_KNOB);
|
||||
set_number(slider, LV_STYLE_PAD_BOTTOM, -(height / 4), LV_PART_KNOB);
|
||||
int below = width > height ? 1000 * height / (width - height) : 0;
|
||||
int stub = slider_stub(height), below = width > stub ? 1000 * stub / (width - stub) : 0;
|
||||
if (lv_slider_get_min_value(slider) != -below || lv_slider_get_max_value(slider) != 1000) lv_slider_set_range(slider, -below, 1000);
|
||||
}
|
||||
// An off light or fan shows only the grey track, as in Home Assistant: no fill, no handle.
|
||||
inline void slider_bar(lv_obj_t *slider, bool shown) {
|
||||
set_number(slider, LV_STYLE_BG_OPA, shown ? LV_OPA_COVER : LV_OPA_TRANSP, LV_PART_INDICATOR);
|
||||
set_number(slider, LV_STYLE_BG_OPA, shown ? LV_OPA_COVER : LV_OPA_TRANSP, LV_PART_KNOB);
|
||||
}
|
||||
inline bool slider_bar_shown(const Tile &t, bool on) { auto d = t.domain(); return on || (d != "light" && d != "fan"); }
|
||||
inline void set_hidden(lv_obj_t *obj, bool hidden) { if (hidden) lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); else lv_obj_remove_flag(obj, LV_OBJ_FLAG_HIDDEN); }
|
||||
// A card's size as its styles request it. LVGL's own getters only follow after a layout pass, and
|
||||
// that pass walks every object on the screen, so the cards are laid out without asking for one.
|
||||
@@ -899,7 +923,7 @@ inline void bind(size_t index, lv_obj_t *tile, lv_obj_t *title, lv_obj_t *value,
|
||||
lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFFFFF),LV_PART_KNOB);lv_obj_set_style_bg_opa(w.slider,LV_OPA_COVER,LV_PART_KNOB);
|
||||
slider_handle(w.slider,lv_obj_get_width(tile)-24,strip);
|
||||
lv_obj_set_style_border_width(w.slider,0,LV_PART_KNOB);lv_obj_set_style_shadow_width(w.slider,0,LV_PART_KNOB);
|
||||
lv_obj_set_style_radius(w.slider,14,LV_PART_MAIN);lv_obj_set_style_radius(w.slider,14,LV_PART_INDICATOR);lv_obj_set_style_radius(w.slider,2,LV_PART_KNOB);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFCE5B4),LV_PART_MAIN);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFB900),LV_PART_INDICATOR);
|
||||
lv_obj_set_style_radius(w.slider,2,LV_PART_KNOB);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFCE5B4),LV_PART_MAIN);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFB900),LV_PART_INDICATOR);
|
||||
lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_remove_flag(w.slider,LV_OBJ_FLAG_GESTURE_BUBBLE);
|
||||
lv_obj_add_event_cb(w.slider,slider_event,LV_EVENT_ALL,(void*)(uintptr_t)index);
|
||||
@@ -1028,7 +1052,14 @@ inline std::string time_text(esphome::ESPTime now) {
|
||||
// large cards) with hour and minute hands. A single card adds a calendar block
|
||||
// beside the dial (weekday, big day number, short month); a wide card adds the
|
||||
// digital time and the date instead. Parts: 0-11 marks, 12-13 hands, 14 centre,
|
||||
// 15-17 text.
|
||||
// 15-17 text, 18 the second hand (points 28-29), shown while the screen is awake.
|
||||
inline void second_hand(Widgets &w,const esphome::ESPTime &now) {
|
||||
float a=(now.is_valid()?now.second:0)*3.14159265f/30;
|
||||
w.points[28]={(lv_value_precise_t)(w.hand_cx-w.hand_r*0.2f*sinf(a)),(lv_value_precise_t)(w.hand_cy+w.hand_r*0.2f*cosf(a))};
|
||||
w.points[29]={(lv_value_precise_t)(w.hand_cx+w.hand_r*0.92f*sinf(a)),(lv_value_precise_t)(w.hand_cy-w.hand_r*0.92f*cosf(a))};
|
||||
auto *p=part_line(w,18,w.points+28,2,w.hand_width);
|
||||
set_hidden(p,!(awake() && now.is_valid()));
|
||||
}
|
||||
inline void render_clock(Widgets &w,const Tile &t,bool large,int width,int height) {
|
||||
bool analog=t.display=="analog";
|
||||
begin_extra(w,analog?(w.wide?"analog":"calendar"):"digital",width,height);
|
||||
@@ -1065,6 +1096,11 @@ inline void render_clock(Widgets &w,const Tile &t,bool large,int width,int heigh
|
||||
w.points[2]={(lv_value_precise_t)cx,(lv_value_precise_t)cy};w.points[3]={(lv_value_precise_t)(cx+radius*0.82f*sinf(minute)),(lv_value_precise_t)(cy-radius*0.82f*cosf(minute))};
|
||||
part_line(w,12,w.points,2,large?5:3);part_line(w,13,w.points+2,2,large?3:2);
|
||||
int center=large?8:4;part_dot(w,14,cx-center/2,cy-center/2,center);
|
||||
// A thin red second hand with a short tail, under the centre dot; tick() moves it every second.
|
||||
w.hand_cx=cx;w.hand_cy=cy;w.hand_r=radius;w.hand_width=large?2:1;
|
||||
bool new_hand=!w.parts[18];
|
||||
second_hand(w,now);
|
||||
if(new_hand)lv_obj_move_to_index(w.parts[18],lv_obj_get_index(w.parts[14]));
|
||||
std::string day=now.is_valid()?std::to_string(now.day_of_month):"--";
|
||||
if(w.wide){
|
||||
int x=dial+(large?16:8),y=std::max(0,(height-text_h)/2);
|
||||
@@ -1257,7 +1293,6 @@ inline int layout_panel(Widgets &w,const Tile &t,bool large,int content_w,int co
|
||||
lv_obj_remove_flag(slider,LV_OBJ_FLAG_GESTURE_BUBBLE);lv_obj_remove_flag(slider,LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_set_size(slider,mode=="volume"?m.slider_w:m.pill_w,h);
|
||||
lv_obj_set_style_pad_all(slider,0,LV_PART_MAIN);
|
||||
lv_obj_set_style_radius(slider,LV_RADIUS_CIRCLE,LV_PART_MAIN);lv_obj_set_style_radius(slider,LV_RADIUS_CIRCLE,LV_PART_INDICATOR);
|
||||
lv_obj_set_style_bg_opa(slider,LV_OPA_COVER,LV_PART_MAIN);lv_obj_set_style_bg_opa(slider,LV_OPA_COVER,LV_PART_INDICATOR);
|
||||
// The handle is a short white bar inside the fill, like Home Assistant's slider.
|
||||
lv_obj_set_style_radius(slider,2,LV_PART_KNOB);lv_obj_set_style_bg_color(slider,lv_color_hex(0xFFFFFF),LV_PART_KNOB);lv_obj_set_style_bg_opa(slider,LV_OPA_COVER,LV_PART_KNOB);
|
||||
@@ -1355,9 +1390,12 @@ 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){
|
||||
lv_color_t fill=fresh() && t.active()?accent:lv_color_hex(0x9E9E9E);
|
||||
set_color(w.control_slider,LV_STYLE_BG_COLOR,lv_color_mix(fill,card,76),LV_PART_MAIN);
|
||||
bool on=fresh() && t.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);
|
||||
set_color(w.control_slider,LV_STYLE_BG_COLOR,fill,LV_PART_INDICATOR);
|
||||
slider_bar(w.control_slider,slider_bar_shown(t,on));
|
||||
}
|
||||
if(w.knob){set_color(w.keys[0],LV_STYLE_BG_COLOR,lv_color_hex(0xD7DADF));set_color(w.keys[0],LV_STYLE_BG_COLOR,lv_color_hex(0xC5C9CF),LV_STATE_PRESSED);}
|
||||
}
|
||||
@@ -1536,10 +1574,11 @@ inline void render_slot(size_t slot) {
|
||||
auto circle_color=available?lv_color_mix(color,lv_color_hex(0xFFFFFF),38):lv_color_hex(0xF0F0F0);
|
||||
// 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: the round end that holds the handle is grey, so the slider reads as off too.
|
||||
// Off: a grey track without fill or handle, as in Home Assistant.
|
||||
auto fill_color=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),30),LV_PART_MAIN);
|
||||
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));
|
||||
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,
|
||||
@@ -1563,7 +1602,7 @@ inline void render_slot(size_t slot) {
|
||||
bool muted=w.extra_mode=="forecast" ? i>=2 && i%3==2 : w.extra_mode=="sunpath" ? i>=1 : w.extra_mode=="calendar" ? i==15||i==17 : i==16;
|
||||
if(lv_obj_check_type(p,&lv_label_class))set_color(p,LV_STYLE_TEXT_COLOR,muted?value_color:title_color);
|
||||
else if(w.extra_mode=="sunpath")continue;
|
||||
else if(lv_obj_check_type(p,&lv_line_class))set_color(p,LV_STYLE_LINE_COLOR,w.extra_mode=="graph"?color:i<12?value_color:i==13?icon_color:title_color);
|
||||
else if(lv_obj_check_type(p,&lv_line_class))set_color(p,LV_STYLE_LINE_COLOR,w.extra_mode=="graph"?color:i==18?lv_color_hex(0xE53935):i<12?value_color:i==13?icon_color:title_color);
|
||||
else set_color(p,LV_STYLE_BG_COLOR,i==14?icon_color:value_color);
|
||||
}
|
||||
lap(swipe_profile::PALETTE);
|
||||
@@ -1586,10 +1625,11 @@ inline lv_timer_t *fill_timer=nullptr;
|
||||
inline bool fill_refreshed=false;
|
||||
inline uint32_t fill_step_ms=0;
|
||||
inline bool fill_cards(size_t cards);
|
||||
// Before the first layout the screen is starting: HA connects, then ESP Screens sends the tiles.
|
||||
inline void render(lv_obj_t *room) {
|
||||
if (!enabled) return;
|
||||
room_label=room; swipe_profile::Lap lap;
|
||||
label(room, !model.configured ? "Choose tiles in HA" : !model.ready() ? "Loading tiles..." : !ha_connected() ? "HA not connected" : !feed_alive() ? "ESP Screens not active" : model.title);
|
||||
label(room, !model.configured ? (!ha_connected() ? "Connecting to Home Assistant..." : "Waiting for ESP Screens...") : !model.ready() ? "Loading tiles..." : !ha_connected() ? "HA not connected" : !feed_alive() ? "ESP Screens not active" : model.title);
|
||||
render_header();
|
||||
lap(swipe_profile::HEADER);
|
||||
bool all=dirty_all || (!dirty_tiles && !dirty_header);
|
||||
@@ -2068,6 +2108,8 @@ inline void tick() {
|
||||
auto &w=widgets[slot];if(!w.tile || w.index>=model.count || lv_obj_has_flag(w.tile,LV_OBJ_FLAG_HIDDEN))continue;
|
||||
const auto &t=model.tiles[w.index];
|
||||
if((t.builtin() && new_minute) || (t.domain()=="timer" && t.state=="active") || (t.domain()=="sun" && second%60==0))card(w.index);
|
||||
// The second hand moves on its own: only its line is redrawn, and it hides during standby.
|
||||
else if(w.parts[18] && w.points && t.builtin() && t.display=="analog")second_hand(w,now);
|
||||
}
|
||||
}
|
||||
if(redraw && refresh)refresh();
|
||||
|
||||
@@ -175,6 +175,7 @@ inline constexpr Named NAMES[] = {
|
||||
{"chevron-left", 0xF0141},
|
||||
{"chevron-right", 0xF0142},
|
||||
{"close", 0xF0156},
|
||||
{"arrow-left", 0xF004D},
|
||||
{"sun-snowflake-variant", 0xF1A79},
|
||||
{"thermostat-auto", 0xF1B17},
|
||||
{"weather-fog", 0xF0591},
|
||||
|
||||
@@ -176,6 +176,28 @@ 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.43 / firmware 0.2.37
|
||||
|
||||
Firmware only: storage version, tile protocol, preferences and keys are unchanged.
|
||||
|
||||
- `runtime_tiles::screen_awake` is a hook both profiles set to `!id(display_dimmed)`; without it
|
||||
the runtime counts as awake. `second_hand()` draws part 18 (points 28-29) of an analog clock
|
||||
card and `tick()` moves it once a second while awake, hiding it otherwise; the minute redraw
|
||||
stays as it was.
|
||||
- `slider_handle()` now sets the track and fill radii (12/42 and 8/42 of the height, round ends
|
||||
under 20 px), a handle of 4 px (2 px on small strips) an eighth of the height from the end, and a
|
||||
shortest fill of a third of the height (`slider_stub`). `slider_bar()` hides fill and knob for
|
||||
an off light or fan (`slider_bar_shown`); tracks are the fill colour at 20 % over the card.
|
||||
`light_controls::Row::off` does the same for the colour card's brightness row.
|
||||
- `commit_slider` floors a light's brightness at 3 (1 %); the card tap turns it off.
|
||||
- Overlay top bar: the profiles' `*_close_button`, `climate_mode_overlay_x`, `vacuum_overlay_x` and
|
||||
the new `overlay_back_button` are one round `arrow-left` (glyph `F004D`, added to
|
||||
`tile_icons.FIXED`) button at the top left, 60/40 px (Guition/CYD) at (16,16)/(10,8);
|
||||
`climate_power_button` is the same size at the top right. `color_done_button` and
|
||||
`vacuum_overlay_ok` are gone; `show_detail()` draws the same bar for the runtime detail card.
|
||||
- `render()` shows "Connecting to Home Assistant..." / "Waiting for ESP Screens..." until the
|
||||
first layout arrives.
|
||||
|
||||
### Compatibility 0.2.42 / firmware 0.2.36
|
||||
|
||||
Firmware only: storage version, tile protocol, preferences and keys are unchanged. What moved
|
||||
|
||||
+74
-106
@@ -1,7 +1,7 @@
|
||||
# Guition ESP32-S3-4848S040 / 4-inch EU wallbox, 480x480 IPS + GT911.
|
||||
# Use guition-device.yaml; see docs/GUITION.md. CYD hardware is a separate profile.
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.36"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.37"
|
||||
# 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"
|
||||
@@ -641,6 +641,7 @@ esphome:
|
||||
runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font();
|
||||
runtime_tiles::clock_font = id(clock_digits)->get_lv_font();
|
||||
runtime_tiles::now_time = []() { return id(homeassistant_time).now(); };
|
||||
runtime_tiles::screen_awake = []() { return !id(display_dimmed); };
|
||||
runtime_tiles::mini_icon_font = id(materialdesign_icons_mini)->get_lv_font();
|
||||
runtime_tiles::control_font = id(sublabel_big)->get_lv_font();
|
||||
// Top bar (0.2.32+): values in the text font, icons in the small icon font; the clock
|
||||
@@ -1342,6 +1343,7 @@ font:
|
||||
- "\U000F0141" # chevron-left
|
||||
- "\U000F0142" # chevron-right
|
||||
- "\U000F0156" # close
|
||||
- "\U000F004D" # arrow-left
|
||||
- "\U000F1A79" # sun-snowflake-variant
|
||||
- "\U000F1B17" # thermostat-auto
|
||||
- "\U000F0591" # weather-fog
|
||||
@@ -3678,10 +3680,33 @@ lvgl:
|
||||
clickable: false
|
||||
scrollable: false
|
||||
|
||||
- obj:
|
||||
id: overlay_back_button
|
||||
align: TOP_LEFT
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: overlay_title
|
||||
align: TOP_MID
|
||||
y: 20
|
||||
y: 30
|
||||
text: "Light"
|
||||
styles: style_room
|
||||
|
||||
@@ -3992,45 +4017,10 @@ lvgl:
|
||||
- obj:
|
||||
id: color_close_button
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 310
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
- obj:
|
||||
id: color_done_button
|
||||
align: TOP_RIGHT
|
||||
x: -15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -4042,11 +4032,22 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 310
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 35
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
|
||||
# ---------- CLIMATE DETAIL OVERLAY ----------
|
||||
- obj:
|
||||
id: climate_detail_overlay
|
||||
@@ -4089,18 +4090,14 @@ lvgl:
|
||||
- obj:
|
||||
id: climate_close_button
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
# 44 instead of 36: slightly bigger tap target, grows away from the arc
|
||||
# (top-left anchor stays put) instead of towards it.
|
||||
width: 66
|
||||
height: 66
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4108,18 +4105,18 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
id: climate_power_button
|
||||
align: TOP_RIGHT
|
||||
x: -15
|
||||
y: 18
|
||||
width: 66
|
||||
height: 66
|
||||
x: -16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -4140,7 +4137,7 @@ lvgl:
|
||||
- label:
|
||||
id: climate_detail_title
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
y: 35
|
||||
text: "Climate"
|
||||
styles: style_room
|
||||
|
||||
@@ -4311,7 +4308,7 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
y: 35
|
||||
text: "Mode"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
@@ -4684,16 +4681,14 @@ lvgl:
|
||||
- obj:
|
||||
id: climate_mode_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xFFFFFF
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xDDDDDD
|
||||
border_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4701,8 +4696,8 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
@@ -4727,10 +4722,9 @@ lvgl:
|
||||
- label:
|
||||
id: vacuum_title
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
y: 35
|
||||
text: "Vacuum"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
styles: style_room
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
@@ -4948,38 +4942,13 @@ lvgl:
|
||||
- obj:
|
||||
id: vacuum_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
id: vacuum_overlay_ok
|
||||
align: TOP_RIGHT
|
||||
x: -15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
radius: 999
|
||||
bg_color: 0xD5EEFC
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
@@ -4988,11 +4957,12 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
|
||||
# ---------- DIM / WAKE OVERLAY ----------
|
||||
- obj:
|
||||
id: dim_wake_overlay
|
||||
@@ -5691,12 +5661,10 @@ script:
|
||||
light_controls::open("diagnostic", true, true, 75);
|
||||
lv_label_set_text(id(color_detail_title), "Example lamp");
|
||||
- lvgl.widget.hide: color_close_button
|
||||
- lvgl.widget.hide: color_done_button
|
||||
- lvgl.widget.show: color_detail_overlay
|
||||
- delay: 40s
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.show: color_close_button
|
||||
- lvgl.widget.show: color_done_button
|
||||
- lambda: 'light_controls::demo = false; light_controls::fallback = light_controls::State{};'
|
||||
|
||||
- id: open_color_detail
|
||||
|
||||
+71
-103
@@ -57,7 +57,7 @@
|
||||
############################################################
|
||||
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.36"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.37"
|
||||
# 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.
|
||||
@@ -823,6 +823,7 @@ esphome:
|
||||
runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font();
|
||||
runtime_tiles::clock_font = id(clock_digits)->get_lv_font();
|
||||
runtime_tiles::now_time = []() { return id(homeassistant_time).now(); };
|
||||
runtime_tiles::screen_awake = []() { return !id(display_dimmed); };
|
||||
runtime_tiles::mini_icon_font = id(materialdesign_icons_mini)->get_lv_font();
|
||||
runtime_tiles::control_font = id(sublabel_big)->get_lv_font();
|
||||
// Top bar (0.2.32+): values in the text font, icons in the small icon font; the clock
|
||||
@@ -1531,6 +1532,7 @@ font:
|
||||
- "\U000F0141" # chevron-left
|
||||
- "\U000F0142" # chevron-right
|
||||
- "\U000F0156" # close
|
||||
- "\U000F004D" # arrow-left
|
||||
- "\U000F1A79" # sun-snowflake-variant
|
||||
- "\U000F1B17" # thermostat-auto
|
||||
- "\U000F0591" # weather-fog
|
||||
@@ -3797,24 +3799,47 @@ lvgl:
|
||||
clickable: false
|
||||
scrollable: false
|
||||
|
||||
- obj:
|
||||
id: overlay_back_button
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: overlay_title
|
||||
align: TOP_MID
|
||||
y: 10
|
||||
y: 19
|
||||
text: "Light"
|
||||
styles: style_room
|
||||
|
||||
- label:
|
||||
id: overlay_value
|
||||
align: TOP_MID
|
||||
y: 35
|
||||
y: 42
|
||||
text: "100 %"
|
||||
styles: style_value_big
|
||||
|
||||
- slider:
|
||||
id: overlay_slider
|
||||
align: CENTER
|
||||
y: 18
|
||||
y: 24
|
||||
width: ${OVERLAY_SLIDER_W}
|
||||
height: ${OVERLAY_SLIDER_H}
|
||||
min_value: 0
|
||||
@@ -4112,44 +4137,9 @@ lvgl:
|
||||
id: color_close_button
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 210
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
- obj:
|
||||
id: color_done_button
|
||||
align: TOP_RIGHT
|
||||
x: -10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -4161,11 +4151,22 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 210
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 21
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
|
||||
# ---------- CLIMATE DETAIL OVERLAY ----------
|
||||
- obj:
|
||||
id: climate_detail_overlay
|
||||
@@ -4209,17 +4210,13 @@ lvgl:
|
||||
id: climate_close_button
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
# 44 instead of 36: slightly bigger tap target, grows away from the arc
|
||||
# (top-left anchor stays put) instead of towards it.
|
||||
width: 44
|
||||
height: 44
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4227,8 +4224,8 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
@@ -4236,9 +4233,9 @@ lvgl:
|
||||
id: climate_power_button
|
||||
align: TOP_RIGHT
|
||||
x: -10
|
||||
y: 9
|
||||
width: 44
|
||||
height: 44
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -4259,7 +4256,7 @@ lvgl:
|
||||
- label:
|
||||
id: climate_detail_title
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
y: 21
|
||||
text: "Climate"
|
||||
styles: style_room
|
||||
|
||||
@@ -4430,7 +4427,7 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
y: 21
|
||||
text: "Mode"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
@@ -4804,15 +4801,13 @@ lvgl:
|
||||
id: climate_mode_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xFFFFFF
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xDDDDDD
|
||||
border_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4820,8 +4815,8 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
@@ -4846,10 +4841,9 @@ lvgl:
|
||||
- label:
|
||||
id: vacuum_title
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
y: 21
|
||||
text: "Vacuum"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
styles: style_room
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
@@ -5068,37 +5062,12 @@ lvgl:
|
||||
id: vacuum_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
id: vacuum_overlay_ok
|
||||
align: TOP_RIGHT
|
||||
x: -10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
radius: 999
|
||||
bg_color: 0xD5EEFC
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
@@ -5107,11 +5076,12 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
|
||||
# ---------- DIM / WAKE OVERLAY ----------
|
||||
- obj:
|
||||
id: dim_wake_overlay
|
||||
@@ -5786,12 +5756,10 @@ script:
|
||||
light_controls::open("diagnostic", true, true, 75);
|
||||
lv_label_set_text(id(color_detail_title), "Example lamp");
|
||||
- lvgl.widget.hide: color_close_button
|
||||
- lvgl.widget.hide: color_done_button
|
||||
- lvgl.widget.show: color_detail_overlay
|
||||
- delay: 40s
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.show: color_close_button
|
||||
- lvgl.widget.show: color_done_button
|
||||
- lambda: 'light_controls::demo = false; light_controls::fallback = light_controls::State{};'
|
||||
|
||||
- id: open_color_detail
|
||||
|
||||
+71
-103
@@ -58,7 +58,7 @@
|
||||
############################################################
|
||||
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.36"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.37"
|
||||
# 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.
|
||||
@@ -827,6 +827,7 @@ esphome:
|
||||
runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font();
|
||||
runtime_tiles::clock_font = id(clock_digits)->get_lv_font();
|
||||
runtime_tiles::now_time = []() { return id(homeassistant_time).now(); };
|
||||
runtime_tiles::screen_awake = []() { return !id(display_dimmed); };
|
||||
runtime_tiles::mini_icon_font = id(materialdesign_icons_mini)->get_lv_font();
|
||||
runtime_tiles::control_font = id(sublabel_big)->get_lv_font();
|
||||
// Top bar (0.2.32+): values in the text font, icons in the small icon font; the clock
|
||||
@@ -1520,6 +1521,7 @@ font:
|
||||
- "\U000F0141" # chevron-left
|
||||
- "\U000F0142" # chevron-right
|
||||
- "\U000F0156" # close
|
||||
- "\U000F004D" # arrow-left
|
||||
- "\U000F1A79" # sun-snowflake-variant
|
||||
- "\U000F1B17" # thermostat-auto
|
||||
- "\U000F0591" # weather-fog
|
||||
@@ -3728,24 +3730,47 @@ lvgl:
|
||||
clickable: false
|
||||
scrollable: false
|
||||
|
||||
- obj:
|
||||
id: overlay_back_button
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: overlay_title
|
||||
align: TOP_MID
|
||||
y: 10
|
||||
y: 19
|
||||
text: "Light"
|
||||
styles: style_room
|
||||
|
||||
- label:
|
||||
id: overlay_value
|
||||
align: TOP_MID
|
||||
y: 35
|
||||
y: 42
|
||||
text: "100 %"
|
||||
styles: style_value_big
|
||||
|
||||
- slider:
|
||||
id: overlay_slider
|
||||
align: CENTER
|
||||
y: 18
|
||||
y: 24
|
||||
width: ${OVERLAY_SLIDER_W}
|
||||
height: ${OVERLAY_SLIDER_H}
|
||||
min_value: 0
|
||||
@@ -4043,44 +4068,9 @@ lvgl:
|
||||
id: color_close_button
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 210
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
- obj:
|
||||
id: color_done_button
|
||||
align: TOP_RIGHT
|
||||
x: -10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -4092,11 +4082,22 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 210
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 21
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
|
||||
# ---------- CLIMATE DETAIL OVERLAY ----------
|
||||
- obj:
|
||||
id: climate_detail_overlay
|
||||
@@ -4140,17 +4141,13 @@ lvgl:
|
||||
id: climate_close_button
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
# 44 instead of 36: slightly bigger tap target, grows away from the arc
|
||||
# (top-left anchor stays put) instead of towards it.
|
||||
width: 44
|
||||
height: 44
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4158,8 +4155,8 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
@@ -4167,9 +4164,9 @@ lvgl:
|
||||
id: climate_power_button
|
||||
align: TOP_RIGHT
|
||||
x: -10
|
||||
y: 9
|
||||
width: 44
|
||||
height: 44
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -4190,7 +4187,7 @@ lvgl:
|
||||
- label:
|
||||
id: climate_detail_title
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
y: 21
|
||||
text: "Climate"
|
||||
styles: style_room
|
||||
|
||||
@@ -4361,7 +4358,7 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
y: 21
|
||||
text: "Mode"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
@@ -4735,15 +4732,13 @@ lvgl:
|
||||
id: climate_mode_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xFFFFFF
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xDDDDDD
|
||||
border_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4751,8 +4746,8 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
@@ -4777,10 +4772,9 @@ lvgl:
|
||||
- label:
|
||||
id: vacuum_title
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
y: 21
|
||||
text: "Vacuum"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
styles: style_room
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
@@ -4999,37 +4993,12 @@ lvgl:
|
||||
id: vacuum_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
y: 8
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
id: vacuum_overlay_ok
|
||||
align: TOP_RIGHT
|
||||
x: -10
|
||||
y: 9
|
||||
width: 36
|
||||
height: 36
|
||||
radius: 999
|
||||
bg_color: 0xD5EEFC
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
@@ -5038,11 +5007,12 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
|
||||
# ---------- DIM / WAKE OVERLAY ----------
|
||||
- obj:
|
||||
id: dim_wake_overlay
|
||||
@@ -5717,12 +5687,10 @@ script:
|
||||
light_controls::open("diagnostic", true, true, 75);
|
||||
lv_label_set_text(id(color_detail_title), "Example lamp");
|
||||
- lvgl.widget.hide: color_close_button
|
||||
- lvgl.widget.hide: color_done_button
|
||||
- lvgl.widget.show: color_detail_overlay
|
||||
- delay: 40s
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.show: color_close_button
|
||||
- lvgl.widget.show: color_done_button
|
||||
- lambda: 'light_controls::demo = false; light_controls::fallback = light_controls::State{};'
|
||||
|
||||
- id: open_color_detail
|
||||
|
||||
+74
-106
@@ -2,7 +2,7 @@
|
||||
# Guition ESP32-S3-4848S040 / 4-inch EU wallbox, 480x480 IPS + GT911.
|
||||
# Use guition-device.yaml; see docs/GUITION.md. CYD hardware is a separate profile.
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.36"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.37"
|
||||
# 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"
|
||||
@@ -645,6 +645,7 @@ esphome:
|
||||
runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font();
|
||||
runtime_tiles::clock_font = id(clock_digits)->get_lv_font();
|
||||
runtime_tiles::now_time = []() { return id(homeassistant_time).now(); };
|
||||
runtime_tiles::screen_awake = []() { return !id(display_dimmed); };
|
||||
runtime_tiles::mini_icon_font = id(materialdesign_icons_mini)->get_lv_font();
|
||||
runtime_tiles::control_font = id(sublabel_big)->get_lv_font();
|
||||
// Top bar (0.2.32+): values in the text font, icons in the small icon font; the clock
|
||||
@@ -1331,6 +1332,7 @@ font:
|
||||
- "\U000F0141" # chevron-left
|
||||
- "\U000F0142" # chevron-right
|
||||
- "\U000F0156" # close
|
||||
- "\U000F004D" # arrow-left
|
||||
- "\U000F1A79" # sun-snowflake-variant
|
||||
- "\U000F1B17" # thermostat-auto
|
||||
- "\U000F0591" # weather-fog
|
||||
@@ -3609,10 +3611,33 @@ lvgl:
|
||||
clickable: false
|
||||
scrollable: false
|
||||
|
||||
- obj:
|
||||
id: overlay_back_button
|
||||
align: TOP_LEFT
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: overlay_title
|
||||
align: TOP_MID
|
||||
y: 20
|
||||
y: 30
|
||||
text: "Light"
|
||||
styles: style_room
|
||||
|
||||
@@ -3923,45 +3948,10 @@ lvgl:
|
||||
- obj:
|
||||
id: color_close_button
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 310
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
- obj:
|
||||
id: color_done_button
|
||||
align: TOP_RIGHT
|
||||
x: -15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -3973,11 +3963,22 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- label:
|
||||
id: color_detail_title
|
||||
width: 310
|
||||
long_mode: DOT
|
||||
text_align: CENTER
|
||||
align: TOP_MID
|
||||
y: 35
|
||||
text: "Light Color"
|
||||
styles: style_room
|
||||
|
||||
|
||||
# ---------- CLIMATE DETAIL OVERLAY ----------
|
||||
- obj:
|
||||
id: climate_detail_overlay
|
||||
@@ -4020,18 +4021,14 @@ lvgl:
|
||||
- obj:
|
||||
id: climate_close_button
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
# 44 instead of 36: slightly bigger tap target, grows away from the arc
|
||||
# (top-left anchor stays put) instead of towards it.
|
||||
width: 66
|
||||
height: 66
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4039,18 +4036,18 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
id: climate_power_button
|
||||
align: TOP_RIGHT
|
||||
x: -15
|
||||
y: 18
|
||||
width: 66
|
||||
height: 66
|
||||
x: -16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
@@ -4071,7 +4068,7 @@ lvgl:
|
||||
- label:
|
||||
id: climate_detail_title
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
y: 35
|
||||
text: "Climate"
|
||||
styles: style_room
|
||||
|
||||
@@ -4242,7 +4239,7 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
y: 35
|
||||
text: "Mode"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
@@ -4615,16 +4612,14 @@ lvgl:
|
||||
- obj:
|
||||
id: climate_mode_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xFFFFFF
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xDDDDDD
|
||||
border_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
@@ -4632,8 +4627,8 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
@@ -4658,10 +4653,9 @@ lvgl:
|
||||
- label:
|
||||
id: vacuum_title
|
||||
align: TOP_MID
|
||||
y: 32
|
||||
y: 35
|
||||
text: "Vacuum"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
styles: style_room
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
@@ -4879,38 +4873,13 @@ lvgl:
|
||||
- obj:
|
||||
id: vacuum_overlay_x
|
||||
align: TOP_LEFT
|
||||
x: 15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
x: 16
|
||||
y: 16
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 999
|
||||
bg_color: 0xEEEEEE
|
||||
bg_opa: COVER
|
||||
border_width: 1
|
||||
border_color: 0xFFFFFF
|
||||
border_opa: 20%
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "x"
|
||||
text_font: sublabel_big
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
- obj:
|
||||
id: vacuum_overlay_ok
|
||||
align: TOP_RIGHT
|
||||
x: -15
|
||||
y: 18
|
||||
width: 54
|
||||
height: 54
|
||||
radius: 999
|
||||
bg_color: 0xD5EEFC
|
||||
bg_opa: COVER
|
||||
border_width: 0
|
||||
clickable: true
|
||||
scrollable: false
|
||||
@@ -4919,11 +4888,12 @@ lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "\U000F012C"
|
||||
text_font: materialdesign_icons
|
||||
text: "\U000F004D"
|
||||
text_font: materialdesign_icons_mini
|
||||
text_color: 0x1B1B1B
|
||||
clickable: false
|
||||
|
||||
|
||||
# ---------- DIM / WAKE OVERLAY ----------
|
||||
- obj:
|
||||
id: dim_wake_overlay
|
||||
@@ -5622,12 +5592,10 @@ script:
|
||||
light_controls::open("diagnostic", true, true, 75);
|
||||
lv_label_set_text(id(color_detail_title), "Example lamp");
|
||||
- lvgl.widget.hide: color_close_button
|
||||
- lvgl.widget.hide: color_done_button
|
||||
- lvgl.widget.show: color_detail_overlay
|
||||
- delay: 40s
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- lvgl.widget.show: color_close_button
|
||||
- lvgl.widget.show: color_done_button
|
||||
- lambda: 'light_controls::demo = false; light_controls::fallback = light_controls::State{};'
|
||||
|
||||
- id: open_color_detail
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
## 0.2.43 (firmware 0.2.37)
|
||||
|
||||
A second hand on the analog clock, sliders that look like Home Assistant's, and one top bar for every card.
|
||||
|
||||
- **Second hand.** The analog clock card gets a thin red second hand with a short tail that moves every second while the screen is in use. During standby (also at the night level) it is hidden and the clock keeps its once-a-minute redraw, so a sleeping screen does no extra work. Only the hand's own line is redrawn, not the card. The small dial in the top bar stays as it was.
|
||||
- **Sliders like Home Assistant.** Card sliders (a light's or fan's direct control, the Sonos volume, the brightness row of the colour card) now use the proportions of HA's control slider: softer corners on track and fill, a slimmer white handle a little in from the end of the fill, a track in the fill colour at 20 %, and a short stub instead of a full circle at 0 or 1 %. A light or fan that is off shows only the empty grey track, without fill or handle, exactly as HA does; the colour card's brightness row then reads Off until you move it.
|
||||
- **A light's slider stops at 1 %.** As in HA, dragging a light's slider all the way down leaves it on at 1 %; tapping the card turns it off.
|
||||
- **One top bar on every card.** Every card that opens over the tiles (brightness, colour, climate, vacuum, media, timer, switch, sensor history and the others) has the same bar: a round back arrow at the left, big enough for a finger (60 px on the Guition, 40 px on the CYD), and the name in the middle. Only a card with one overriding action keeps a round button at the right, such as the on/off of a climate device; the colour and vacuum cards lose their extra "done" buttons, which only closed the card. The brightness card, which had no button at all, gets the back arrow too.
|
||||
- **A starting screen says so.** Until the first layout arrives the top bar reads "Connecting to Home Assistant..." and then "Waiting for ESP Screens...", instead of "Choose tiles in HA".
|
||||
- Firmware 0.2.37 for both boards; the app offers the update.
|
||||
|
||||
## 0.2.42 (firmware 0.2.36)
|
||||
|
||||
Smoother page swipes and a new colour card for lights.
|
||||
|
||||
@@ -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.36'
|
||||
FIRMWARE_VERSION = '0.2.37'
|
||||
ATTRS = frozenset('brightness percentage current_position current_temperature temperature current_humidity min_temp max_temp target_temp_step supported_color_modes hvac_modes hvac_action hs_color color_temp_kelvin min_color_temp_kelvin max_color_temp_kelvin fan_speed_list unit_of_measurement battery_level fan_speed volume_level is_volume_muted media_title options min max step temperature_unit supported_features device_class next_rising next_setting finishes_at duration remaining humidity wind_speed wind_speed_unit apparent_temperature fan_modes swing_modes fan_mode swing_mode'.split())
|
||||
# Attributes whose boolean value the screen needs; every other bool stays behind.
|
||||
BOOL_ATTRS = frozenset(['is_volume_muted'])
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -205,6 +205,8 @@ FIXED = (
|
||||
('chevron-left', 'F0141'),
|
||||
('chevron-right', 'F0142'),
|
||||
('close', 'F0156'),
|
||||
# Back button of every overlay (firmware 0.2.37+).
|
||||
('arrow-left', 'F004D'),
|
||||
('sun-snowflake-variant', 'F1A79'),
|
||||
('thermostat-auto', 'F1B17'),
|
||||
('weather-fog', 'F0591'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ESP Screen Manager
|
||||
version: "0.2.42"
|
||||
version: "0.2.43"
|
||||
slug: esp_screen_manager
|
||||
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||
|
||||
@@ -222,6 +222,19 @@ class ParityTests(unittest.TestCase):
|
||||
self.assertIn('render_header();', TILES.split('inline void render(lv_obj_t *room) {', 1)[1].split('\n', 5)[4])
|
||||
self.assertLessEqual(HEADER_MIN_FIRMWARE, tuple(int(p) for p in FIRMWARE_VERSION.split('.')))
|
||||
|
||||
def test_second_hand_runs_only_while_the_screen_is_awake(self):
|
||||
for name in PROFILES:
|
||||
text = (ROOT / name).read_text()
|
||||
self.assertIn('runtime_tiles::screen_awake = []() { return !id(display_dimmed); };', text, name)
|
||||
for name in ('packages/cyd.yaml', 'packages/guition.yaml'):
|
||||
self.assertIn('runtime_tiles::screen_awake', (ROOT / name).read_text(), name)
|
||||
self.assertIn('inline bool awake() { return !screen_awake || screen_awake(); }', TILES)
|
||||
second_hand = TILES.split('inline void second_hand(', 1)[1].split('\n}', 1)[0]
|
||||
self.assertIn('set_hidden(p,!(awake() && now.is_valid()));', second_hand)
|
||||
self.assertIn('part_line(w,18,w.points+28,2,w.hand_width)', second_hand)
|
||||
tick = TILES.split('inline void tick() {', 1)[1]
|
||||
self.assertIn('t.builtin() && t.display=="analog")second_hand(w,now);', tick)
|
||||
|
||||
HAS_AIOHTTP = importlib.util.find_spec('aiohttp') is not None
|
||||
if HAS_AIOHTTP:
|
||||
from aiohttp.test_utils import TestClient, TestServer
|
||||
|
||||
Reference in New Issue
Block a user