Smooth graphs, sun path card, skeleton page switches and editor drag & drop

Firmware 0.2.15: trend graphs use a Catmull-Rom curve with a soft fill
drawn by the card's draw event; sun.sun gets a wide sun-path card with
horizon, arc and the sun at its current position; page switches show
the new page's card frames immediately and fill them from a one-shot
timer; the navigation is one full-width bar without a divider; the CYD
adopts the Guition light palette in styles and overlays.

App 0.2.13: pointer-based drag & drop (mouse and touch) to reorder tiles
in the mockup and to drop entities from the list, sticky save bar, a new
header with explained actions and a three-step guide, clearer firmware
dialog. New sun, weather and clock tiles start wide in their best view.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-13 09:09:37 +02:00
parent 517b102786
commit 4af92e1b4c
15 changed files with 807 additions and 321 deletions
+4 -3
View File
@@ -38,9 +38,10 @@ De firmware en ingebouwde CLI zijn getest met **ESPHome 2026.6.2**.
- **Speciale kaarten (firmware 0.2.14+):** een **klok** (digitaal of analoog) - **Speciale kaarten (firmware 0.2.14+):** een **klok** (digitaal of analoog)
als ingebouwde tegel, een **weersvoorspelling** met vijf dagen op een als ingebouwde tegel, een **weersvoorspelling** met vijf dagen op een
dubbelbrede kaart, een **grafiek** van de sensorgeschiedenis in de tegel, dubbelbrede kaart, een **grafiek** van de sensorgeschiedenis in de tegel,
**zonsopgang/-ondergang** (`sun.sun`), een **kookwekker** (`timer.*`, tikken een **zonnebaan** (`sun.sun`: horizon met de zon tussen opkomst en
start of pauzeert) en **aanwezigheid** (`person.*`). Kies ze in de kiezer als ondergang), een **kookwekker** (`timer.*`, tikken start of pauzeert) en
elke andere tegel; **Dubbelbreed** is een optie voor iedere tegel. **aanwezigheid** (`person.*`). Kies ze in de kiezer als elke andere tegel of
sleep ze direct in de schermmockup; **Dubbelbreed** is een optie voor iedere tegel.
- **Scherminstellingen:** standby-tijd, normale en gedimde helderheid, - **Scherminstellingen:** standby-tijd, normale en gedimde helderheid,
nachturen, klok, terug naar de hoofdpagina en optioneel swipen tussen pagina's. nachturen, klok, terug naar de hoofdpagina en optioneel swipen tussen pagina's.
- **Guition-rotatie:** 0°, 90°, 180° of 270°, direct vanuit de beheerpagina. - **Guition-rotatie:** 0°, 90°, 180° of 270°, direct vanuit de beheerpagina.
+142 -17
View File
@@ -28,6 +28,7 @@ inline const lv_font_t *mini_icon_font = nullptr;
inline const lv_font_t *watch_value_font = nullptr, *watch_icon_font = nullptr; inline const lv_font_t *watch_value_font = nullptr, *watch_icon_font = nullptr;
// Big digits for the clock card; the board profile sets it with the local time source. // Big digits for the clock card; the board profile sets it with the local time source.
inline const lv_font_t *clock_font = nullptr; inline const lv_font_t *clock_font = nullptr;
inline lv_obj_t *room_label = nullptr; // remembered by render() so page switches can render synchronously
inline std::function<esphome::ESPTime()> now_time; inline std::function<esphome::ESPTime()> now_time;
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 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 tick();
@@ -75,7 +76,10 @@ struct Widgets {
int title_x=0,title_y=0,value_x=0,value_y=0; const lv_font_t *value_font{}, *icon_font{}; 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`. // 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{}; 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{};
// 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;
}; };
constexpr unsigned POINT_BUFFER = 128;
inline std::array<Widgets, 10> widgets; inline std::array<Widgets, 10> widgets;
inline bool fresh() { return model.ready() && esphome::millis() - last_received < 95000; } inline bool fresh() { return model.ready() && esphome::millis() - last_received < 95000; }
inline float number(JsonVariant value, float fallback = NAN) { inline float number(JsonVariant value, float fallback = NAN) {
@@ -640,13 +644,47 @@ inline uint32_t domain_accent(const Tile &t) {
inline void end_extra(Widgets &w) { inline void end_extra(Widgets &w) {
if(!w.extra)return; if(!w.extra)return;
lv_obj_add_flag(w.extra,LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(w.extra,LV_OBJ_FLAG_HIDDEN);
w.fill_points=nullptr;w.fill_count=0;
if(!w.extra_mode.empty()){lv_obj_clean(w.extra);w.parts.fill(nullptr);w.extra_mode.clear();delete[] w.points;w.points=nullptr;} if(!w.extra_mode.empty()){lv_obj_clean(w.extra);w.parts.fill(nullptr);w.extra_mode.clear();delete[] w.points;w.points=nullptr;}
} }
// Two triangles per segment between the polyline and its baseline. No canvas
// buffer is needed, so the CYD can afford it as well.
inline void extra_draw(lv_event_t *e) {
auto &w=*static_cast<Widgets *>(lv_event_get_user_data(e));
if(!w.fill_points || w.fill_count<2 || !w.fill_opa)return;
auto *layer=lv_event_get_layer(e);lv_area_t area;lv_obj_get_coords(w.extra,&area);
lv_draw_triangle_dsc_t dsc;lv_draw_triangle_dsc_init(&dsc);dsc.color=w.fill_color;dsc.opa=w.fill_opa;
const lv_value_precise_t ox=area.x1+w.fill_x, oy=area.y1+w.fill_y, base=oy+w.fill_base;
for(unsigned i=0;i+1<w.fill_count;++i){
const auto &a=w.fill_points[i],&b=w.fill_points[i+1];
dsc.p[0]={ox+a.x,oy+a.y};dsc.p[1]={ox+b.x,oy+b.y};dsc.p[2]={ox+b.x,base};lv_draw_triangle(layer,&dsc);
dsc.p[1]=dsc.p[2];dsc.p[2]={ox+a.x,base};lv_draw_triangle(layer,&dsc);
}
}
inline void begin_extra(Widgets &w,const char *mode,int width,int height) { inline void begin_extra(Widgets &w,const char *mode,int width,int height) {
if(!w.extra){w.extra=lv_obj_create(w.tile);lv_obj_remove_style_all(w.extra);lv_obj_remove_flag(w.extra,LV_OBJ_FLAG_CLICKABLE);lv_obj_remove_flag(w.extra,LV_OBJ_FLAG_SCROLLABLE);} if(!w.extra){
if(w.extra_mode!=mode){end_extra(w);w.extra_mode=mode;w.points=new lv_point_precise_t[28];w.cached_active=-1;} w.extra=lv_obj_create(w.tile);lv_obj_remove_style_all(w.extra);lv_obj_remove_flag(w.extra,LV_OBJ_FLAG_CLICKABLE);lv_obj_remove_flag(w.extra,LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_event_cb(w.extra,extra_draw,LV_EVENT_DRAW_MAIN,&w);
}
if(w.extra_mode!=mode){end_extra(w);w.extra_mode=mode;w.points=new lv_point_precise_t[POINT_BUFFER];w.cached_active=-1;}
w.fill_points=nullptr;w.fill_count=0;
lv_obj_set_pos(w.extra,0,0);lv_obj_set_size(w.extra,width,height);lv_obj_remove_flag(w.extra,LV_OBJ_FLAG_HIDDEN); lv_obj_set_pos(w.extra,0,0);lv_obj_set_size(w.extra,width,height);lv_obj_remove_flag(w.extra,LV_OBJ_FLAG_HIDDEN);
} }
// Catmull-Rom curve through the samples: the trend reads smoothly without extra data.
inline unsigned smooth(const lv_point_precise_t *in,unsigned n,lv_point_precise_t *out,unsigned capacity,int width,int height) {
if(n<2 || capacity<2){for(unsigned i=0;i<n && i<capacity;++i)out[i]=in[i];return n<capacity?n:capacity;}
const unsigned steps=4;unsigned count=0;
for(unsigned i=0;i+1<n;++i){
const auto &p0=in[i?i-1:0],&p1=in[i],&p2=in[i+1],&p3=in[i+2<n?i+2:n-1];
for(unsigned s=0;s<steps && count<capacity-1;++s){
float t=float(s)/steps,t2=t*t,t3=t2*t;
float x=0.5f*(2*p1.x+(-p0.x+p2.x)*t+(2*p0.x-5*p1.x+4*p2.x-p3.x)*t2+(-p0.x+3*p1.x-3*p2.x+p3.x)*t3);
float y=0.5f*(2*p1.y+(-p0.y+p2.y)*t+(2*p0.y-5*p1.y+4*p2.y-p3.y)*t2+(-p0.y+3*p1.y-3*p2.y+p3.y)*t3);
out[count++]={(lv_value_precise_t)std::clamp(x,0.0f,float(width-1)),(lv_value_precise_t)std::clamp(y,0.0f,float(height-1))};
}
}
out[count++]=in[n-1];return count;
}
inline lv_obj_t *part_label(Widgets &w,unsigned i,const lv_font_t *font,int x,int y,int width,lv_text_align_t align,const std::string &text) { inline lv_obj_t *part_label(Widgets &w,unsigned i,const lv_font_t *font,int x,int y,int width,lv_text_align_t align,const std::string &text) {
auto *&p=w.parts[i]; auto *&p=w.parts[i];
if(!p){p=lv_label_create(w.extra);lv_label_set_long_mode(p,LV_LABEL_LONG_CLIP);lv_obj_remove_flag(p,LV_OBJ_FLAG_CLICKABLE);} if(!p){p=lv_label_create(w.extra);lv_label_set_long_mode(p,LV_LABEL_LONG_CLIP);lv_obj_remove_flag(p,LV_OBJ_FLAG_CLICKABLE);}
@@ -730,21 +768,66 @@ inline void render_forecast(Widgets &w,const Tile &t,bool large,int width,int he
} }
} }
} }
// Sparkline of the manager's 24 history samples in the given rectangle. // Smoothed trend of the manager's 24 history samples with a soft fill beneath.
inline void render_graph(Widgets &w,const Tile &t,bool large,int x,int y,int width,int height) { inline void render_graph(Widgets &w,const Tile &t,bool large,int x,int y,int width,int height) {
begin_extra(w,"graph",x+width,y+height); begin_extra(w,"graph",x+width,y+height);
float minimum=INFINITY,maximum=-INFINITY;for(float v:t.history)if(std::isfinite(v)){minimum=std::min(minimum,v);maximum=std::max(maximum,v);} float minimum=INFINITY,maximum=-INFINITY;for(float v:t.history)if(std::isfinite(v)){minimum=std::min(minimum,v);maximum=std::max(maximum,v);}
unsigned n=0;int stroke=large?3:2; lv_point_precise_t raw[24];unsigned n=0;int stroke=large?3:2,top=stroke;
for(unsigned i=0;i<24;++i){ for(unsigned i=0;i<24;++i){
if(!std::isfinite(t.history[i]))continue; if(!std::isfinite(t.history[i]))continue;
float level=maximum>minimum?(t.history[i]-minimum)/(maximum-minimum):0.5f; float level=maximum>minimum?(t.history[i]-minimum)/(maximum-minimum):0.5f;
w.points[n++]={(lv_value_precise_t)(stroke/2+i*(width-stroke-1)/23),(lv_value_precise_t)(height-stroke/2-1-level*(height-stroke-2))}; raw[n++]={(lv_value_precise_t)(stroke/2+i*(width-stroke-1)/23),(lv_value_precise_t)(height-stroke/2-1-level*(height-stroke-1-top))};
} }
if(n==1){w.points[1]=w.points[0];w.points[1].x=(lv_value_precise_t)(width-1);n=2;} if(n==1){raw[1]=raw[0];raw[1].x=(lv_value_precise_t)(width-1);n=2;}
part_line(w,0,w.points,n,stroke,x,y); unsigned count=smooth(raw,n,w.points,POINT_BUFFER,width,height);
part_line(w,0,w.points,count,stroke,x,y);
w.fill_points=w.points;w.fill_count=count;w.fill_x=x;w.fill_y=y;w.fill_base=height;w.fill_opa=LV_OPA_20;
}
// Sun path: horizon, an arc from sunrise to sunset and the sun at the current
// position (or below the horizon at night). Wide cards only.
inline int minutes_of(const std::string &clock) {
unsigned h=0,m=0;return sscanf(clock.c_str(),"%u:%u",&h,&m)==2 && h<24 && m<60 ? int(h*60+m) : -1;
}
inline void render_sunpath(Widgets &w,const Tile &t,bool large,int width,int height) {
begin_extra(w,"sunpath",width,height);
const lv_font_t *title_font=lv_obj_get_style_text_font(w.title,LV_PART_MAIN);
int title_h=lv_font_get_line_height(title_font),text_h=lv_font_get_line_height(w.value_font);
int horizon=height-text_h-(large?4:2),top=title_h+(large?4:2),x0=large?14:8,x1=width-x0;
part_label(w,0,title_font,0,0,width,LV_TEXT_ALIGN_LEFT,t.name.empty()?"Zon":t.name);
part_label(w,1,w.value_font,0,horizon+(large?3:1),width/2,LV_TEXT_ALIGN_LEFT,"op "+t.sunrise);
part_label(w,2,w.value_font,width/2,horizon+(large?3:1),width/2,LV_TEXT_ALIGN_RIGHT,"onder "+t.sunset);
auto now=now_time?now_time():esphome::ESPTime{};
int rise=minutes_of(t.sunrise),set=minutes_of(t.sunset),minute=now.is_valid()?now.hour*60+now.minute:-1;
bool day=t.state=="above_horizon";float fraction=0.5f;
if(rise>=0 && set>=0 && minute>=0){
if(day){int span=(set-rise+1440)%1440;if(!span)span=1;fraction=std::clamp(float((minute-rise+1440)%1440)/span,0.0f,1.0f);}
else{int span=(rise-set+1440)%1440;if(!span)span=1;fraction=std::clamp(float((minute-set+1440)%1440)/span,0.0f,1.0f);}
}
const unsigned segments=40;float amplitude=day?float(horizon-top):float(height-text_h-horizon-(large?2:1));
auto *arc=w.points,*travelled=w.points+segments+1,*line=w.points+2*segments+3;
for(unsigned i=0;i<=segments;++i){
float a=3.14159265f*i/segments;
arc[i]={(lv_value_precise_t)(x0+(x1-x0)*float(i)/segments),(lv_value_precise_t)(day?horizon-sinf(a)*amplitude:horizon+sinf(a)*amplitude)};
}
unsigned filled=std::min(segments,unsigned(fraction*segments));
for(unsigned i=0;i<=filled;++i)travelled[i]=arc[i];
float sa=3.14159265f*fraction;
lv_point_precise_t sun={(lv_value_precise_t)(x0+(x1-x0)*fraction),(lv_value_precise_t)(day?horizon-sinf(sa)*amplitude:horizon+sinf(sa)*amplitude)};
travelled[filled+1]=sun;
line[0]={(lv_value_precise_t)0,(lv_value_precise_t)horizon};line[1]={(lv_value_precise_t)(width-1),(lv_value_precise_t)horizon};
bool dark_text=light_theme || t.background!=0;
uint32_t path=dark_text?0xCFD8DC:0x6B8299, accent=day?0xFF9800:0x5C6BC0, disc=day?0xFFB300:0xB0BEC5;
lv_obj_set_style_line_color(part_line(w,3,line,2,2),lv_color_hex(path),0);
lv_obj_set_style_line_color(part_line(w,4,arc,segments+1,large?2:1),lv_color_hex(path),0);
lv_obj_set_style_line_color(part_line(w,5,travelled,filled+2,large?4:3),lv_color_hex(accent),0);
int size=large?18:10,glow=size+(large?12:6);
auto *halo=part_dot(w,6,int(sun.x)-glow/2,int(sun.y)-glow/2,glow);lv_obj_set_style_bg_color(halo,lv_color_hex(disc),0);lv_obj_set_style_bg_opa(halo,LV_OPA_30,0);
lv_obj_set_style_bg_color(part_dot(w,7,int(sun.x)-size/2,int(sun.y)-size/2,size),lv_color_hex(disc),0);
if(day){w.fill_points=travelled;w.fill_count=filled+2;w.fill_x=0;w.fill_y=0;w.fill_base=horizon;w.fill_color=lv_color_hex(0xFFB300);w.fill_opa=LV_OPA_20;}
} }
inline void render(lv_obj_t *room) { inline void render(lv_obj_t *room) {
if (!enabled) return; if (!enabled) return;
room_label=room;
label(room, !model.configured ? "Kies tegels in HA" : !model.ready() ? "Tegels laden..." : !fresh() ? "HA niet verbonden" : model.title); label(room, !model.configured ? "Kies tegels in HA" : !model.ready() ? "Tegels laden..." : !fresh() ? "HA niet verbonden" : model.title);
for (size_t slot = 0; slot < 6; ++slot) { for (size_t slot = 0; slot < 6; ++slot) {
auto &w=widgets[slot]; auto &w=widgets[slot];
@@ -777,8 +860,9 @@ inline void render(lv_obj_t *room) {
bool large_tile=lv_obj_get_height(w.tile)>80; bool large_tile=lv_obj_get_height(w.tile)>80;
// Cards that replace the name/status layout entirely. // Cards that replace the name/status layout entirely.
bool clock=t.builtin(), forecast=d=="weather" && t.display=="forecast" && w.wide && t.forecast_count>0 && fresh() && t.available(); bool clock=t.builtin(), forecast=d=="weather" && t.display=="forecast" && w.wide && t.forecast_count>0 && fresh() && t.available();
bool sunpath=d=="sun" && t.display=="sunpath" && w.wide && !t.sunrise.empty() && !t.sunset.empty() && fresh() && t.available();
bool graph=d=="sensor" && t.display=="graph" && t.has_history && !clock; bool graph=d=="sensor" && t.display=="graph" && t.has_history && !clock;
bool custom=clock||forecast; bool custom=clock||forecast||sunpath;
if(!large_tile){lv_obj_set_style_pad_top(w.tile,watch||custom||graph?2:4,0);lv_obj_set_style_pad_bottom(w.tile,watch||custom||graph?2:4,0);} if(!large_tile){lv_obj_set_style_pad_top(w.tile,watch||custom||graph?2:4,0);lv_obj_set_style_pad_bottom(w.tile,watch||custom||graph?2:4,0);}
lv_obj_set_style_text_font(w.value,watch && watch_value_font ? watch_value_font : w.value_font,0); lv_obj_set_style_text_font(w.value,watch && watch_value_font ? watch_value_font : w.value_font,0);
lv_obj_set_height(w.value,lv_font_get_line_height(lv_obj_get_style_text_font(w.value,LV_PART_MAIN))); lv_obj_set_height(w.value,lv_font_get_line_height(lv_obj_get_style_text_font(w.value,LV_PART_MAIN)));
@@ -787,7 +871,9 @@ inline void render(lv_obj_t *room) {
for(auto *o:{w.title,w.value,w.circle,w.unit}){if(custom)lv_obj_add_flag(o,LV_OBJ_FLAG_HIDDEN);else lv_obj_remove_flag(o,LV_OBJ_FLAG_HIDDEN);} for(auto *o:{w.title,w.value,w.circle,w.unit}){if(custom)lv_obj_add_flag(o,LV_OBJ_FLAG_HIDDEN);else lv_obj_remove_flag(o,LV_OBJ_FLAG_HIDDEN);}
if(custom){ if(custom){
lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN);
if(clock)render_clock(w,t,large_tile,content_width,content_height);else render_forecast(w,t,large_tile,content_width,content_height); if(clock)render_clock(w,t,large_tile,content_width,content_height);
else if(sunpath)render_sunpath(w,t,large_tile,content_width,content_height);
else render_forecast(w,t,large_tile,content_width,content_height);
}else{ }else{
int title_height=lv_obj_get_height(w.title),value_height=lv_obj_get_height(w.value); int title_height=lv_obj_get_height(w.title),value_height=lv_obj_get_height(w.value);
int line_gap=large_tile?2:1,text_height=title_height+line_gap+value_height; int line_gap=large_tile?2:1,text_height=title_height+line_gap+value_height;
@@ -862,9 +948,13 @@ inline void render(lv_obj_t *room) {
lv_obj_set_style_text_color(w.title, title_color, 0); lv_obj_set_style_text_color(w.title, title_color, 0);
lv_obj_set_style_text_color(w.value, value_color, 0); lv_obj_set_style_text_color(w.value, value_color, 0);
// Custom parts follow the card palette: text like the title, lines/dots in the accent. // Custom parts follow the card palette: text like the title, lines/dots in the accent.
// The sun path sets its own colours on every render.
w.fill_color=color;
for(unsigned i=0;i<w.parts.size();++i){ for(unsigned i=0;i<w.parts.size();++i){
auto *p=w.parts[i];if(!p)continue; auto *p=w.parts[i];if(!p)continue;
if(lv_obj_check_type(p,&lv_label_class))lv_obj_set_style_text_color(p,w.extra_mode=="forecast" && i>=2 && i%3==2 ? value_color : i==16 ? value_color : title_color,0); bool muted=w.extra_mode=="forecast" ? i>=2 && i%3==2 : w.extra_mode=="sunpath" ? i>=1 : i==16;
if(lv_obj_check_type(p,&lv_label_class))lv_obj_set_style_text_color(p,muted?value_color:title_color,0);
else if(w.extra_mode=="sunpath")continue;
else if(lv_obj_check_type(p,&lv_line_class))lv_obj_set_style_line_color(p,w.extra_mode=="graph"?color:i==13?icon_color:title_color,0); else if(lv_obj_check_type(p,&lv_line_class))lv_obj_set_style_line_color(p,w.extra_mode=="graph"?color:i==13?icon_color:title_color,0);
else lv_obj_set_style_bg_color(p,i==14?icon_color:value_color,0); else lv_obj_set_style_bg_color(p,i==14?icon_color:value_color,0);
} }
@@ -946,7 +1036,15 @@ inline unsigned page_count() {
std::array<Placement,MAX_TILES> placement; std::array<Placement,MAX_TILES> placement;
return pack(model.tiles,model.count,placement); return pack(model.tiles,model.count,placement);
} }
inline void show_page(int &page, lv_obj_t *previous, lv_obj_t *next, lv_obj_t *number) { // Page switches feel immediate: the new page's card frames (right widths, no
// contents, light skeleton style) appear in the very next frame, and a one-shot
// LVGL timer fills them in right after. Keepalives and re-packing on the same
// page apply directly. Nothing is allocated, so the CYD stays comfortable.
inline lv_obj_t *nav_prev=nullptr,*nav_next=nullptr,*nav_number=nullptr;
inline int applied_page=-1,target_page=0;
inline lv_timer_t *page_timer=nullptr;
// Slot assignment plus card widths and visibility for a page; contents are untouched.
inline int place_page(int page) {
std::array<Placement,MAX_TILES> placement; std::array<Placement,MAX_TILES> placement;
int pages=pack(model.tiles,model.count,placement); int pages=pack(model.tiles,model.count,placement);
page=std::clamp(page,0,pages-1); page=std::clamp(page,0,pages-1);
@@ -958,14 +1056,41 @@ inline void show_page(int &page, lv_obj_t *previous, lv_obj_t *next, lv_obj_t *n
if(slot<SLOTS_PER_PAGE && w.index<model.count){lv_obj_set_width(w.tile,w.wide?wide_width:w.base_width);lv_obj_remove_flag(w.tile,LV_OBJ_FLAG_HIDDEN);} if(slot<SLOTS_PER_PAGE && w.index<model.count){lv_obj_set_width(w.tile,w.wide?wide_width:w.base_width);lv_obj_remove_flag(w.tile,LV_OBJ_FLAG_HIDDEN);}
else{lv_obj_add_flag(w.tile,LV_OBJ_FLAG_HIDDEN);end_extra(w);} else{lv_obj_add_flag(w.tile,LV_OBJ_FLAG_HIDDEN);end_extra(w);}
} }
for(auto *control:{previous,next,number}){ for(auto *control:{nav_prev,nav_next,nav_number}){
if(pages>1)lv_obj_remove_flag(control,LV_OBJ_FLAG_HIDDEN); if(pages>1)lv_obj_remove_flag(control,LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(control,LV_OBJ_FLAG_HIDDEN); else lv_obj_add_flag(control,LV_OBJ_FLAG_HIDDEN);
} }
if(page==0)lv_obj_add_state(previous,LV_STATE_DISABLED);else lv_obj_remove_state(previous,LV_STATE_DISABLED); if(page==0)lv_obj_add_state(nav_prev,LV_STATE_DISABLED);else lv_obj_remove_state(nav_prev,LV_STATE_DISABLED);
if(page==pages-1)lv_obj_add_state(next,LV_STATE_DISABLED);else lv_obj_remove_state(next,LV_STATE_DISABLED); if(page==pages-1)lv_obj_add_state(nav_next,LV_STATE_DISABLED);else lv_obj_remove_state(nav_next,LV_STATE_DISABLED);
std::string caption=std::to_string(page+1)+" / "+std::to_string(pages);lv_label_set_text(number,caption.c_str()); for(auto *control:{nav_prev,nav_next})if(lv_obj_get_child_count(control))
if(refresh)refresh(); lv_obj_set_style_text_opa(lv_obj_get_child(control,0),lv_obj_has_state(control,LV_STATE_DISABLED)?LV_OPA_30:LV_OPA_COVER,0);
std::string caption=std::to_string(page+1)+" / "+std::to_string(pages);lv_label_set_text(nav_number,caption.c_str());
return page;
}
inline void apply_page(int page) {
applied_page=place_page(page);
if(room_label)render(room_label);else if(refresh)refresh();
}
inline void skeleton_page(int page) {
place_page(page);
uint32_t bg=light_theme?0xF4F5F7:0x4A6076, border=light_theme?0xE3E5E8:0x6F879C;
for(size_t slot=0;slot<SLOTS_PER_PAGE;++slot){
auto &w=widgets[slot];if(!w.tile || lv_obj_has_flag(w.tile,LV_OBJ_FLAG_HIDDEN))continue;
for(auto *o:{w.title,w.value,w.circle,w.unit,w.slider,w.extra})if(o)lv_obj_add_flag(o,LV_OBJ_FLAG_HIDDEN);
lv_obj_set_width(w.progress,0);
lv_obj_set_style_bg_color(w.tile,lv_color_hex(bg),0);
lv_obj_set_style_border_color(w.tile,lv_color_hex(border),0);
}
}
inline void page_timer_done(lv_timer_t *) { page_timer=nullptr; apply_page(target_page); }
inline void show_page(int &page, lv_obj_t *previous, lv_obj_t *next, lv_obj_t *number) {
nav_prev=previous;nav_next=next;nav_number=number;
page=std::clamp(page,0,int(page_count())-1);
target_page=page;
if(applied_page<0 || page==applied_page){if(page_timer){lv_timer_delete(page_timer);page_timer=nullptr;}apply_page(page);return;}
skeleton_page(page);
if(page_timer)lv_timer_reset(page_timer);
else{page_timer=lv_timer_create(page_timer_done,60,nullptr);lv_timer_set_repeat_count(page_timer,1);}
} }
inline uint32_t last_live_second=0; inline uint32_t last_live_second=0;
@@ -991,7 +1116,7 @@ inline void tick() {
for(size_t slot=0;slot<SLOTS_PER_PAGE;++slot){ for(size_t slot=0;slot<SLOTS_PER_PAGE;++slot){
auto &w=widgets[slot];if(!w.tile || w.index>=model.count || lv_obj_has_flag(w.tile,LV_OBJ_FLAG_HIDDEN))continue; 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]; const auto &t=model.tiles[w.index];
if(t.builtin() || (t.domain()=="timer" && t.state=="active"))redraw=true; if(t.builtin() || (t.domain()=="timer" && t.state=="active") || (t.domain()=="sun" && second%60==0))redraw=true;
} }
} }
if(redraw && refresh)refresh(); if(redraw && refresh)refresh();
+1 -1
View File
@@ -23,7 +23,7 @@ def demo_layout():
{'entity': 'sensor.demo_temperatuur', 'name': 'Woonkamer', 'options': {'display': 'graph'}}, {'entity': 'sensor.demo_temperatuur', 'name': 'Woonkamer', 'options': {'display': 'graph'}},
{'entity': 'person.demo', 'name': 'Max', 'options': {}}, {'entity': 'person.demo', 'name': 'Max', 'options': {}},
{'entity': 'timer.demo', 'name': 'Eieren', 'options': {}}, {'entity': 'timer.demo', 'name': 'Eieren', 'options': {}},
{'entity': 'sun.sun', 'name': 'Zon', 'options': {}}, {'entity': 'sun.sun', 'name': 'Zon', 'options': {'display': 'sunpath'}},
{'entity': 'sensor.demo_energie', 'name': 'Verbruik vandaag', 'options': {'display': 'graph', 'size': 'wide'}}, {'entity': 'sensor.demo_energie', 'name': 'Verbruik vandaag', 'options': {'display': 'graph', 'size': 'wide'}},
{'entity': 'light.demo', 'name': 'Tafellamp', 'options': {'display': 'watch', 'size': 'wide'}}, {'entity': 'light.demo', 'name': 'Tafellamp', 'options': {'display': 'watch', 'size': 'wide'}},
{'entity': 'weather.demo_standaard', 'name': 'Weer nu', 'options': {}}, {'entity': 'weather.demo_standaard', 'name': 'Weer nu', 'options': {}},
+55
View File
@@ -0,0 +1,55 @@
# App 0.2.13 / firmware 0.2.15 — zonnebaan, gladde grafiek, editor-UX
## Wijzigingen
- **Grafiek**: Catmull-Rom-curve door dezelfde 24 historiepunten (vier
tussenpunten per segment) met een zachte vulling eronder. De vulling wordt
door een `LV_EVENT_DRAW_MAIN`-tekenevent van de kaartcontainer als
driehoeken getekend; er is geen canvasbuffer nodig, dus ook de CYD kan het.
- **Zonnebaan** (`sun.sun`, `display: sunpath`, altijd dubbelbreed): horizon,
boog van opkomst naar ondergang, de zon op de huidige positie met een gloed
en een vulling van het afgelegde deel; ’s nachts loopt de zon onder de
horizon in indigo. De positie wordt uit de HH:MM-tijden van de manager en
de lokale tijd berekend en ververst elke minuut.
- **Navigatie**: één onderbalk over de volle breedte met een doorlopende
bovenlijn; linkerhelft = vorige, rechterhelft = volgende, paginanummer in
het midden. Een uitgeschakelde helft dimt zijn tekst.
- **Paginawissel**: `show_page` zet in hetzelfde frame de kaartkaders van de
nieuwe pagina neer (juiste breedtes via `pack`, inhoud verborgen, lichte
skeletonstijl) en een eenmalige LVGL-timer van 60 ms vult daarna de inhoud
synchroon in. Een eerdere fade-variant voelde traag en is vervangen.
Keepalives en herpakken op dezelfde pagina passen direct toe.
- **CYD-kleuren**: het Guition-schema is overgenomen — vlakke lichtgrijze
achtergrond (0xE7E7E7), witte kaarten met 0xDDDDDD-rand, donkere tekst,
lichtblauwe iconcirkels — inclusief alle bedieningsoverlays (75 kleurwaarden
één-op-één gelijk aan de Guition) en `light_theme` voor de runtimekaarten.
- **Navigatie** zonder scheidingslijn: alleen de teksten "< Vorige",
"Volgende >" en het paginanummer.
- **Editor**: pointer-gebaseerd slepen (muis en touch met kort vasthouden)
om tegels in de mockup te ordenen en entiteiten uit de lijst op een plek
te laten vallen, met auto-scroll bij de schermranden; sticky opslaan-balk;
nieuwe kop met drie uitgelegde acties en een stappenuitleg; verbeterde
firmwaredialoog. Zon-, weer- en kloktegels starten dubbelbreed in hun
mooiste weergave.
## Tests
- 55 Python-tests en negen C++-testprogramma's geslaagd; `--check` van de
pakketgenerator; beide Easy Setup-profielen gebouwd (CYD-flash 72,9 %).
- Editor in de browser met een gemockte inventory gecontroleerd: kop, stappen,
sticky opslaan-balk, dubbelbrede mockup. Slepen getest met gesimuleerde
pointer-events: tegel 4 → positie 1 en een entiteit uit de lijst → positie 2
komen op de juiste plek; een sleep telt niet als klik.
- Beide borden via USB geflasht; `device_info` meldt 0.2.15. Guition-snapshots
van de demo-indeling: zonnebaan met zon op de boog en tijden, gladde
gevulde grafiek enkel- en dubbelbreed, nieuwe onderbalk. Demo-renderdiagnose
op de Guition 10/10 geslaagd (`count=9`).
- De eigenaar bevestigde het uiterlijk op de Guition, meldde tussenframes bij
paginawissels (foto met een achtergebleven sparkline) en vond de fade te
traag; daarop de skeletonwissel. Renderdiagnose op de echte indelingen van
beide borden na de laatste flash: zie hieronder.
- Laatste flash (Guition 08:59:18, CYD 09:03:32, beide 0.2.15): renderdiagnose
op de echte indelingen geslaagd — Guition 10/10 (`count=6`, heap ~6,46 MB),
CYD 10/10 (`count=7`, ~52,5 kB vrij). Beide handmatige profielen compileren.
De skeletonwissel en de CYD-kleuren zijn door de eigenaar visueel te
beoordelen; er is geen langdurige stabiliteitsproef uitgevoerd.
+30 -18
View File
@@ -1,7 +1,7 @@
# Guition ESP32-S3-4848S040 / 4-inch EU wallbox, 480x480 IPS + GT911. # 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. # Use guition-device.yaml; see docs/GUITION.md. CYD hardware is a separate profile.
substitutions: substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.14" SCREEN_FIRMWARE_VERSION: "0.2.15"
DYNAMIC_TILES: "false" DYNAMIC_TILES: "false"
DASHBOARD_INBOX_INTERNAL: "true" DASHBOARD_INBOX_INTERNAL: "true"
DEVICE_NAME: "guition-wallbox" DEVICE_NAME: "guition-wallbox"
@@ -3227,19 +3227,22 @@ lvgl:
was_on: false was_on: false
# ---------- VALUE SLIDER OVERLAY ---------- # ---------- VALUE SLIDER OVERLAY ----------
# ---------- PAGE NAVIGATION: one full-width bar ----------
- obj: - obj:
id: page_prev id: page_prev
align: BOTTOM_LEFT align: BOTTOM_LEFT
x: 14 x: 0
y: -4 y: 0
width: 150 width: 200
height: 44 height: 44
pad_all: 0 pad_all: 0
radius: 12 radius: 0
bg_color: 0xEEEEEE bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3249,31 +3252,39 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: LEFT_MID
x: 24
text: "< Vorige" text: "< Vorige"
text_font: sublabel text_font: label
text_color: 0x1B1B1B text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
id: page_number id: page_number
align: BOTTOM_MID align: BOTTOM_MID
y: -18 y: 0
width: 80
height: 44
pad_top: 12
border_width: 0
text: "1 / 2" text: "1 / 2"
text_align: CENTER
text_font: sublabel text_font: sublabel
text_color: 0x1B1B1B text_color: 0x1B1B1B
- obj: - obj:
id: page_next id: page_next
align: BOTTOM_RIGHT align: BOTTOM_RIGHT
x: -14 x: 0
y: -4 y: 0
width: 150 width: 200
height: 44 height: 44
pad_all: 0 pad_all: 0
radius: 12 radius: 0
bg_color: 0xEEEEEE bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3283,9 +3294,10 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: RIGHT_MID
x: -24
text: "Volgende >" text: "Volgende >"
text_font: sublabel text_font: label
text_color: 0x1B1B1B text_color: 0x1B1B1B
clickable: false clickable: false
+118 -101
View File
@@ -58,7 +58,7 @@
############################################################ ############################################################
substitutions: substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.14" SCREEN_FIRMWARE_VERSION: "0.2.15"
DYNAMIC_TILES: "false" DYNAMIC_TILES: "false"
DASHBOARD_INBOX_INTERNAL: "true" DASHBOARD_INBOX_INTERNAL: "true"
DEVICE_NAME: "cyd-new" DEVICE_NAME: "cyd-new"
@@ -643,8 +643,8 @@ substitutions:
OVERLAY_SLIDER_W: "96" OVERLAY_SLIDER_W: "96"
OVERLAY_SLIDER_H: "160" OVERLAY_SLIDER_H: "160"
OVERLAY_ICON_Y: "74" OVERLAY_ICON_Y: "74"
BG_TOP_COLOR: "0xAEC4D9" # achtergrond boven (zacht blauw) BG_TOP_COLOR: "0xE7E7E7" # lichte achtergrond, gelijk aan de Guition
BG_BOTTOM_COLOR: "0xD6BB9E" # achtergrond onder (warm zand) BG_BOTTOM_COLOR: "0xE7E7E7"
TILE_ICON_X: "0" TILE_ICON_X: "0"
TILE_ICON_Y: "0" TILE_ICON_Y: "0"
TILE_TITLE_X: "48" TILE_TITLE_X: "48"
@@ -809,6 +809,7 @@ esphome:
settings.night_start = atoi("${NIGHT_START_HOUR}") * 60; settings.night_start = atoi("${NIGHT_START_HOUR}") * 60;
settings.night_end = atoi("${NIGHT_END_HOUR}") * 60; settings.night_end = atoi("${NIGHT_END_HOUR}") * 60;
settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}"); settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}");
runtime_tiles::light_theme = true;
runtime_tiles::watch_font = id(headline)->get_lv_font(); runtime_tiles::watch_font = id(headline)->get_lv_font();
runtime_tiles::watch_value_font = id(watch_value)->get_lv_font(); runtime_tiles::watch_value_font = id(watch_value)->get_lv_font();
runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font(); runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font();
@@ -898,7 +899,7 @@ esphome:
- lambda: |- - lambda: |-
static const char *entities[] = {"${TILE1_ENTITY}", "${TILE2_ENTITY}", "${TILE3_ENTITY}", "${TILE4_ENTITY}", "${TILE5_ENTITY}", "${TILE6_ENTITY}", "${TILE7_ENTITY}", "${TILE9_ENTITY}"}; static const char *entities[] = {"${TILE1_ENTITY}", "${TILE2_ENTITY}", "${TILE3_ENTITY}", "${TILE4_ENTITY}", "${TILE5_ENTITY}", "${TILE6_ENTITY}", "${TILE7_ENTITY}", "${TILE9_ENTITY}"};
if (!runtime_tiles::enabled) light_controls::subscribe(entities, 8); if (!runtime_tiles::enabled) light_controls::subscribe(entities, 8);
light_controls::setup(id(color_detail_overlay), id(sublabel)->get_lv_font(), atoi("${DISPLAY_W}"), atoi("${DISPLAY_H}")); light_controls::setup(id(color_detail_overlay), id(sublabel)->get_lv_font(), atoi("${DISPLAY_W}"), atoi("${DISPLAY_H}"), true);
light_controls::commits[0] = [](int value) { id(set_light_hue).execute(value); }; light_controls::commits[0] = [](int value) { id(set_light_hue).execute(value); };
light_controls::commits[1] = [](int value) { id(set_light_color_temperature).execute(value); }; light_controls::commits[1] = [](int value) { id(set_light_color_temperature).execute(value); };
light_controls::commits[2] = [](int value) { id(color_brightness_commit).execute(value); }; light_controls::commits[2] = [](int value) { id(color_brightness_commit).execute(value); };
@@ -2059,7 +2060,9 @@ lvgl:
bg_color: 0xFFFFFF bg_color: 0xFFFFFF
bg_opa: COVER bg_opa: COVER
radius: 18 radius: 18
border_width: 0 border_width: 1
border_color: 0xDDDDDD
border_opa: COVER
pad_left: 8 pad_left: 8
pad_right: 8 pad_right: 8
pad_top: 8 pad_top: 8
@@ -2068,10 +2071,12 @@ lvgl:
shadow_opa: 40% shadow_opa: 40%
- id: style_tile_disabled - id: style_tile_disabled
bg_color: 0x939391 bg_color: 0xFFFFFF
bg_opa: COVER bg_opa: COVER
radius: 18 radius: 18
border_width: 0 border_width: 1
border_color: 0xDDDDDD
border_opa: COVER
pad_left: 8 pad_left: 8
pad_right: 8 pad_right: 8
pad_top: 8 pad_top: 8
@@ -2080,25 +2085,25 @@ lvgl:
shadow_opa: 25% shadow_opa: 25%
- id: style_icon_circle - id: style_icon_circle
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
radius: 999 radius: 999
border_width: 0 border_width: 0
- id: style_icon_circle_disabled - id: style_icon_circle_disabled
bg_color: 0x7B7B6F bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
radius: 999 radius: 999
border_width: 0 border_width: 0
- id: style_room - id: style_room
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
- id: style_time - id: style_time
text_font: time_label text_font: time_label
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
- id: style_title - id: style_title
@@ -2113,17 +2118,17 @@ lvgl:
- id: style_value_big - id: style_value_big
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 80% text_opa: 80%
- id: style_title_disabled - id: style_title_disabled
text_font: label text_font: label
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
- id: style_value_disabled - id: style_value_disabled
text_font: sublabel text_font: sublabel
text_color: 0xD9D9D9 text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
pages: pages:
@@ -3364,19 +3369,22 @@ lvgl:
was_on: false was_on: false
# ---------- VALUE SLIDER OVERLAY ---------- # ---------- VALUE SLIDER OVERLAY ----------
# ---------- PAGE NAVIGATION: one full-width bar ----------
- obj: - obj:
id: page_prev id: page_prev
align: BOTTOM_LEFT align: BOTTOM_LEFT
x: 9 x: 0
y: -2 y: 0
width: 100 width: 130
height: 32 height: 32
pad_all: 0 pad_all: 0
radius: 8 radius: 0
bg_color: 0x3C3C40 bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3386,31 +3394,39 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: LEFT_MID
x: 12
text: "< Vorige" text: "< Vorige"
text_font: sublabel text_font: label
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
id: page_number id: page_number
align: BOTTOM_MID align: BOTTOM_MID
y: -9 y: 0
width: 60
height: 32
pad_top: 9
border_width: 0
text: "1 / 2" text: "1 / 2"
text_align: CENTER
text_font: sublabel text_font: sublabel
text_color: 0xFFFFFF text_color: 0x1B1B1B
- obj: - obj:
id: page_next id: page_next
align: BOTTOM_RIGHT align: BOTTOM_RIGHT
x: -9 x: 0
y: -2 y: 0
width: 100 width: 130
height: 32 height: 32
pad_all: 0 pad_all: 0
radius: 8 radius: 0
bg_color: 0x3C3C40 bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3420,10 +3436,11 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: RIGHT_MID
x: -12
text: "Volgende >" text: "Volgende >"
text_font: sublabel text_font: label
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -3460,7 +3477,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 18% bg_opa: 18%
border_width: 0 border_width: 0
clickable: false clickable: false
@@ -3491,7 +3508,7 @@ lvgl:
value: 100 value: 100
adv_hittest: false adv_hittest: false
bg_color: 0x939391 bg_color: 0xFFFFFF
bg_opa: 100% bg_opa: 100%
radius: 28 radius: 28
# Enige widget met ZOWEL radius ALS een kind (overlay_icon). # Enige widget met ZOWEL radius ALS een kind (overlay_icon).
@@ -3504,7 +3521,7 @@ lvgl:
border_width: 0 border_width: 0
indicator: indicator:
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: 100% bg_opa: 100%
radius: 28 radius: 28
@@ -3536,7 +3553,7 @@ lvgl:
align: CENTER align: CENTER
y: ${OVERLAY_ICON_Y} y: ${OVERLAY_ICON_Y}
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
text: "${TILE1_ICON}" text: "${TILE1_ICON}"
clickable: false clickable: false
@@ -3745,7 +3762,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x171719 bg_color: 0xF7F7F7
bg_opa: COVER bg_opa: COVER
hidden: true hidden: true
scrollable: false scrollable: false
@@ -3771,7 +3788,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 72% bg_opa: 72%
border_width: 0 border_width: 0
clickable: false clickable: false
@@ -3785,7 +3802,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -3799,7 +3816,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
@@ -3832,7 +3849,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- CLIMATE DETAIL OVERLAY ---------- # ---------- CLIMATE DETAIL OVERLAY ----------
@@ -3842,7 +3859,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x171719 bg_color: 0xF7F7F7
bg_opa: COVER bg_opa: COVER
hidden: true hidden: true
scrollable: false scrollable: false
@@ -3868,7 +3885,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 62% bg_opa: 62%
border_width: 0 border_width: 0
clickable: false clickable: false
@@ -3884,7 +3901,7 @@ lvgl:
width: 44 width: 44
height: 44 height: 44
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -3898,7 +3915,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -3909,7 +3926,7 @@ lvgl:
width: 44 width: 44
height: 44 height: 44
radius: 999 radius: 999
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -3921,7 +3938,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
@@ -3937,7 +3954,7 @@ lvgl:
y: 42 y: 42
text: "Current: --" text: "Current: --"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xD1E8FF text_color: 0x1B1B1B
text_opa: 85% text_opa: 85%
clickable: false clickable: false
@@ -3988,7 +4005,7 @@ lvgl:
y: -16 y: -16
text: "SET TO" text: "SET TO"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 68% text_opa: 68%
clickable: false clickable: false
@@ -3998,7 +4015,7 @@ lvgl:
y: 20 y: 20
text: "20.0°" text: "20.0°"
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4009,7 +4026,7 @@ lvgl:
width: 48 width: 48
height: 36 height: 36
radius: 18 radius: 18
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -4025,7 +4042,7 @@ lvgl:
align: CENTER align: CENTER
text: "-" text: "-"
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4036,7 +4053,7 @@ lvgl:
width: 48 width: 48
height: 36 height: 36
radius: 18 radius: 18
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -4052,7 +4069,7 @@ lvgl:
align: CENTER align: CENTER
text: "+" text: "+"
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4062,7 +4079,7 @@ lvgl:
width: 126 width: 126
height: 34 height: 34
radius: 17 radius: 17
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 82% bg_opa: 82%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4075,7 +4092,7 @@ lvgl:
align: CENTER align: CENTER
text: "Auto" text: "Auto"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- CLIMATE MODE PICKER ---------- # ---------- CLIMATE MODE PICKER ----------
# Vervangt het doorcyclen van modi. Cyclen stuurt bij elke tik een # Vervangt het doorcyclen van modi. Cyclen stuurt bij elke tik een
@@ -4089,7 +4106,7 @@ lvgl:
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
hidden: true hidden: true
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 90% bg_opa: 90%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4101,7 +4118,7 @@ lvgl:
y: 16 y: 16
text: "Mode" text: "Mode"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# Flex zorgt dat niet-ondersteunde modi geen gat achterlaten: # Flex zorgt dat niet-ondersteunde modi geen gat achterlaten:
@@ -4128,7 +4145,7 @@ lvgl:
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4141,14 +4158,14 @@ lvgl:
align: CENTER align: CENTER
text: "Off" text: "Off"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_heat id: cmode_heat
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4161,14 +4178,14 @@ lvgl:
align: CENTER align: CENTER
text: "Heat" text: "Heat"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_cool id: cmode_cool
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4181,14 +4198,14 @@ lvgl:
align: CENTER align: CENTER
text: "Cool" text: "Cool"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_heat_cool id: cmode_heat_cool
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4201,14 +4218,14 @@ lvgl:
align: CENTER align: CENTER
text: "Heat/Cool" text: "Heat/Cool"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_auto id: cmode_auto
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4221,14 +4238,14 @@ lvgl:
align: CENTER align: CENTER
text: "Auto" text: "Auto"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_dry id: cmode_dry
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4241,14 +4258,14 @@ lvgl:
align: CENTER align: CENTER
text: "Dry" text: "Dry"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_fan_only id: cmode_fan_only
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4261,7 +4278,7 @@ lvgl:
align: CENTER align: CENTER
text: "Fan" text: "Fan"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4272,7 +4289,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -4286,7 +4303,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4297,7 +4314,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4309,7 +4326,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- VACUUM CONTROL ---------- # ---------- VACUUM CONTROL ----------
@@ -4322,7 +4339,7 @@ lvgl:
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
hidden: true hidden: true
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 90% bg_opa: 90%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4335,7 +4352,7 @@ lvgl:
y: 16 y: 16
text: "Vacuum" text: "Vacuum"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4360,7 +4377,7 @@ lvgl:
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4373,14 +4390,14 @@ lvgl:
align: CENTER align: CENTER
text: "Start" text: "Start"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vac_pause id: vac_pause
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4393,14 +4410,14 @@ lvgl:
align: CENTER align: CENTER
text: "Pause" text: "Pause"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vac_return_to_base id: vac_return_to_base
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4413,14 +4430,14 @@ lvgl:
align: CENTER align: CENTER
text: "Dock" text: "Dock"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vac_stop id: vac_stop
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4433,7 +4450,7 @@ lvgl:
align: CENTER align: CENTER
text: "Stop" text: "Stop"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
@@ -4441,7 +4458,7 @@ lvgl:
y: 138 y: 138
text: "Suction" text: "Suction"
text_font: sublabel text_font: sublabel
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4466,7 +4483,7 @@ lvgl:
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4481,14 +4498,14 @@ lvgl:
align: CENTER align: CENTER
text: "Quiet" text: "Quiet"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vacspd_balanced id: vacspd_balanced
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4503,14 +4520,14 @@ lvgl:
align: CENTER align: CENTER
text: "Balanced" text: "Balanced"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vacspd_turbo id: vacspd_turbo
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4525,14 +4542,14 @@ lvgl:
align: CENTER align: CENTER
text: "Turbo" text: "Turbo"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vacspd_max id: vacspd_max
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4547,7 +4564,7 @@ lvgl:
align: CENTER align: CENTER
text: "Max" text: "Max"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4558,7 +4575,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -4572,7 +4589,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4583,7 +4600,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4595,7 +4612,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- DIM / WAKE OVERLAY ---------- # ---------- DIM / WAKE OVERLAY ----------
+118 -101
View File
@@ -59,7 +59,7 @@
############################################################ ############################################################
substitutions: substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.14" SCREEN_FIRMWARE_VERSION: "0.2.15"
DYNAMIC_TILES: "true" DYNAMIC_TILES: "true"
DASHBOARD_INBOX_INTERNAL: "false" DASHBOARD_INBOX_INTERNAL: "false"
DEVICE_NAME: "cyd-new" DEVICE_NAME: "cyd-new"
@@ -644,8 +644,8 @@ substitutions:
OVERLAY_SLIDER_W: "96" OVERLAY_SLIDER_W: "96"
OVERLAY_SLIDER_H: "160" OVERLAY_SLIDER_H: "160"
OVERLAY_ICON_Y: "74" OVERLAY_ICON_Y: "74"
BG_TOP_COLOR: "0xAEC4D9" # achtergrond boven (zacht blauw) BG_TOP_COLOR: "0xE7E7E7" # lichte achtergrond, gelijk aan de Guition
BG_BOTTOM_COLOR: "0xD6BB9E" # achtergrond onder (warm zand) BG_BOTTOM_COLOR: "0xE7E7E7"
TILE_ICON_X: "0" TILE_ICON_X: "0"
TILE_ICON_Y: "0" TILE_ICON_Y: "0"
TILE_TITLE_X: "48" TILE_TITLE_X: "48"
@@ -813,6 +813,7 @@ esphome:
settings.night_start = atoi("${NIGHT_START_HOUR}") * 60; settings.night_start = atoi("${NIGHT_START_HOUR}") * 60;
settings.night_end = atoi("${NIGHT_END_HOUR}") * 60; settings.night_end = atoi("${NIGHT_END_HOUR}") * 60;
settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}"); settings.night_brightness = atoi("${NIGHT_DIM_BRIGHTNESS}");
runtime_tiles::light_theme = true;
runtime_tiles::watch_font = id(headline)->get_lv_font(); runtime_tiles::watch_font = id(headline)->get_lv_font();
runtime_tiles::watch_value_font = id(watch_value)->get_lv_font(); runtime_tiles::watch_value_font = id(watch_value)->get_lv_font();
runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font(); runtime_tiles::watch_icon_font = id(watch_icon)->get_lv_font();
@@ -902,7 +903,7 @@ esphome:
- lambda: |- - lambda: |-
static const char *entities[] = {"${TILE1_ENTITY}", "${TILE2_ENTITY}", "${TILE3_ENTITY}", "${TILE4_ENTITY}", "${TILE5_ENTITY}", "${TILE6_ENTITY}", "${TILE7_ENTITY}", "${TILE9_ENTITY}"}; static const char *entities[] = {"${TILE1_ENTITY}", "${TILE2_ENTITY}", "${TILE3_ENTITY}", "${TILE4_ENTITY}", "${TILE5_ENTITY}", "${TILE6_ENTITY}", "${TILE7_ENTITY}", "${TILE9_ENTITY}"};
if (!runtime_tiles::enabled) light_controls::subscribe(entities, 8); if (!runtime_tiles::enabled) light_controls::subscribe(entities, 8);
light_controls::setup(id(color_detail_overlay), id(sublabel)->get_lv_font(), atoi("${DISPLAY_W}"), atoi("${DISPLAY_H}")); light_controls::setup(id(color_detail_overlay), id(sublabel)->get_lv_font(), atoi("${DISPLAY_W}"), atoi("${DISPLAY_H}"), true);
light_controls::commits[0] = [](int value) { id(set_light_hue).execute(value); }; light_controls::commits[0] = [](int value) { id(set_light_hue).execute(value); };
light_controls::commits[1] = [](int value) { id(set_light_color_temperature).execute(value); }; light_controls::commits[1] = [](int value) { id(set_light_color_temperature).execute(value); };
light_controls::commits[2] = [](int value) { id(color_brightness_commit).execute(value); }; light_controls::commits[2] = [](int value) { id(color_brightness_commit).execute(value); };
@@ -1992,7 +1993,9 @@ lvgl:
bg_color: 0xFFFFFF bg_color: 0xFFFFFF
bg_opa: COVER bg_opa: COVER
radius: 18 radius: 18
border_width: 0 border_width: 1
border_color: 0xDDDDDD
border_opa: COVER
pad_left: 8 pad_left: 8
pad_right: 8 pad_right: 8
pad_top: 8 pad_top: 8
@@ -2001,10 +2004,12 @@ lvgl:
shadow_opa: 40% shadow_opa: 40%
- id: style_tile_disabled - id: style_tile_disabled
bg_color: 0x939391 bg_color: 0xFFFFFF
bg_opa: COVER bg_opa: COVER
radius: 18 radius: 18
border_width: 0 border_width: 1
border_color: 0xDDDDDD
border_opa: COVER
pad_left: 8 pad_left: 8
pad_right: 8 pad_right: 8
pad_top: 8 pad_top: 8
@@ -2013,25 +2018,25 @@ lvgl:
shadow_opa: 25% shadow_opa: 25%
- id: style_icon_circle - id: style_icon_circle
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
radius: 999 radius: 999
border_width: 0 border_width: 0
- id: style_icon_circle_disabled - id: style_icon_circle_disabled
bg_color: 0x7B7B6F bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
radius: 999 radius: 999
border_width: 0 border_width: 0
- id: style_room - id: style_room
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
- id: style_time - id: style_time
text_font: time_label text_font: time_label
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
- id: style_title - id: style_title
@@ -2046,17 +2051,17 @@ lvgl:
- id: style_value_big - id: style_value_big
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 80% text_opa: 80%
- id: style_title_disabled - id: style_title_disabled
text_font: label text_font: label
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
- id: style_value_disabled - id: style_value_disabled
text_font: sublabel text_font: sublabel
text_color: 0xD9D9D9 text_color: 0x1B1B1B
text_opa: 100% text_opa: 100%
pages: pages:
@@ -3297,19 +3302,22 @@ lvgl:
was_on: false was_on: false
# ---------- VALUE SLIDER OVERLAY ---------- # ---------- VALUE SLIDER OVERLAY ----------
# ---------- PAGE NAVIGATION: one full-width bar ----------
- obj: - obj:
id: page_prev id: page_prev
align: BOTTOM_LEFT align: BOTTOM_LEFT
x: 9 x: 0
y: -2 y: 0
width: 100 width: 130
height: 32 height: 32
pad_all: 0 pad_all: 0
radius: 8 radius: 0
bg_color: 0x3C3C40 bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3319,31 +3327,39 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: LEFT_MID
x: 12
text: "< Vorige" text: "< Vorige"
text_font: sublabel text_font: label
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
id: page_number id: page_number
align: BOTTOM_MID align: BOTTOM_MID
y: -9 y: 0
width: 60
height: 32
pad_top: 9
border_width: 0
text: "1 / 2" text: "1 / 2"
text_align: CENTER
text_font: sublabel text_font: sublabel
text_color: 0xFFFFFF text_color: 0x1B1B1B
- obj: - obj:
id: page_next id: page_next
align: BOTTOM_RIGHT align: BOTTOM_RIGHT
x: -9 x: 0
y: -2 y: 0
width: 100 width: 130
height: 32 height: 32
pad_all: 0 pad_all: 0
radius: 8 radius: 0
bg_color: 0x3C3C40 bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3353,10 +3369,11 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: RIGHT_MID
x: -12
text: "Volgende >" text: "Volgende >"
text_font: sublabel text_font: label
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -3393,7 +3410,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 18% bg_opa: 18%
border_width: 0 border_width: 0
clickable: false clickable: false
@@ -3424,7 +3441,7 @@ lvgl:
value: 100 value: 100
adv_hittest: false adv_hittest: false
bg_color: 0x939391 bg_color: 0xFFFFFF
bg_opa: 100% bg_opa: 100%
radius: 28 radius: 28
# Enige widget met ZOWEL radius ALS een kind (overlay_icon). # Enige widget met ZOWEL radius ALS een kind (overlay_icon).
@@ -3437,7 +3454,7 @@ lvgl:
border_width: 0 border_width: 0
indicator: indicator:
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: 100% bg_opa: 100%
radius: 28 radius: 28
@@ -3469,7 +3486,7 @@ lvgl:
align: CENTER align: CENTER
y: ${OVERLAY_ICON_Y} y: ${OVERLAY_ICON_Y}
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
text: "${TILE1_ICON}" text: "${TILE1_ICON}"
clickable: false clickable: false
@@ -3678,7 +3695,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x171719 bg_color: 0xF7F7F7
bg_opa: COVER bg_opa: COVER
hidden: true hidden: true
scrollable: false scrollable: false
@@ -3704,7 +3721,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 72% bg_opa: 72%
border_width: 0 border_width: 0
clickable: false clickable: false
@@ -3718,7 +3735,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -3732,7 +3749,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
@@ -3765,7 +3782,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- CLIMATE DETAIL OVERLAY ---------- # ---------- CLIMATE DETAIL OVERLAY ----------
@@ -3775,7 +3792,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x171719 bg_color: 0xF7F7F7
bg_opa: COVER bg_opa: COVER
hidden: true hidden: true
scrollable: false scrollable: false
@@ -3801,7 +3818,7 @@ lvgl:
y: 0 y: 0
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 62% bg_opa: 62%
border_width: 0 border_width: 0
clickable: false clickable: false
@@ -3817,7 +3834,7 @@ lvgl:
width: 44 width: 44
height: 44 height: 44
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -3831,7 +3848,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -3842,7 +3859,7 @@ lvgl:
width: 44 width: 44
height: 44 height: 44
radius: 999 radius: 999
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -3854,7 +3871,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
@@ -3870,7 +3887,7 @@ lvgl:
y: 42 y: 42
text: "Current: --" text: "Current: --"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xD1E8FF text_color: 0x1B1B1B
text_opa: 85% text_opa: 85%
clickable: false clickable: false
@@ -3921,7 +3938,7 @@ lvgl:
y: -16 y: -16
text: "SET TO" text: "SET TO"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
text_opa: 68% text_opa: 68%
clickable: false clickable: false
@@ -3931,7 +3948,7 @@ lvgl:
y: 20 y: 20
text: "20.0°" text: "20.0°"
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -3942,7 +3959,7 @@ lvgl:
width: 48 width: 48
height: 36 height: 36
radius: 18 radius: 18
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -3958,7 +3975,7 @@ lvgl:
align: CENTER align: CENTER
text: "-" text: "-"
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -3969,7 +3986,7 @@ lvgl:
width: 48 width: 48
height: 36 height: 36
radius: 18 radius: 18
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -3985,7 +4002,7 @@ lvgl:
align: CENTER align: CENTER
text: "+" text: "+"
text_font: headline text_font: headline
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -3995,7 +4012,7 @@ lvgl:
width: 126 width: 126
height: 34 height: 34
radius: 17 radius: 17
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 82% bg_opa: 82%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4008,7 +4025,7 @@ lvgl:
align: CENTER align: CENTER
text: "Auto" text: "Auto"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- CLIMATE MODE PICKER ---------- # ---------- CLIMATE MODE PICKER ----------
# Vervangt het doorcyclen van modi. Cyclen stuurt bij elke tik een # Vervangt het doorcyclen van modi. Cyclen stuurt bij elke tik een
@@ -4022,7 +4039,7 @@ lvgl:
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
hidden: true hidden: true
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 90% bg_opa: 90%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4034,7 +4051,7 @@ lvgl:
y: 16 y: 16
text: "Mode" text: "Mode"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# Flex zorgt dat niet-ondersteunde modi geen gat achterlaten: # Flex zorgt dat niet-ondersteunde modi geen gat achterlaten:
@@ -4061,7 +4078,7 @@ lvgl:
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4074,14 +4091,14 @@ lvgl:
align: CENTER align: CENTER
text: "Off" text: "Off"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_heat id: cmode_heat
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4094,14 +4111,14 @@ lvgl:
align: CENTER align: CENTER
text: "Heat" text: "Heat"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_cool id: cmode_cool
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4114,14 +4131,14 @@ lvgl:
align: CENTER align: CENTER
text: "Cool" text: "Cool"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_heat_cool id: cmode_heat_cool
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4134,14 +4151,14 @@ lvgl:
align: CENTER align: CENTER
text: "Heat/Cool" text: "Heat/Cool"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_auto id: cmode_auto
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4154,14 +4171,14 @@ lvgl:
align: CENTER align: CENTER
text: "Auto" text: "Auto"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_dry id: cmode_dry
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4174,14 +4191,14 @@ lvgl:
align: CENTER align: CENTER
text: "Dry" text: "Dry"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: cmode_fan_only id: cmode_fan_only
width: 132 width: 132
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4194,7 +4211,7 @@ lvgl:
align: CENTER align: CENTER
text: "Fan" text: "Fan"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4205,7 +4222,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -4219,7 +4236,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4230,7 +4247,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4242,7 +4259,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- VACUUM CONTROL ---------- # ---------- VACUUM CONTROL ----------
@@ -4255,7 +4272,7 @@ lvgl:
width: ${DISPLAY_W} width: ${DISPLAY_W}
height: ${DISPLAY_H} height: ${DISPLAY_H}
hidden: true hidden: true
bg_color: 0x000000 bg_color: 0xFFFFFF
bg_opa: 90% bg_opa: 90%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4268,7 +4285,7 @@ lvgl:
y: 16 y: 16
text: "Vacuum" text: "Vacuum"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4293,7 +4310,7 @@ lvgl:
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4306,14 +4323,14 @@ lvgl:
align: CENTER align: CENTER
text: "Start" text: "Start"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vac_pause id: vac_pause
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4326,14 +4343,14 @@ lvgl:
align: CENTER align: CENTER
text: "Pause" text: "Pause"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vac_return_to_base id: vac_return_to_base
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4346,14 +4363,14 @@ lvgl:
align: CENTER align: CENTER
text: "Dock" text: "Dock"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vac_stop id: vac_stop
width: 72 width: 72
height: 38 height: 38
radius: 19 radius: 19
bg_color: 0x2C2C30 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4366,7 +4383,7 @@ lvgl:
align: CENTER align: CENTER
text: "Stop" text: "Stop"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
@@ -4374,7 +4391,7 @@ lvgl:
y: 138 y: 138
text: "Suction" text: "Suction"
text_font: sublabel text_font: sublabel
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4399,7 +4416,7 @@ lvgl:
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4414,14 +4431,14 @@ lvgl:
align: CENTER align: CENTER
text: "Quiet" text: "Quiet"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vacspd_balanced id: vacspd_balanced
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4436,14 +4453,14 @@ lvgl:
align: CENTER align: CENTER
text: "Balanced" text: "Balanced"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vacspd_turbo id: vacspd_turbo
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4458,14 +4475,14 @@ lvgl:
align: CENTER align: CENTER
text: "Turbo" text: "Turbo"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
id: vacspd_max id: vacspd_max
width: 72 width: 72
height: 32 height: 32
radius: 19 radius: 19
bg_color: 0x1F1F23 bg_color: 0xF0F0F0
bg_opa: 92% bg_opa: 92%
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4480,7 +4497,7 @@ lvgl:
align: CENTER align: CENTER
text: "Max" text: "Max"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4491,7 +4508,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0x3C3C40 bg_color: 0xEEEEEE
bg_opa: COVER bg_opa: COVER
border_width: 1 border_width: 1
border_color: 0xFFFFFF border_color: 0xFFFFFF
@@ -4505,7 +4522,7 @@ lvgl:
align: CENTER align: CENTER
text: "x" text: "x"
text_font: sublabel_big text_font: sublabel_big
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
- obj: - obj:
@@ -4516,7 +4533,7 @@ lvgl:
width: 36 width: 36
height: 36 height: 36
radius: 999 radius: 999
bg_color: 0xFEC600 bg_color: 0xD5EEFC
bg_opa: COVER bg_opa: COVER
border_width: 0 border_width: 0
clickable: true clickable: true
@@ -4528,7 +4545,7 @@ lvgl:
align: CENTER align: CENTER
text: "\U000F012C" text: "\U000F012C"
text_font: materialdesign_icons text_font: materialdesign_icons
text_color: 0xFFFFFF text_color: 0x1B1B1B
clickable: false clickable: false
# ---------- DIM / WAKE OVERLAY ---------- # ---------- DIM / WAKE OVERLAY ----------
+30 -18
View File
@@ -2,7 +2,7 @@
# Guition ESP32-S3-4848S040 / 4-inch EU wallbox, 480x480 IPS + GT911. # 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. # Use guition-device.yaml; see docs/GUITION.md. CYD hardware is a separate profile.
substitutions: substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.14" SCREEN_FIRMWARE_VERSION: "0.2.15"
DYNAMIC_TILES: "true" DYNAMIC_TILES: "true"
DASHBOARD_INBOX_INTERNAL: "false" DASHBOARD_INBOX_INTERNAL: "false"
DEVICE_NAME: "guition-new" DEVICE_NAME: "guition-new"
@@ -3160,19 +3160,22 @@ lvgl:
was_on: false was_on: false
# ---------- VALUE SLIDER OVERLAY ---------- # ---------- VALUE SLIDER OVERLAY ----------
# ---------- PAGE NAVIGATION: one full-width bar ----------
- obj: - obj:
id: page_prev id: page_prev
align: BOTTOM_LEFT align: BOTTOM_LEFT
x: 14 x: 0
y: -4 y: 0
width: 150 width: 200
height: 44 height: 44
pad_all: 0 pad_all: 0
radius: 12 radius: 0
bg_color: 0xEEEEEE bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3182,31 +3185,39 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: LEFT_MID
x: 24
text: "< Vorige" text: "< Vorige"
text_font: sublabel text_font: label
text_color: 0x1B1B1B text_color: 0x1B1B1B
clickable: false clickable: false
- label: - label:
id: page_number id: page_number
align: BOTTOM_MID align: BOTTOM_MID
y: -18 y: 0
width: 80
height: 44
pad_top: 12
border_width: 0
text: "1 / 2" text: "1 / 2"
text_align: CENTER
text_font: sublabel text_font: sublabel
text_color: 0x1B1B1B text_color: 0x1B1B1B
- obj: - obj:
id: page_next id: page_next
align: BOTTOM_RIGHT align: BOTTOM_RIGHT
x: -14 x: 0
y: -4 y: 0
width: 150 width: 200
height: 44 height: 44
pad_all: 0 pad_all: 0
radius: 12 radius: 0
bg_color: 0xEEEEEE bg_opa: TRANSP
bg_opa: COVER
border_width: 0 border_width: 0
scrollable: false scrollable: false
pressed:
bg_color: 0xE3E3E3
bg_opa: COVER
on_short_click: on_short_click:
- if: - if:
condition: condition:
@@ -3216,9 +3227,10 @@ lvgl:
- script.execute: show_tile_page - script.execute: show_tile_page
widgets: widgets:
- label: - label:
align: CENTER align: RIGHT_MID
x: -24
text: "Volgende >" text: "Volgende >"
text_font: sublabel text_font: label
text_color: 0x1B1B1B text_color: 0x1B1B1B
clickable: false clickable: false
+10
View File
@@ -1,3 +1,13 @@
## 0.2.13 (firmware 0.2.15)
- Editor: sleep tegels in de schermmockup om te ordenen en sleep entiteiten rechtstreeks uit de lijst naar een plek in de mockup — met muis én touch (even vasthouden). De opslaan-balk blijft altijd in beeld.
- Nieuwe kop met duidelijke acties (**Nieuw scherm**, **Firmware & USB**, **Uitleg**) en een korte uitleg in drie stappen; de firmwaredialoog legt profiel, doel en knoppen uit.
- **Zonnebaan**: `sun.sun` toont dubbelbreed een horizon met de zon op zijn huidige positie tussen opkomst en ondergang (’s nachts onder de horizon), met beide tijden. Nieuwe zon-, weer- en kloktegels starten meteen dubbelbreed in hun mooiste weergave.
- Grafiek: vloeiende curve door dezelfde 24 punten met een zachte vulling eronder, zoals de HA-trendkaart.
- Scherm: de paginering is één geïntegreerde onderbalk — links tikken is vorige, rechts is volgende, het paginanummer staat in het midden.
- Scherm: bij een paginawissel verschijnen direct de kaartkaders van de nieuwe pagina (skeleton) en vult de inhoud een fractie later; geen oude waarden meer in nieuwe kaders.
- CYD: hetzelfde lichte kleurenschema als de Guition — lichtgrijze achtergrond, witte kaarten met een fijne rand, donkere tekst en lichtblauwe accenten, ook in de bedieningskaarten.
## 0.2.12 (firmware 0.2.14) ## 0.2.12 (firmware 0.2.14)
- Nieuwe kaarten in de kiezer: **Klok** (digitaal of analoog, ingebouwd), **weersvoorspelling** met vijf dagen op een dubbelbrede weerkaart, **grafiek** van de sensorgeschiedenis in de tegel, **zon** (`sun.sun`, opkomst en ondergang in je eigen tijdzone), **kookwekker** (`timer.*`, live aftellen; tikken start of pauzeert, lang indrukken annuleert) en **aanwezigheid** (`person.*`). - Nieuwe kaarten in de kiezer: **Klok** (digitaal of analoog, ingebouwd), **weersvoorspelling** met vijf dagen op een dubbelbrede weerkaart, **grafiek** van de sensorgeschiedenis in de tegel, **zon** (`sun.sun`, opkomst en ondergang in je eigen tijdzone), **kookwekker** (`timer.*`, live aftellen; tikken start of pauzeert, lang indrukken annuleert) en **aanwezigheid** (`person.*`).
+5 -3
View File
@@ -30,7 +30,9 @@ TILE_BACKGROUNDS = {
} }
# Display modes per domain; everything else offers standard and watch (large value). # Display modes per domain; everything else offers standard and watch (large value).
DISPLAYS = {'weather': ('standard', 'watch', 'forecast'), 'sensor': ('standard', 'watch', 'graph'), 'screen': ('digital', 'analog')} DISPLAYS = {'weather': ('standard', 'watch', 'forecast'), 'sensor': ('standard', 'watch', 'graph'), 'screen': ('digital', 'analog'), 'sun': ('standard', 'watch', 'sunpath')}
# Displays that only work on a double-width card.
WIDE_ONLY = ('forecast', 'sunpath')
# Additive schema 1 extension. An absent object retains old firmware/YAML defaults. # Additive schema 1 extension. An absent object retains old firmware/YAML defaults.
SETTING_RULES = { SETTING_RULES = {
@@ -114,8 +116,8 @@ def validate_layout(data):
for key, allowed in choices.items(): for key, allowed in choices.items():
if key in options and options[key] not in allowed: if key in options and options[key] not in allowed:
raise ValueError('Ongeldige tegelinstelling: ' + key) raise ValueError('Ongeldige tegelinstelling: ' + key)
# The five-day strip only fits a double-width card. # The five-day strip and the sun path only fit a double-width card.
if options.get('display') == 'forecast': if options.get('display') in WIDE_ONLY:
options = {**options, 'size': 'wide'} options = {**options, 'size': 'wide'}
if options.get('tap') == 'toggle' and domain not in {'light','switch','input_boolean','fan','media_player'}: if options.get('tap') == 'toggle' and domain not in {'light','switch','input_boolean','fan','media_player'}:
raise ValueError('Deze entiteit ondersteunt geen aan/uit-actie.') raise ValueError('Deze entiteit ondersteunt geen aan/uit-actie.')
+107 -36
View File
@@ -5,7 +5,6 @@ let inventory = { screens: [], entities: [] },
layout = null, layout = null,
dirty = false, dirty = false,
filter = "", filter = "",
dragIndex = -1,
busy = false, busy = false,
selectedTile = null; selectedTile = null;
const node = (tag, text, cls) => { const node = (tag, text, cls) => {
@@ -225,7 +224,15 @@ const domains = {
timer: ["Kookwekker", "⏱", "#008577", "#def3ed"], timer: ["Kookwekker", "⏱", "#008577", "#def3ed"],
person: ["Persoon", "☺", "#2f7d32", "#e1f2e2"], person: ["Persoon", "☺", "#2f7d32", "#e1f2e2"],
}; };
const displayNames = { standard: "standaard", watch: "grote waarde", forecast: "weersvoorspelling", graph: "grafiek", digital: "digitale klok", analog: "analoge klok" }; const displayNames = { standard: "standaard", watch: "grote waarde", forecast: "weersvoorspelling", graph: "grafiek", digital: "digitale klok", analog: "analoge klok", sunpath: "zonnebaan" };
// New tiles start with the card that shows the entity best.
function defaultOptions(id) {
const domain = id.split(".")[0];
if (domain === "sun") return { options: { display: "sunpath", size: "wide" } };
if (domain === "weather") return { options: { display: "forecast", size: "wide" } };
if (domain === "screen") return { options: { display: "digital", size: "wide" } };
return {};
}
// Same packing as the firmware: wide tiles start in the left column and take a whole row. // Same packing as the firmware: wide tiles start in the left column and take a whole row.
function packTiles(tiles) { function packTiles(tiles) {
let position = 0; let position = 0;
@@ -283,11 +290,8 @@ function renderPreview() {
if(background)card.style.backgroundColor=background; if(background)card.style.backgroundColor=background;
if (place.wide) card.classList.add("wide"); if (place.wide) card.classList.add("wide");
card.append(domainBadge(tile.entity), node("strong", name)); card.append(domainBadge(tile.entity), node("strong", name));
card.draggable = true; card.dataset.index = index;
card.ondragstart = (e) => { dragIndex = index; e.dataTransfer.setData("text/plain", String(index)); }; enableDrag(card, { kind: "tile", index });
card.ondragend = () => { dragIndex = -1; };
card.ondragover = (e) => { if (dragIndex >= 0) e.preventDefault(); };
card.ondrop = (e) => { e.preventDefault(); if (dragIndex >= 0) move(dragIndex, index); };
card.classList.toggle("chosen", selectedTile === tile.entity); card.classList.toggle("chosen", selectedTile === tile.entity);
card.setAttribute("aria-label", `Tegel ${index + 1}: ${name}, instellen`); card.setAttribute("aria-label", `Tegel ${index + 1}: ${name}, instellen`);
card.onclick = () => { card.onclick = () => {
@@ -322,27 +326,6 @@ function renderTiles() {
if (i > 0 && placement[i].page !== placement[i - 1].page) $("#tiles").append(node("li", `Pagina ${placement[i].page + 1}`, "page-break")); if (i > 0 && placement[i].page !== placement[i - 1].page) $("#tiles").append(node("li", `Pagina ${placement[i].page + 1}`, "page-break"));
const li = node("li", undefined, "tile"); const li = node("li", undefined, "tile");
li.classList.toggle("selected-tile", selectedTile === tile.entity); li.classList.toggle("selected-tile", selectedTile === tile.entity);
li.draggable = true;
li.ondragstart = (e) => {
if (e.target.closest("input, select, button")) {
e.preventDefault();
return;
}
dragIndex = i;
e.dataTransfer.setData("text/plain", String(i));
li.classList.add("dragging");
};
li.ondragend = () => {
dragIndex = -1;
li.classList.remove("dragging");
};
li.ondragover = (e) => {
if (dragIndex >= 0) e.preventDefault();
};
li.ondrop = (e) => {
e.preventDefault();
if (dragIndex >= 0) move(dragIndex, i);
};
const content = node("div"), const content = node("div"),
input = node("input"); input = node("input");
input.value = tile.name; input.value = tile.name;
@@ -423,6 +406,7 @@ function renderTiles() {
: [["standard", "Naam en status"], ["watch", "Grote waarde"]]; : [["standard", "Naam en status"], ["watch", "Grote waarde"]];
if (domain === "weather") displays.push(["forecast", "Weersvoorspelling (dubbelbreed)"]); if (domain === "weather") displays.push(["forecast", "Weersvoorspelling (dubbelbreed)"]);
if (domain === "sensor") displays.push(["graph", "Grafiek van de geschiedenis"]); if (domain === "sensor") displays.push(["graph", "Grafiek van de geschiedenis"]);
if (domain === "sun") displays.push(["sunpath", "Zonnebaan (dubbelbreed)"]);
const fields = [ const fields = [
[ [
"tap", "tap",
@@ -498,7 +482,7 @@ function renderTiles() {
}; };
if (key === "display" && input.value === "watch") if (key === "display" && input.value === "watch")
tile.options.inline = "none"; tile.options.inline = "none";
if (key === "display" && input.value === "forecast") if (key === "display" && ["forecast", "sunpath"].includes(input.value))
tile.options.size = "wide"; tile.options.size = "wide";
if (key === "inline" && input.value === "slider") if (key === "inline" && input.value === "slider")
tile.options.display = "standard"; tile.options.display = "standard";
@@ -522,6 +506,97 @@ function renderTiles() {
$("#tiles").append(li); $("#tiles").append(li);
}); });
} }
function addTile(id, at) {
if (layout.tiles.some((t) => t.entity === id) || layout.tiles.length >= tileLimit()) return;
layout.tiles.splice(Math.min(at, layout.tiles.length), 0, { entity: id, name: "", ...defaultOptions(id) });
selectedTile = id;
markDirty();
renderTiles();
renderResults();
}
// Pointer-based drag & drop: works with mouse and touch, from the picker into
// the mockup and between tiles. Touch starts after a short hold so the page
// still scrolls; a finished drag never doubles as a click.
const drag = { active: false, source: null, element: null, ghost: null, target: null, timer: 0, start: null, offset: null, pointerId: null, suppressUntil: 0, last: null, scroller: 0 };
function enableDrag(element, source) {
element.addEventListener("pointerdown", (e) => {
if (e.button !== 0 || element.disabled) return;
Object.assign(drag, { source, element, start: { x: e.clientX, y: e.clientY }, pointerId: e.pointerId, active: false });
clearTimeout(drag.timer);
if (e.pointerType === "touch") drag.timer = setTimeout(() => beginDrag(e), 260);
});
element.addEventListener("pointermove", (e) => {
if (!drag.start || drag.element !== element) return;
const distance = Math.hypot(e.clientX - drag.start.x, e.clientY - drag.start.y);
if (!drag.active) {
if (e.pointerType === "touch") { if (distance > 10) { clearTimeout(drag.timer); drag.start = null; } return; }
if (distance < 6) return;
beginDrag(e);
}
moveDrag(e);
});
const finish = (e) => {
if (drag.element !== element) return;
clearTimeout(drag.timer);
if (drag.active) endDrag(e.type === "pointerup");
drag.start = null;
};
element.addEventListener("pointerup", finish);
element.addEventListener("pointercancel", finish);
}
function beginDrag(e) {
if (drag.active || !drag.start) return;
drag.active = true;
try { drag.element.setPointerCapture(drag.pointerId); } catch {}
const rect = drag.element.getBoundingClientRect();
const ghost = drag.element.cloneNode(true);
ghost.classList.add("drag-ghost");
ghost.style.width = `${rect.width}px`;
drag.offset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
document.body.append(ghost);
drag.ghost = ghost;
document.body.classList.add("dragging");
document.addEventListener("touchmove", blockScroll, { passive: false });
// Near the viewport edges the page scrolls along, so the mockup can be reached on small screens.
drag.scroller = setInterval(() => {
if (!drag.last) return;
const step = drag.last.y < 70 ? -12 : drag.last.y > innerHeight - 70 ? 12 : 0;
if (step) { window.scrollBy(0, step); setTarget(document.elementFromPoint(drag.last.x, drag.last.y)?.closest(".preview-tile, .preview-gap, .preview-grid") || null); }
}, 16);
moveDrag(e);
}
function blockScroll(e) { if (drag.active) e.preventDefault(); }
function moveDrag(e) {
if (!drag.ghost) return;
drag.last = { x: e.clientX, y: e.clientY };
drag.ghost.style.transform = `translate(${e.clientX - drag.offset.x}px, ${e.clientY - drag.offset.y}px)`;
const under = document.elementFromPoint(e.clientX, e.clientY);
setTarget(under?.closest(".preview-tile, .preview-gap, .preview-grid") || null);
}
function setTarget(target) {
if (drag.target === target) return;
drag.target?.classList.remove("drop-target");
drag.target = target;
drag.target?.classList.add("drop-target");
}
function endDrag(drop) {
const { target, source } = drag;
setTarget(null);
drag.ghost?.remove();
drag.ghost = null;
drag.active = false;
drag.suppressUntil = Date.now() + 400;
clearInterval(drag.scroller);
drag.last = null;
document.body.classList.remove("dragging");
document.removeEventListener("touchmove", blockScroll);
try { drag.element.releasePointerCapture(drag.pointerId); } catch {}
if (!drop || !target) return;
const to = target.dataset.index !== undefined ? Number(target.dataset.index) : layout.tiles.length;
if (source.kind === "tile") { if (to !== source.index) move(source.index, to); }
else addTile(source.id, to);
}
window.addEventListener("click", (e) => { if (Date.now() < drag.suppressUntil) { e.stopPropagation(); e.preventDefault(); } }, true);
function renderResults() { function renderResults() {
if (!layout) return; if (!layout) return;
const query = $("#search").value.toLocaleLowerCase(); const query = $("#search").value.toLocaleLowerCase();
@@ -550,13 +625,8 @@ function renderResults() {
node("span", chosen.has(entity.id) ? "✓" : "+", "plus"), node("span", chosen.has(entity.id) ? "✓" : "+", "plus"),
); );
b.disabled = chosen.has(entity.id) || layout.tiles.length >= tileLimit(); b.disabled = chosen.has(entity.id) || layout.tiles.length >= tileLimit();
b.onclick = () => { b.onclick = () => addTile(entity.id, layout.tiles.length);
layout.tiles.push({ entity: entity.id, name: "" }); if (!b.disabled) enableDrag(b, { kind: "entity", id: entity.id });
selectedTile = entity.id;
markDirty();
renderTiles();
renderResults();
};
$("#results").append(b); $("#results").append(b);
} }
if (!matches.length) if (!matches.length)
@@ -845,6 +915,7 @@ function openSection(id) {
section.scrollIntoView({ behavior: "smooth", block: "start" }); section.scrollIntoView({ behavior: "smooth", block: "start" });
} }
$("#nav-tiles").onclick = () => openSection("#tile-section"); $("#nav-tiles").onclick = () => openSection("#tile-section");
$("#open-help").onclick = () => openSection("#help");
$("#nav-settings").onclick = () => openSection("#general-settings"); $("#nav-settings").onclick = () => openSection("#general-settings");
$("#nav-inspector").onclick = () => inspect(); $("#nav-inspector").onclick = () => inspect();
async function inspect(entity) { async function inspect(entity) {
+65 -12
View File
@@ -12,11 +12,29 @@
<div class="brand"> <div class="brand">
<span class="mark">▦</span> <span class="mark">▦</span>
<div> <div>
<strong>ESP Screens</strong><small>Je huis. Jouw bediening.</small> <strong>ESP Screens</strong
><small>Tegels voor je CYD en Guition, zonder opnieuw flashen</small>
</div> </div>
</div> </div>
<button id="open-firmware" class="quiet">Firmware & USB</button> <nav class="toolbar" aria-label="Acties">
<button id="new-screen">+ Nieuw scherm</button> <button id="new-screen" class="tool tool-primary">
<span class="tool-icon" aria-hidden="true">+</span>
<span class="tool-text"
><b>Nieuw scherm</b><small>Koppel een CYD of Guition</small></span
>
</button>
<button id="open-firmware" class="tool">
<span class="tool-icon" aria-hidden="true">⇪</span>
<span class="tool-text"
><b>Firmware & USB</b
><small>Bouwen en installeren via USB of wifi</small></span
>
</button>
<button id="open-help" class="tool">
<span class="tool-icon" aria-hidden="true">?</span>
<span class="tool-text"><b>Uitleg</b><small>Hoe werkt het</small></span>
</button>
</nav>
</header> </header>
<main> <main>
<div class="intro"> <div class="intro">
@@ -27,6 +45,26 @@
</div> </div>
<span id="connection" role="status" class="badge">Verbinden…</span> <span id="connection" role="status" class="badge">Verbinden…</span>
</div> </div>
<ol class="howto" aria-label="Zo werkt het">
<li>
<b>1 · Koppelen</b><span
>Kies <b>Nieuw scherm</b> voor de installatie-YAML met eigen
sleutels. Eén keer via USB flashen, daarna gaat alles draadloos.</span
>
</li>
<li>
<b>2 · Tegels kiezen</b><span
>Sleep entiteiten uit de lijst rechts in de schermmockup, of tik
op +. Klik op een tegel voor weergave, breedte en kleur.</span
>
</li>
<li>
<b>3 · Opslaan</b><span
>Met <b>Opslaan & naar scherm</b> staat het direct op je scherm.
Nieuwe schermfuncties installeer je via <b>Firmware & USB</b>.</span
>
</li>
</ol>
<div class="workspace"> <div class="workspace">
<aside> <aside>
<h2>Mijn schermen</h2> <h2>Mijn schermen</h2>
@@ -136,7 +174,7 @@
<b>Firmware & USB</b> bovenaan. Je bestaande YAML en secrets blijven <b>Firmware & USB</b> bovenaan. Je bestaande YAML en secrets blijven
bewaard. bewaard.
</p> </p>
<details class="help"> <details class="help" id="help">
<summary>Hoe werkt het dagelijks?</summary> <summary>Hoe werkt het dagelijks?</summary>
<p> <p>
Zoek een entiteit, voeg deze toe en klik op Zoek een entiteit, voeg deze toe en klik op
@@ -147,9 +185,18 @@
waarde, mini-schuif of klikgedrag. waarde, mini-schuif of klikgedrag.
</p> </p>
<p> <p>
Nieuwe firmware bouw en installeer je hier met de ingebouwde ESPHome Speciale kaarten: de <b>Klok</b> staat bovenaan de kiezer; een
CLI. Je tegels blijven in deze app bewaard. Een offline scherm krijgt weerentiteit toont de vijfdaagse voorspelling, <code>sun.sun</code>
de wijzigingen zodra het terug is. de zonnebaan, een sensor een trendgrafiek, een <code>timer</code>
telt live af en een <code>person</code> toont thuis/weg. Zet een
tegel op <b>Dubbelbreed</b> voor meer ruimte.
</p>
<p>
Nieuwe firmware bouw en installeer je via <b>Firmware & USB</b> met
de ingebouwde ESPHome CLI: kies je eigen profiel, dan een USB-poort of
wifi, en klik op <b>Bouwen & installeren</b>. Je tegels blijven in
deze app bewaard; een offline scherm krijgt de wijzigingen zodra het
terug is.
</p> </p>
</details> </details>
</main> </main>
@@ -271,13 +318,19 @@
</dialog> </dialog>
<dialog id="firmware-dialog"> <dialog id="firmware-dialog">
<div class="dialog-head"> <div class="dialog-head">
<h2>Firmware & USB</h2> <div>
<button id="close-firmware" class="quiet">Sluiten</button> <p class="eyebrow">FIRMWARE & USB</p>
<h2>Schermfirmware bouwen en installeren</h2>
</div>
<button id="close-firmware" class="quiet" aria-label="Sluiten">✕</button>
</div> </div>
<p> <p>
De ingebouwde ESPHome CLI gebruikt je eigen YAML en secrets. Tegels De ingebouwde ESPHome CLI gebruikt je eigen profiel (YAML met sleutels)
blijven in ESP Screens staan. Op de Raspberry kan de eerste build enkele en secrets. Kies het profiel van het scherm, dan een USB-poort (scherm
minuten duren. aan de Home Assistant-machine) of <b>Wifi / OTA</b> met de hostnaam.
<b>Controleren</b> valideert alleen, <b>Alleen bouwen</b> compileert,
<b>Bouwen & installeren</b> zet de firmware op het scherm. Je tegels
blijven in ESP Screens staan. De eerste build duurt enkele minuten.
</p> </p>
<label>ESPHome-profiel<select id="firmware-file"></select></label> <label>ESPHome-profiel<select id="firmware-file"></select></label>
<label <label
+112 -2
View File
@@ -184,6 +184,98 @@ aside h2 {
color: var(--muted); color: var(--muted);
padding: 7px; padding: 7px;
} }
.toolbar {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.tool {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 14px 8px 9px;
border: 1px solid var(--line);
border-radius: 14px;
background: white;
color: var(--ink);
text-align: left;
}
.tool:hover {
border-color: #b9cbec;
box-shadow: 0 3px 10px #202c3c0c;
}
.tool-icon {
width: 34px;
height: 34px;
border-radius: 10px;
background: var(--soft);
color: var(--accent);
display: grid;
place-items: center;
font-size: 19px;
font-weight: 700;
flex: none;
}
.tool-primary .tool-icon {
background: var(--accent);
color: white;
}
.tool-text b {
display: block;
font-size: 13px;
}
.tool-text small {
color: var(--muted);
font-size: 11px;
}
.howto {
list-style: none;
padding: 0;
margin: 0 0 26px;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.howto li {
background: white;
border: 1px solid var(--line);
border-radius: 14px;
padding: 12px 14px;
font-size: 12px;
color: var(--muted);
line-height: 1.45;
}
.howto li > b {
display: block;
color: var(--ink);
font-size: 13px;
margin-bottom: 4px;
}
.preview-tile,
.result {
touch-action: pan-y;
cursor: grab;
}
.drag-ghost {
position: fixed;
left: 0;
top: 0;
z-index: 1000;
pointer-events: none;
opacity: 0.92;
box-shadow: 0 14px 32px #202c3c33;
background: white;
margin: 0;
}
.drop-target {
outline: 2px solid var(--accent);
outline-offset: 2px;
background: var(--soft) !important;
}
body.dragging {
cursor: grabbing;
user-select: none;
}
#editor, #editor,
.empty { .empty {
background: white; background: white;
@@ -331,9 +423,15 @@ aside h2 {
font-size: 22px; font-size: 22px;
} }
.savebar { .savebar {
margin-top: 25px; position: sticky;
padding-top: 20px; bottom: 0;
z-index: 6;
margin: 25px -27px -27px;
padding: 16px 27px;
background: #ffffffee;
backdrop-filter: blur(6px);
border-top: 1px solid var(--line); border-top: 1px solid var(--line);
border-radius: 0 0 18px 18px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@@ -475,10 +573,22 @@ dialog label {
margin-bottom: 10px; margin-bottom: 10px;
} }
} }
@media (max-width: 1000px) {
.howto {
grid-template-columns: 1fr;
}
}
@media (max-width: 620px) { @media (max-width: 620px) {
main { main {
padding: 25px 16px; padding: 25px 16px;
} }
.tool-text small {
display: none;
}
.savebar {
margin: 25px -18px -18px;
padding: 14px 18px;
}
.workspace { .workspace {
display: block; display: block;
} }
+1 -1
View File
@@ -1,5 +1,5 @@
name: ESP Screen Manager name: ESP Screen Manager
version: "0.2.12" version: "0.2.13"
slug: esp_screen_manager slug: esp_screen_manager
description: Kies tegels voor je CYD en Guition touchscreen, zonder opnieuw flashen. description: Kies tegels voor je CYD en Guition touchscreen, zonder opnieuw flashen.
url: https://github.com/MaxGramser/homeassistant_espscreen url: https://github.com/MaxGramser/homeassistant_espscreen
+1
View File
@@ -62,6 +62,7 @@ class SpecialTiles(unittest.TestCase):
layout=validate_layout({'title':'T','tiles':[{'entity':'screen.clock','options':{'display':'analog','size':'wide'}},{'entity':'sun.sun'},{'entity':'timer.egg'},{'entity':'person.max'}, layout=validate_layout({'title':'T','tiles':[{'entity':'screen.clock','options':{'display':'analog','size':'wide'}},{'entity':'sun.sun'},{'entity':'timer.egg'},{'entity':'person.max'},
{'entity':'weather.home','options':{'display':'forecast'}},{'entity':'sensor.t','options':{'display':'graph','size':'wide'}}]}) {'entity':'weather.home','options':{'display':'forecast'}},{'entity':'sensor.t','options':{'display':'graph','size':'wide'}}]})
self.assertEqual(layout['tiles'][4]['options'],{'display':'forecast','size':'wide'},'forecast forces a wide card') self.assertEqual(layout['tiles'][4]['options'],{'display':'forecast','size':'wide'},'forecast forces a wide card')
self.assertEqual(validate_layout({'title':'T','tiles':[{'entity':'sun.sun','options':{'display':'sunpath'}}]})['tiles'][0]['options'],{'display':'sunpath','size':'wide'})
self.assertEqual(layout['tiles'][0]['options']['display'],'analog') self.assertEqual(layout['tiles'][0]['options']['display'],'analog')
for tile in [{'entity':'screen.other'},{'entity':'light.a','options':{'size':'double'}},{'entity':'light.a','options':{'display':'graph'}}, for tile in [{'entity':'screen.other'},{'entity':'light.a','options':{'size':'double'}},{'entity':'light.a','options':{'display':'graph'}},
{'entity':'sensor.a','options':{'display':'forecast'}},{'entity':'screen.clock','options':{'display':'watch'}}]: {'entity':'sensor.a','options':{'display':'forecast'}},{'entity':'screen.clock','options':{'display':'watch'}}]: