Improve vacuum controls, slider gestures and per-tile settings discovery
This commit is contained in:
@@ -27,7 +27,12 @@ class TouchGuard {
|
|||||||
if (std::abs(x - start_x_) > 18 || std::abs(y - start_y_) > 18) moved_ = true;
|
if (std::abs(x - start_x_) > 18 || std::abs(y - start_y_) > 18) moved_ = true;
|
||||||
}
|
}
|
||||||
bool accept(uint32_t now, int tile) {
|
bool accept(uint32_t now, int tile) {
|
||||||
if (accepted_ || moved_ || now - started_ < 60) return false;
|
return !moved_ && accept_slider(now, tile);
|
||||||
|
}
|
||||||
|
// Only for a slider which captured this contact and did not lose the press.
|
||||||
|
// Consume the gesture so its parent can never also turn it into a tile tap.
|
||||||
|
bool accept_slider(uint32_t now, int tile) {
|
||||||
|
if (accepted_ || now - started_ < 60) return false;
|
||||||
if (has_previous_ && tile == previous_tile_ && now - previous_ < 600) return false;
|
if (has_previous_ && tile == previous_tile_ && now - previous_ < 600) return false;
|
||||||
accepted_ = true;
|
accepted_ = true;
|
||||||
has_previous_ = true;
|
has_previous_ = true;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ inline void preview(Row &row) {
|
|||||||
int value = lv_slider_get_value(row.slider);
|
int value = lv_slider_get_value(row.slider);
|
||||||
if (row.index == 0) {
|
if (row.index == 0) {
|
||||||
lv_label_set_text_fmt(row.value, "%d°", value);
|
lv_label_set_text_fmt(row.value, "%d°", value);
|
||||||
lv_obj_set_style_bg_color(row.slider, lv_color_hsv_to_rgb(value % 360, 100, 100), LV_PART_KNOB);
|
lv_obj_set_style_bg_color(row.slider, lv_color_hex(0x111111), LV_PART_KNOB);
|
||||||
} else if (row.index == 1) {
|
} else if (row.index == 1) {
|
||||||
lv_label_set_text_fmt(row.value, "%d K", value);
|
lv_label_set_text_fmt(row.value, "%d K", value);
|
||||||
} else lv_label_set_text_fmt(row.value, "%d %%", value);
|
} else lv_label_set_text_fmt(row.value, "%d %%", value);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ struct Tile {
|
|||||||
std::string entity, name, state, unit, modes, hvac_modes;
|
std::string entity, name, state, unit, modes, hvac_modes;
|
||||||
std::array<std::string, 4> fan_speeds;
|
std::array<std::string, 4> fan_speeds;
|
||||||
unsigned fan_speed_count = 0;
|
unsigned fan_speed_count = 0;
|
||||||
|
std::string fan_speed;
|
||||||
float brightness = NAN, percentage = NAN, position = NAN;
|
float brightness = NAN, percentage = NAN, position = NAN;
|
||||||
float current = NAN, target = NAN, humidity = NAN, minimum = 7, maximum = 35, step = 0.5f;
|
float current = NAN, target = NAN, humidity = NAN, minimum = 7, maximum = 35, step = 0.5f;
|
||||||
int hue = 0, kelvin = 3000, min_kelvin = 0, max_kelvin = 0;
|
int hue = 0, kelvin = 3000, min_kelvin = 0, max_kelvin = 0;
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ inline std::string receive(const std::string &payload) {
|
|||||||
if (std::isfinite(kelvin)) tile.kelvin = std::lround(std::clamp(kelvin, 1000.0f, 15000.0f));
|
if (std::isfinite(kelvin)) tile.kelvin = std::lround(std::clamp(kelvin, 1000.0f, 15000.0f));
|
||||||
tile.min_kelvin = std::clamp(number(a["min_color_temp_kelvin"], 0), 0.0f, 15000.0f);
|
tile.min_kelvin = std::clamp(number(a["min_color_temp_kelvin"], 0), 0.0f, 15000.0f);
|
||||||
tile.max_kelvin = std::clamp(number(a["max_color_temp_kelvin"], 0), 0.0f, 15000.0f);
|
tile.max_kelvin = std::clamp(number(a["max_color_temp_kelvin"], 0), 0.0f, 15000.0f);
|
||||||
|
tile.fan_speed = string(a["fan_speed"], 48);
|
||||||
tile.fan_speed_count = 0;
|
tile.fan_speed_count = 0;
|
||||||
if (a["fan_speed_list"].is<JsonArray>()) for (JsonVariant speed : a["fan_speed_list"].as<JsonArray>()) {
|
if (a["fan_speed_list"].is<JsonArray>()) for (JsonVariant speed : a["fan_speed_list"].as<JsonArray>()) {
|
||||||
if (tile.fan_speed_count == 4) break;
|
if (tile.fan_speed_count == 4) break;
|
||||||
@@ -193,6 +194,9 @@ namespace runtime_tiles {
|
|||||||
inline lv_obj_t *detail_root=nullptr;
|
inline lv_obj_t *detail_root=nullptr;
|
||||||
inline unsigned detail_index=0;
|
inline unsigned detail_index=0;
|
||||||
inline const lv_font_t *detail_font=nullptr;
|
inline const lv_font_t *detail_font=nullptr;
|
||||||
|
inline lv_obj_t *detail_actions[16]{};
|
||||||
|
inline unsigned detail_action_count=0;
|
||||||
|
inline lv_obj_t *detail_status=nullptr;
|
||||||
inline lv_obj_t *detail_label(lv_obj_t *parent,const std::string &text,int x,int y,int width) {
|
inline lv_obj_t *detail_label(lv_obj_t *parent,const std::string &text,int x,int y,int width) {
|
||||||
auto *label=lv_label_create(parent);lv_label_set_text(label,text.c_str());lv_obj_set_pos(label,x,y);lv_obj_set_width(label,width);
|
auto *label=lv_label_create(parent);lv_label_set_text(label,text.c_str());lv_obj_set_pos(label,x,y);lv_obj_set_width(label,width);
|
||||||
lv_obj_set_style_text_font(label,detail_font,0);lv_obj_set_style_text_color(label,lv_color_hex(light_theme?0x202020:0xFFFFFF),0);
|
lv_obj_set_style_text_font(label,detail_font,0);lv_obj_set_style_text_color(label,lv_color_hex(light_theme?0x202020:0xFFFFFF),0);
|
||||||
@@ -208,6 +212,9 @@ inline int slider_value(const Tile &t){
|
|||||||
if(d=="number"||d=="input_number") {char *end;float state=strtof(t.state.c_str(),&end);if(end!=t.state.c_str() && t.maximum>t.minimum)value=(state-t.minimum)/(t.maximum-t.minimum);}
|
if(d=="number"||d=="input_number") {char *end;float state=strtof(t.state.c_str(),&end);if(end!=t.state.c_str() && t.maximum>t.minimum)value=(state-t.minimum)/(t.maximum-t.minimum);}
|
||||||
return std::clamp((int)std::lround(value*1000),0,1000);
|
return std::clamp((int)std::lround(value*1000),0,1000);
|
||||||
}
|
}
|
||||||
|
inline lv_obj_t *captured_slider=nullptr;
|
||||||
|
inline bool slider_changed=false;
|
||||||
|
inline void slider_event(lv_event_t *e);
|
||||||
inline void commit_slider(unsigned i,int raw){
|
inline void commit_slider(unsigned i,int raw){
|
||||||
if(i>=model.count || !fresh())return;auto &t=model.tiles[i];if(!t.available() || t.loading(esphome::millis()))return;
|
if(i>=model.count || !fresh())return;auto &t=model.tiles[i];if(!t.available() || t.loading(esphome::millis()))return;
|
||||||
float value=std::clamp(raw,0,1000)/1000.0f;auto d=t.domain();
|
float value=std::clamp(raw,0,1000)/1000.0f;auto d=t.domain();
|
||||||
@@ -220,10 +227,20 @@ inline void commit_slider(unsigned i,int raw){
|
|||||||
value=std::clamp(t.minimum+std::round(value*(t.maximum-t.minimum)/t.step)*t.step,t.minimum,t.maximum);
|
value=std::clamp(t.minimum+std::round(value*(t.maximum-t.minimum)/t.step)*t.step,t.minimum,t.maximum);
|
||||||
action(d+".set_value",t.entity,"value",std::to_string(value)); }
|
action(d+".set_value",t.entity,"value",std::to_string(value)); }
|
||||||
}
|
}
|
||||||
inline void detail_button(const char *text,int x,int y,int width,int height,int command){
|
inline void slider_event(lv_event_t *e){
|
||||||
|
auto *slider=lv_event_get_target_obj(e);auto code=lv_event_get_code(e);
|
||||||
|
if(code==LV_EVENT_PRESSED){captured_slider=slider;slider_changed=false;}
|
||||||
|
if(code==LV_EVENT_VALUE_CHANGED && captured_slider==slider)slider_changed=true;
|
||||||
|
if(code==LV_EVENT_PRESS_LOST && captured_slider==slider){captured_slider=nullptr;slider_changed=false;}
|
||||||
|
if(code==LV_EVENT_RELEASED && captured_slider==slider){
|
||||||
|
unsigned index=(uintptr_t)lv_event_get_user_data(e);bool changed=slider_changed;captured_slider=nullptr;slider_changed=false;
|
||||||
|
if(changed && cyd::touch_guard.accept_slider(esphome::millis(),200+index))commit_slider(index,lv_slider_get_value(slider));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline lv_obj_t *detail_button(const char *text,int x,int y,int width,int height,int command){
|
||||||
auto *button=lv_obj_create(detail_root);lv_obj_remove_style_all(button);lv_obj_set_pos(button,x,y);lv_obj_set_size(button,width,height);
|
auto *button=lv_obj_create(detail_root);lv_obj_remove_style_all(button);lv_obj_set_pos(button,x,y);lv_obj_set_size(button,width,height);
|
||||||
lv_obj_set_style_bg_color(button,lv_color_hex(light_theme?(command==0?0x009FE3:0xD9E6F0):0x34495E),0);lv_obj_set_style_bg_opa(button,LV_OPA_COVER,0);lv_obj_set_style_radius(button,12,0);lv_obj_add_flag(button,LV_OBJ_FLAG_CLICKABLE);
|
lv_obj_set_style_bg_color(button,lv_color_hex(light_theme?(command==0?0x009FE3:0xD9E6F0):0x34495E),0);lv_obj_set_style_bg_opa(button,LV_OPA_COVER,0);lv_obj_set_style_radius(button,12,0);lv_obj_add_flag(button,LV_OBJ_FLAG_CLICKABLE);
|
||||||
auto *label=detail_label(button,text,6,0,width-12);lv_obj_center(label);if(command==0)lv_obj_set_style_text_color(label,lv_color_hex(0xFFFFFF),0);lv_obj_remove_flag(label,LV_OBJ_FLAG_CLICKABLE);
|
auto *label=detail_label(button,text,6,0,width-12);lv_obj_center(label);lv_obj_set_style_text_align(label,LV_TEXT_ALIGN_CENTER,0);if(command==0)lv_obj_set_style_text_color(label,lv_color_hex(0xFFFFFF),0);lv_obj_remove_flag(label,LV_OBJ_FLAG_CLICKABLE);
|
||||||
lv_obj_add_event_cb(button,[](lv_event_t *e){
|
lv_obj_add_event_cb(button,[](lv_event_t *e){
|
||||||
int cmd=(intptr_t)lv_event_get_user_data(e);if(cmd==-1){hide_detail();return;}
|
int cmd=(intptr_t)lv_event_get_user_data(e);if(cmd==-1){hide_detail();return;}
|
||||||
if(!fresh()||detail_index>=model.count || !cyd::touch_guard.accept(esphome::millis(),300+cmd))return;
|
if(!fresh()||detail_index>=model.count || !cyd::touch_guard.accept(esphome::millis(),300+cmd))return;
|
||||||
@@ -235,27 +252,69 @@ inline void detail_button(const char *text,int x,int y,int width,int height,int
|
|||||||
if(cmd==22)action("media_player.media_next_track",t.entity);
|
if(cmd==22)action("media_player.media_next_track",t.entity);
|
||||||
if(cmd>=30 && cmd<38 && cmd-30<(int)t.option_count)action(t.domain()+".select_option",t.entity,"option",t.options[cmd-30]);
|
if(cmd>=30 && cmd<38 && cmd-30<(int)t.option_count)action(t.domain()+".select_option",t.entity,"option",t.options[cmd-30]);
|
||||||
},LV_EVENT_SHORT_CLICKED,(void*)(intptr_t)command);
|
},LV_EVENT_SHORT_CLICKED,(void*)(intptr_t)command);
|
||||||
|
lv_obj_set_style_bg_color(button,lv_color_hex(0x0075B0),LV_STATE_PRESSED);
|
||||||
|
lv_obj_set_style_transform_width(button,-2,LV_STATE_PRESSED);lv_obj_set_style_transform_height(button,-2,LV_STATE_PRESSED);
|
||||||
|
lv_obj_set_style_opa(button,LV_OPA_50,LV_STATE_DISABLED);
|
||||||
|
if(command>=0 && detail_action_count<16)detail_actions[detail_action_count++]=button;
|
||||||
|
return button;
|
||||||
}
|
}
|
||||||
inline void show_detail(unsigned index){
|
inline void show_detail(unsigned index){
|
||||||
if(index>=model.count)return;detail_index=index;auto &t=model.tiles[index];
|
if(index>=model.count)return;detail_index=index;auto &t=model.tiles[index];
|
||||||
if(!detail_font)detail_font=lv_obj_get_style_text_font(widgets[index].title,LV_PART_MAIN);
|
if(!detail_font)detail_font=lv_obj_get_style_text_font(widgets[index].title,LV_PART_MAIN);
|
||||||
if(!detail_root){detail_root=lv_obj_create(lv_screen_active());lv_obj_remove_style_all(detail_root);lv_obj_set_size(detail_root,lv_pct(100),lv_pct(100));lv_obj_remove_flag(detail_root,LV_OBJ_FLAG_SCROLLABLE);}
|
if(!detail_root){detail_root=lv_obj_create(lv_screen_active());lv_obj_remove_style_all(detail_root);lv_obj_set_size(detail_root,lv_pct(100),lv_pct(100));lv_obj_remove_flag(detail_root,LV_OBJ_FLAG_SCROLLABLE);}
|
||||||
lv_obj_clean(detail_root);lv_obj_remove_flag(detail_root,LV_OBJ_FLAG_HIDDEN);lv_obj_move_foreground(detail_root);
|
detail_action_count=0;detail_status=nullptr;lv_obj_clean(detail_root);lv_obj_remove_flag(detail_root,LV_OBJ_FLAG_HIDDEN);lv_obj_move_foreground(detail_root);
|
||||||
lv_obj_set_style_bg_color(detail_root,lv_color_hex(light_theme?0xEFEFEF:0x202A38),0);lv_obj_set_style_bg_opa(detail_root,LV_OPA_COVER,0);
|
lv_obj_set_style_bg_color(detail_root,lv_color_hex(light_theme?0xEFEFEF:0x202A38),0);lv_obj_set_style_bg_opa(detail_root,LV_OPA_COVER,0);
|
||||||
int width=lv_display_get_horizontal_resolution(lv_display_get_default()), height=lv_display_get_vertical_resolution(lv_display_get_default());
|
int width=lv_display_get_horizontal_resolution(lv_display_get_default()), height=lv_display_get_vertical_resolution(lv_display_get_default());
|
||||||
bool large=width>=480;int pad=large?20:10, top=large?100:62, gap=large?12:6,bh=large?58:34,cw=(width-pad*2-gap)/2;
|
bool large=width>=480;int pad=large?20:10, top=large?100:62, gap=large?12:6,bh=large?58:34,cw=(width-pad*2-gap)/2;
|
||||||
auto *heading=detail_label(detail_root,t.name,pad,large?24:12,width-80);if(watch_font){lv_obj_set_style_text_font(heading,watch_font,0);lv_obj_set_height(heading,lv_font_get_line_height(watch_font));}
|
auto *heading=detail_label(detail_root,t.name,pad,large?24:12,width-80);if(watch_font){lv_obj_set_style_text_font(heading,watch_font,0);lv_obj_set_height(heading,lv_font_get_line_height(watch_font));}
|
||||||
detail_button("X",width-58,8,48,40,-1);
|
detail_button("X",width-58,8,48,40,-1);
|
||||||
std::string state=t.state=="docked"?"In dock":t.state=="cleaning"?"Bezig met schoonmaken":t.state=="paused"?"Gepauzeerd":t.state;
|
std::string state=t.state=="docked"?"In dock":t.state=="cleaning"?"Bezig met schoonmaken":t.state=="paused"?"Gepauzeerd":t.state;
|
||||||
detail_label(detail_root,state+(t.unit.empty()?"":" "+t.unit),pad,large?60:35,width-2*pad);
|
detail_status=detail_label(detail_root,state+(t.unit.empty()?"":" "+t.unit),pad,large?60:35,width-2*pad);
|
||||||
auto d=t.domain();
|
auto d=t.domain();
|
||||||
if(d=="vacuum"){
|
if(d=="vacuum"){
|
||||||
std::string summary=std::isfinite(t.battery)?"Accu "+std::to_string((int)t.battery)+"%":"Robotstofzuiger";
|
// Native shapes keep the robot crisp without image buffers or extra layers.
|
||||||
detail_label(detail_root,summary,pad,top,width-2*pad);top+=large?42:25;
|
auto shape=[&](lv_obj_t *parent,int x,int y,int w,int h,uint32_t color,int radius){
|
||||||
const char *names[]={"Start","Pauze","Naar dock","Zoeken"};
|
auto *o=lv_obj_create(parent);lv_obj_remove_style_all(o);lv_obj_set_pos(o,x,y);lv_obj_set_size(o,w,h);
|
||||||
for(int i=0;i<4;++i)detail_button(names[i],pad+(i%2)*(cw+gap),top+(i/2)*(bh+gap),cw,bh,i);
|
lv_obj_set_style_radius(o,radius,0);lv_obj_set_style_bg_color(o,lv_color_hex(color),0);lv_obj_set_style_bg_opa(o,LV_OPA_COVER,0);
|
||||||
top+=2*(bh+gap);
|
lv_obj_remove_flag(o,LV_OBJ_FLAG_CLICKABLE);lv_obj_remove_flag(o,LV_OBJ_FLAG_SCROLLABLE);return o;
|
||||||
for(unsigned i=0;i<t.fan_speed_count;++i)detail_button(t.fan_speeds[i].c_str(),pad+(i%2)*(cw+gap),top+(i/2)*(large?42:24),cw,large?36:22,10+i);
|
};
|
||||||
|
uint32_t surface=light_theme?0xFFFFFF:0x2B3B4D, muted=light_theme?0xEAF5FC:0x344D63;
|
||||||
|
if(large){
|
||||||
|
auto *hero=shape(detail_root,pad,100,width-2*pad,148,surface,24);
|
||||||
|
shape(hero,18,14,120,120,muted,60);
|
||||||
|
auto *robot=shape(hero,35,27,86,86,0xFFFFFF,43);
|
||||||
|
lv_obj_set_style_border_width(robot,2,0);lv_obj_set_style_border_color(robot,lv_color_hex(0xCEDDE6),0);
|
||||||
|
shape(robot,27,10,30,30,0xE3EBEF,15);shape(robot,35,18,14,14,0xA8BCC8,7);
|
||||||
|
shape(robot,29,59,26,5,0x00A6ED,3);
|
||||||
|
detail_label(hero,t.state=="cleaning"?"Aan het werk":"Klaar voor je huis",154,24,260);
|
||||||
|
auto *badge=shape(hero,154,59,240,32,muted,16);
|
||||||
|
auto *status=detail_label(badge,t.loading(esphome::millis())?"Opdracht verstuurd...":state,12,5,218);
|
||||||
|
lv_obj_set_style_text_color(status,lv_color_hex(light_theme?0x087BA8:0xFFFFFF),0);
|
||||||
|
detail_label(hero,std::isfinite(t.battery)?"Batterij "+std::to_string((int)t.battery)+"%":"Verbonden via Home Assistant",154,107,260);
|
||||||
|
detail_button(t.state=="cleaning"?"Pauzeer schoonmaken":"Start schoonmaken",pad,260,width-2*pad,58,t.state=="cleaning"?1:0);
|
||||||
|
detail_button("Terug naar dock",pad,330,cw,48,2);
|
||||||
|
detail_button("Vind mijn robot",pad+cw+gap,330,cw,48,3);
|
||||||
|
detail_label(detail_root,"Zuigkracht",pad,394,width-2*pad);
|
||||||
|
top=424;
|
||||||
|
}else{
|
||||||
|
auto *robot=shape(detail_root,pad,64,46,46,muted,23);
|
||||||
|
shape(robot,16,8,14,14,0xA8BCC8,7);shape(robot,15,32,16,3,0x00A6ED,2);
|
||||||
|
detail_label(detail_root,std::isfinite(t.battery)?"Batterij "+std::to_string((int)t.battery)+"%":"Robotstofzuiger",pad+58,66,width-2*pad-58);
|
||||||
|
detail_label(detail_root,"Kies een actie",pad+58,87,width-2*pad-58);
|
||||||
|
detail_button(t.state=="cleaning"?"Pauzeren":"Schoonmaken",pad,120,cw,38,t.state=="cleaning"?1:0);
|
||||||
|
detail_button("Naar dock",pad+cw+gap,120,cw,38,2);
|
||||||
|
detail_label(detail_root,"Zuigkracht",pad,168,width-2*pad);top=194;
|
||||||
|
}
|
||||||
|
if(!t.fan_speed_count)detail_label(detail_root,"Automatische zuigkracht",pad,top,width-2*pad);
|
||||||
|
int count=std::max(1,(int)t.fan_speed_count),sw=(width-2*pad-gap*(count-1))/count;
|
||||||
|
for(unsigned i=0;i<t.fan_speed_count;++i){
|
||||||
|
std::string name=t.fan_speeds[i];
|
||||||
|
if(name=="quiet")name="Stil";else if(name=="balanced")name="Normaal";else if(name=="turbo")name="Turbo";else if(name=="max")name="Max";
|
||||||
|
auto *button=detail_button(name.c_str(),pad+i*(sw+gap),top,sw,large?36:30,10+i);
|
||||||
|
bool selected=t.fan_speed==t.fan_speeds[i];
|
||||||
|
lv_obj_set_style_bg_color(button,lv_color_hex(selected?0x009FE3:surface),0);
|
||||||
|
if(selected)lv_obj_set_style_text_color(lv_obj_get_child(button,0),lv_color_hex(0xFFFFFF),0);
|
||||||
|
}
|
||||||
}else if(d=="sensor"){
|
}else if(d=="sensor"){
|
||||||
float minimum=INFINITY,maximum=-INFINITY;for(float value:t.history)if(t.has_history&&std::isfinite(value)){minimum=std::min(minimum,value);maximum=std::max(maximum,value);}
|
float minimum=INFINITY,maximum=-INFINITY;for(float value:t.history)if(t.has_history&&std::isfinite(value)){minimum=std::min(minimum,value);maximum=std::max(maximum,value);}
|
||||||
if(!std::isfinite(minimum)){detail_label(detail_root,"Geen numerieke HA-historie",pad,top,width-2*pad);return;}
|
if(!std::isfinite(minimum)){detail_label(detail_root,"Geen numerieke HA-historie",pad,top,width-2*pad);return;}
|
||||||
@@ -263,7 +322,8 @@ inline void show_detail(unsigned index){
|
|||||||
int chart_y=top+(large?46:28),chart_h=height-chart_y-30,bar_w=(width-pad*2)/24;
|
int chart_y=top+(large?46:28),chart_h=height-chart_y-30,bar_w=(width-pad*2)/24;
|
||||||
for(unsigned i=0;i<24;++i){if(!std::isfinite(t.history[i]))continue;int h=maximum>minimum?8+(chart_h-8)*(t.history[i]-minimum)/(maximum-minimum):chart_h/2;
|
for(unsigned i=0;i<24;++i){if(!std::isfinite(t.history[i]))continue;int h=maximum>minimum?8+(chart_h-8)*(t.history[i]-minimum)/(maximum-minimum):chart_h/2;
|
||||||
auto *bar=lv_obj_create(detail_root);lv_obj_remove_style_all(bar);lv_obj_set_pos(bar,pad+i*bar_w,chart_y+chart_h-h);lv_obj_set_size(bar,std::max(2,bar_w-2),h);lv_obj_set_style_bg_color(bar,lv_color_hex(0x16A5E6),0);lv_obj_set_style_bg_opa(bar,LV_OPA_COVER,0);}
|
auto *bar=lv_obj_create(detail_root);lv_obj_remove_style_all(bar);lv_obj_set_pos(bar,pad+i*bar_w,chart_y+chart_h-h);lv_obj_set_size(bar,std::max(2,bar_w-2),h);lv_obj_set_style_bg_color(bar,lv_color_hex(0x16A5E6),0);lv_obj_set_style_bg_opa(bar,LV_OPA_COVER,0);}
|
||||||
detail_label(detail_root,"Eerder Nu",pad,height-24,width-2*pad);
|
detail_label(detail_root,std::to_string(t.history_hours)+" uur geleden",pad,height-24,(width-2*pad)/2);
|
||||||
|
auto *now=detail_label(detail_root,"Nu",width/2,height-24,width/2-pad);lv_obj_set_style_text_align(now,LV_TEXT_ALIGN_RIGHT,0);
|
||||||
}else if(d=="select"||d=="input_select"){
|
}else if(d=="select"||d=="input_select"){
|
||||||
for(unsigned i=0;i<t.option_count;++i)detail_button(t.options[i].c_str(),pad+(i%2)*(cw+gap),top+(i/2)*(bh+gap),cw,bh,30+i);
|
for(unsigned i=0;i<t.option_count;++i)detail_button(t.options[i].c_str(),pad+(i%2)*(cw+gap),top+(i/2)*(bh+gap),cw,bh,30+i);
|
||||||
}else if(d=="number"||d=="input_number"||d=="media_player"){
|
}else if(d=="number"||d=="input_number"||d=="media_player"){
|
||||||
@@ -274,7 +334,7 @@ inline void show_detail(unsigned index){
|
|||||||
}
|
}
|
||||||
detail_label(detail_root,d=="media_player"?"Volume":"Waarde",pad,top,width-2*pad);
|
detail_label(detail_root,d=="media_player"?"Volume":"Waarde",pad,top,width-2*pad);
|
||||||
auto *slider=lv_slider_create(detail_root);lv_obj_set_pos(slider,pad+12,top+(large?52:34));lv_obj_set_size(slider,width-2*pad-24,large?24:16);lv_slider_set_range(slider,0,1000);lv_slider_set_value(slider,slider_value(t),LV_ANIM_OFF);lv_obj_set_style_bg_color(slider,lv_color_hex(0x111111),LV_PART_KNOB);
|
auto *slider=lv_slider_create(detail_root);lv_obj_set_pos(slider,pad+12,top+(large?52:34));lv_obj_set_size(slider,width-2*pad-24,large?24:16);lv_slider_set_range(slider,0,1000);lv_slider_set_value(slider,slider_value(t),LV_ANIM_OFF);lv_obj_set_style_bg_color(slider,lv_color_hex(0x111111),LV_PART_KNOB);
|
||||||
lv_obj_add_event_cb(slider,[](lv_event_t *e){if(cyd::touch_guard.accept(esphome::millis(),399))commit_slider(detail_index,lv_slider_get_value(lv_event_get_target_obj(e)));},LV_EVENT_RELEASED,nullptr);
|
lv_obj_add_event_cb(slider,slider_event,LV_EVENT_ALL,(void*)(uintptr_t)index);
|
||||||
}else if(d=="weather"){
|
}else if(d=="weather"){
|
||||||
char text[80];snprintf(text,sizeof(text),"%.1f %s",t.current,t.unit.c_str());auto *value=detail_label(detail_root,text,pad,top,width-2*pad);if(watch_font){lv_obj_set_style_text_font(value,watch_font,0);lv_obj_set_height(value,lv_font_get_line_height(watch_font));}
|
char text[80];snprintf(text,sizeof(text),"%.1f %s",t.current,t.unit.c_str());auto *value=detail_label(detail_root,text,pad,top,width-2*pad);if(watch_font){lv_obj_set_style_text_font(value,watch_font,0);lv_obj_set_height(value,lv_font_get_line_height(watch_font));}
|
||||||
}
|
}
|
||||||
@@ -335,10 +395,11 @@ inline void bind(size_t index, lv_obj_t *tile, lv_obj_t *title, lv_obj_t *value,
|
|||||||
lv_obj_set_height(value,lv_font_get_line_height(w.value_font));
|
lv_obj_set_height(value,lv_font_get_line_height(w.value_font));
|
||||||
lv_label_set_long_mode(title,LV_LABEL_LONG_DOT);lv_label_set_long_mode(value,LV_LABEL_LONG_DOT);
|
lv_label_set_long_mode(title,LV_LABEL_LONG_DOT);lv_label_set_long_mode(value,LV_LABEL_LONG_DOT);
|
||||||
w.progress=lv_obj_create(tile);lv_obj_remove_style_all(w.progress);lv_obj_set_size(w.progress,0,3);lv_obj_align(w.progress,LV_ALIGN_BOTTOM_LEFT,0,0);lv_obj_set_style_bg_color(w.progress,lv_color_hex(0x00A6ED),0);lv_obj_set_style_bg_opa(w.progress,LV_OPA_COVER,0);
|
w.progress=lv_obj_create(tile);lv_obj_remove_style_all(w.progress);lv_obj_set_size(w.progress,0,3);lv_obj_align(w.progress,LV_ALIGN_BOTTOM_LEFT,0,0);lv_obj_set_style_bg_color(w.progress,lv_color_hex(0x00A6ED),0);lv_obj_set_style_bg_opa(w.progress,LV_OPA_COVER,0);
|
||||||
w.slider=lv_slider_create(tile);lv_obj_set_size(w.slider,lv_obj_get_width(tile)-24,lv_obj_get_height(tile)>80?10:4);lv_obj_align(w.slider,LV_ALIGN_BOTTOM_MID,0,-1);lv_slider_set_range(w.slider,0,1000);
|
w.slider=lv_slider_create(tile);lv_obj_set_size(w.slider,lv_obj_get_width(tile)-24,lv_obj_get_height(tile)>80?28:10);lv_obj_align(w.slider,LV_ALIGN_BOTTOM_MID,0,-1);lv_slider_set_range(w.slider,0,1000);
|
||||||
lv_obj_set_style_bg_color(w.slider,lv_color_hex(0x111111),LV_PART_KNOB);lv_obj_set_style_pad_all(w.slider,3,LV_PART_KNOB);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFCE5B4),LV_PART_MAIN);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFB900),LV_PART_INDICATOR);
|
lv_obj_set_style_bg_color(w.slider,lv_color_hex(0x111111),LV_PART_KNOB);lv_obj_set_style_pad_hor(w.slider,lv_obj_get_height(tile)>80?-10:0,LV_PART_KNOB);lv_obj_set_style_pad_ver(w.slider,lv_obj_get_height(tile)>80?-5:1,LV_PART_KNOB);lv_obj_set_style_radius(w.slider,14,LV_PART_MAIN);lv_obj_set_style_radius(w.slider,14,LV_PART_INDICATOR);lv_obj_set_style_radius(w.slider,3,LV_PART_KNOB);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFCE5B4),LV_PART_MAIN);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFB900),LV_PART_INDICATOR);
|
||||||
|
lv_obj_set_style_opa(w.slider,LV_OPA_TRANSP,LV_PART_KNOB);
|
||||||
lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN);
|
||||||
lv_obj_add_event_cb(w.slider,[](lv_event_t *e){auto &w=*static_cast<Widgets*>(lv_event_get_user_data(e));if(!fresh() || !cyd::touch_guard.accept(esphome::millis(),200+w.index))return;commit_slider(w.index,lv_slider_get_value(w.slider));},LV_EVENT_RELEASED,&w);
|
lv_obj_add_event_cb(w.slider,slider_event,LV_EVENT_ALL,(void*)(uintptr_t)index);
|
||||||
lv_obj_add_event_cb(tile, event, LV_EVENT_SHORT_CLICKED, &widgets[index]);
|
lv_obj_add_event_cb(tile, event, LV_EVENT_SHORT_CLICKED, &widgets[index]);
|
||||||
lv_obj_add_event_cb(tile, event, LV_EVENT_LONG_PRESSED, &widgets[index]);
|
lv_obj_add_event_cb(tile, event, LV_EVENT_LONG_PRESSED, &widgets[index]);
|
||||||
}
|
}
|
||||||
@@ -375,6 +436,9 @@ inline void render(lv_obj_t *room) {
|
|||||||
if(watch)lv_obj_add_flag(w.circle,LV_OBJ_FLAG_HIDDEN);else lv_obj_remove_flag(w.circle,LV_OBJ_FLAG_HIDDEN);
|
if(watch)lv_obj_add_flag(w.circle,LV_OBJ_FLAG_HIDDEN);else lv_obj_remove_flag(w.circle,LV_OBJ_FLAG_HIDDEN);
|
||||||
lv_obj_set_x(w.title,watch?0:w.title_x);
|
lv_obj_set_x(w.title,watch?0:w.title_x);
|
||||||
bool mini=t.inline_control=="slider" && !watch && t.available();
|
bool mini=t.inline_control=="slider" && !watch && t.available();
|
||||||
|
bool large_tile=lv_obj_get_height(w.tile)>80;
|
||||||
|
lv_obj_set_y(w.title,mini?0:w.title_y);
|
||||||
|
if(mini){lv_obj_set_y(w.value,large_tile?24:14);lv_obj_add_flag(w.circle,LV_OBJ_FLAG_HIDDEN);lv_obj_set_x(w.title,0);lv_obj_set_x(w.value,0);}
|
||||||
if(mini){lv_obj_remove_flag(w.slider,LV_OBJ_FLAG_HIDDEN);if(!lv_obj_has_state(w.slider,LV_STATE_PRESSED))lv_slider_set_value(w.slider,slider_value(t),LV_ANIM_OFF);}
|
if(mini){lv_obj_remove_flag(w.slider,LV_OBJ_FLAG_HIDDEN);if(!lv_obj_has_state(w.slider,LV_STATE_PRESSED))lv_slider_set_value(w.slider,slider_value(t),LV_ANIM_OFF);}
|
||||||
else lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN);
|
else lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN);
|
||||||
bool on = fresh() && t.active();
|
bool on = fresh() && t.active();
|
||||||
@@ -392,6 +456,12 @@ inline void render(lv_obj_t *room) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline void tick() {
|
inline void tick() {
|
||||||
|
if(detail_root && !lv_obj_has_flag(detail_root,LV_OBJ_FLAG_HIDDEN) && detail_index<model.count){
|
||||||
|
auto &t=model.tiles[detail_index];bool waiting=t.loading(esphome::millis())&&!t.local_feedback;
|
||||||
|
for(unsigned i=0;i<detail_action_count;++i){if(waiting||!fresh()||!t.available())lv_obj_add_state(detail_actions[i],LV_STATE_DISABLED);else lv_obj_remove_state(detail_actions[i],LV_STATE_DISABLED);}
|
||||||
|
if(waiting && detail_status)lv_label_set_text(detail_status,t.confirmed?"Bevestigd door Home Assistant":"Opdracht verstuurd...");
|
||||||
|
else if(detail_status){std::string state=t.state=="docked"?"In dock":t.state=="cleaning"?"Bezig met schoonmaken":t.state=="paused"?"Gepauzeerd":t.state;lv_label_set_text(detail_status,(state+(t.unit.empty()?"":" "+t.unit)).c_str());}
|
||||||
|
}
|
||||||
if(!enabled)return;
|
if(!enabled)return;
|
||||||
bool redraw=false;
|
bool redraw=false;
|
||||||
for(auto &t:model.tiles)if(t.pending){redraw=true;if(!t.loading(esphome::millis()))t.pending=false;}
|
for(auto &t:model.tiles)if(t.pending){redraw=true;if(!t.loading(esphome::millis()))t.pending=false;}
|
||||||
|
|||||||
@@ -69,3 +69,27 @@ staat in permanente appdata. Een browser sluiten stopt de build niet.
|
|||||||
De bestaande ESPHome Device Builder mag blijven staan, maar is voor deze route
|
De bestaande ESPHome Device Builder mag blijven staan, maar is voor deze route
|
||||||
niet nodig. Werk niet gelijktijdig vanuit twee builders aan hetzelfde profiel.
|
niet nodig. Werk niet gelijktijdig vanuit twee builders aan hetzelfde profiel.
|
||||||
Bestaande eigen YAML blijft leidend; een nieuwe YAML aanmaken is geen updateroute.
|
Bestaande eigen YAML blijft leidend; een nieuwe YAML aanmaken is geen updateroute.
|
||||||
|
|
||||||
|
## Verfijningen in 0.2.1
|
||||||
|
|
||||||
|
Bovenaan je geselecteerde scherm staan **Tegels instellen**, **Algemene
|
||||||
|
instellingen** en **Inspector**. Open bij een tegel **Bediening & weergave
|
||||||
|
instellen**. Kies bijvoorbeeld **Kleine slider op deze tegel? → Ja, direct
|
||||||
|
bedienen**, de klikactie of een grote waarde. Grote waarde en kleine slider zijn
|
||||||
|
alternatieven; de keuzelijsten werken elkaar direct bij. Klik daarna op
|
||||||
|
**Opslaan & naar scherm**. Hiervoor hoef je niet opnieuw te flashen.
|
||||||
|
|
||||||
|
**Inspecteer deze tegel** toont de actuele HA-status, ondersteunde eigenschappen
|
||||||
|
en ingestelde weergave. De algemene Inspector toont het hele scherm. Deze knoppen
|
||||||
|
bedienen geen apparaten.
|
||||||
|
|
||||||
|
Firmware 0.2.1 corrigeert het afwijzen van schuifgebaren in de kleine slider.
|
||||||
|
Alleen een door de slider vastgehouden aanraking mag schuiven; verloren contacten,
|
||||||
|
ruis en dubbele acties blijven geblokkeerd. De kleine slider heeft alleen een
|
||||||
|
gekleurde vulling; de grote sliders behouden hun zwarte handvat. Geschiedenis
|
||||||
|
vermeldt de periode expliciet, bijvoorbeeld **24 uur geleden → Nu**.
|
||||||
|
|
||||||
|
De vacuumkaart heeft een robotweergave, status, schoonmaak-/pauzeknop, dockknop en
|
||||||
|
zuigkrachtkeuze. Knoppen veranderen direct bij indrukken en worden tijdelijk
|
||||||
|
gedimd na een opdracht. Een ontvangen HA-statuswijziging is terugkoppeling, geen
|
||||||
|
garantie dat een fysieke schoonmaaktaak voltooid is.
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ esphome:
|
|||||||
min_version: 2026.6.2
|
min_version: 2026.6.2
|
||||||
project:
|
project:
|
||||||
name: maxgramser.esp_screen
|
name: maxgramser.esp_screen
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
platformio_options:
|
platformio_options:
|
||||||
build_flags:
|
build_flags:
|
||||||
- -DLV_USE_SNAPSHOT=1
|
- -DLV_USE_SNAPSHOT=1
|
||||||
|
|||||||
@@ -777,7 +777,7 @@ esphome:
|
|||||||
min_version: 2026.6.2
|
min_version: 2026.6.2
|
||||||
project:
|
project:
|
||||||
name: maxgramser.esp_screen
|
name: maxgramser.esp_screen
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
includes:
|
includes:
|
||||||
- cyd_ui.h
|
- cyd_ui.h
|
||||||
- light_controls.h
|
- light_controls.h
|
||||||
|
|||||||
+1
-1
@@ -781,7 +781,7 @@ esphome:
|
|||||||
min_version: 2026.6.2
|
min_version: 2026.6.2
|
||||||
project:
|
project:
|
||||||
name: maxgramser.esp_screen
|
name: maxgramser.esp_screen
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
includes:
|
includes:
|
||||||
- <esphome/components/smart_display/cyd_ui.h>
|
- <esphome/components/smart_display/cyd_ui.h>
|
||||||
- <esphome/components/smart_display/light_controls.h>
|
- <esphome/components/smart_display/light_controls.h>
|
||||||
|
|||||||
@@ -586,7 +586,7 @@ esphome:
|
|||||||
min_version: 2026.6.2
|
min_version: 2026.6.2
|
||||||
project:
|
project:
|
||||||
name: maxgramser.esp_screen
|
name: maxgramser.esp_screen
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
platformio_options:
|
platformio_options:
|
||||||
build_flags:
|
build_flags:
|
||||||
- -DLV_USE_SNAPSHOT=1
|
- -DLV_USE_SNAPSHOT=1
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
- Algemene instellingen en Inspector direct bovenaan bereikbaar.
|
||||||
|
- Tegel selecteren: klikactie, grote waarde, kleine slider (ja/nee) en historieperiode.
|
||||||
|
- Inspectie per tegel met actuele HA-status en eigenschappen.
|
||||||
|
- Firmware: schuifgebaren correct verwerken, brede mini-slider zonder handvat, concrete historie-as.
|
||||||
|
- Nieuwe vacuumkaart met robotweergave, actieve zuigkracht en druk-/opdrachtfeedback.
|
||||||
|
|
||||||
# 0.2.0
|
# 0.2.0
|
||||||
|
|
||||||
- ESPHome CLI in de app: bestaande profielen controleren, bouwen en via USB/OTA installeren.
|
- ESPHome CLI in de app: bestaande profielen controleren, bouwen en via USB/OTA installeren.
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ let inventory = { screens: [], entities: [] },
|
|||||||
dirty = false,
|
dirty = false,
|
||||||
filter = "",
|
filter = "",
|
||||||
dragIndex = -1,
|
dragIndex = -1,
|
||||||
busy = false;
|
busy = false,
|
||||||
|
selectedTile = null;
|
||||||
const node = (tag, text, cls) => {
|
const node = (tag, text, cls) => {
|
||||||
const n = document.createElement(tag);
|
const n = document.createElement(tag);
|
||||||
if (text !== undefined) n.textContent = text;
|
if (text !== undefined) n.textContent = text;
|
||||||
@@ -256,7 +257,22 @@ function renderTiles() {
|
|||||||
actions.append(b);
|
actions.append(b);
|
||||||
}
|
}
|
||||||
const options = node("details", undefined, "tile-options");
|
const options = node("details", undefined, "tile-options");
|
||||||
options.append(node("summary", "Tegelinstellingen"));
|
options.append(node("summary", "Bediening & weergave instellen"));
|
||||||
|
options.open = selectedTile === tile.entity;
|
||||||
|
options.ontoggle = () => {
|
||||||
|
li.classList.toggle("selected-tile", options.open);
|
||||||
|
if (options.open) {
|
||||||
|
selectedTile = tile.entity;
|
||||||
|
document.querySelectorAll(".tile-options").forEach((other) => {
|
||||||
|
if (other !== options) other.open = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
li.onclick = (e) => {
|
||||||
|
if (!e.target.closest("input, select, button, summary, .tile-options"))
|
||||||
|
options.open = !options.open;
|
||||||
|
};
|
||||||
|
const controls = {};
|
||||||
const domain = tile.entity.split(".")[0];
|
const domain = tile.entity.split(".")[0];
|
||||||
const fields = [
|
const fields = [
|
||||||
[
|
[
|
||||||
@@ -295,10 +311,10 @@ function renderTiles() {
|
|||||||
)
|
)
|
||||||
fields.push([
|
fields.push([
|
||||||
"inline",
|
"inline",
|
||||||
"Op de tegel",
|
"Kleine slider op deze tegel?",
|
||||||
[
|
[
|
||||||
["none", "Geen extra bediening"],
|
["none", "Nee"],
|
||||||
["slider", "Mini-schuif"],
|
["slider", "Ja, direct bedienen"],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
if (domain === "sensor")
|
if (domain === "sensor")
|
||||||
@@ -328,6 +344,7 @@ function renderTiles() {
|
|||||||
: key === "history_hours"
|
: key === "history_hours"
|
||||||
? 24
|
? 24
|
||||||
: "none");
|
: "none");
|
||||||
|
controls[key] = input;
|
||||||
input.onchange = () => {
|
input.onchange = () => {
|
||||||
tile.options = {
|
tile.options = {
|
||||||
...tile.options,
|
...tile.options,
|
||||||
@@ -337,11 +354,18 @@ function renderTiles() {
|
|||||||
tile.options.inline = "none";
|
tile.options.inline = "none";
|
||||||
if (key === "inline" && input.value === "slider")
|
if (key === "inline" && input.value === "slider")
|
||||||
tile.options.display = "standard";
|
tile.options.display = "standard";
|
||||||
|
for (const [field, control] of Object.entries(controls)) {
|
||||||
|
if (tile.options[field] !== undefined)
|
||||||
|
control.value = tile.options[field];
|
||||||
|
}
|
||||||
markDirty();
|
markDirty();
|
||||||
};
|
};
|
||||||
label.append(input);
|
label.append(input);
|
||||||
options.append(label);
|
options.append(label);
|
||||||
}
|
}
|
||||||
|
const inspectTile = node("button", "Inspecteer deze tegel", "quiet");
|
||||||
|
inspectTile.onclick = () => inspect(tile.entity);
|
||||||
|
options.append(inspectTile);
|
||||||
content.append(options);
|
content.append(options);
|
||||||
li.append(node("span", "⠿", "grip"), content, actions);
|
li.append(node("span", "⠿", "grip"), content, actions);
|
||||||
$("#tiles").append(li);
|
$("#tiles").append(li);
|
||||||
@@ -430,7 +454,7 @@ $("#save").onclick = async () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast(e.message);
|
toast(e.message);
|
||||||
} finally {
|
} finally {
|
||||||
busy = false;
|
((busy = false), (selectedTile = null));
|
||||||
$("#save").disabled = false;
|
$("#save").disabled = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -625,17 +649,50 @@ $("#create-profile").onclick = async () => {
|
|||||||
toast(e.message);
|
toast(e.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$("#inspect").onclick = async () => {
|
function openSection(id) {
|
||||||
|
const section = $(id);
|
||||||
|
if (section.tagName === "DETAILS") section.open = true;
|
||||||
|
section.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
}
|
||||||
|
$("#nav-tiles").onclick = () => openSection("#tile-section");
|
||||||
|
$("#nav-settings").onclick = () => openSection("#general-settings");
|
||||||
|
$("#nav-inspector").onclick = () => inspect();
|
||||||
|
async function inspect(entity) {
|
||||||
if (!selected) return;
|
if (!selected) return;
|
||||||
|
openSection("#inspector-section");
|
||||||
|
$("#inspection-summary").textContent = "Actuele HA-status ophalen...";
|
||||||
try {
|
try {
|
||||||
|
const data = await (
|
||||||
|
await api(`screens/${encodeURIComponent(selected)}/inspect`)
|
||||||
|
).json();
|
||||||
|
const tiles = entity
|
||||||
|
? data.tiles.filter((t) => t.entity === entity)
|
||||||
|
: data.tiles;
|
||||||
|
$("#inspection-summary").replaceChildren();
|
||||||
|
for (const tile of tiles) {
|
||||||
|
const card = node("article", undefined, "inspection-tile");
|
||||||
|
card.append(
|
||||||
|
node("strong", tile.entity),
|
||||||
|
node("p", `Status: ${tile.state}`),
|
||||||
|
);
|
||||||
|
const options =
|
||||||
|
layout.tiles.find((t) => t.entity === tile.entity)?.options || {};
|
||||||
|
card.append(
|
||||||
|
node(
|
||||||
|
"small",
|
||||||
|
`Kleine slider: ${options.inline === "slider" ? "ja" : "nee"} · Weergave: ${options.display === "watch" ? "grote waarde" : "standaard"}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$("#inspection-summary").append(card);
|
||||||
|
}
|
||||||
$("#inspection").textContent = JSON.stringify(
|
$("#inspection").textContent = JSON.stringify(
|
||||||
await (
|
entity ? tiles[0] : data,
|
||||||
await api(`screens/${encodeURIComponent(selected)}/inspect`)
|
|
||||||
).json(),
|
|
||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
$("#inspection-summary").textContent = e.message;
|
||||||
toast(e.message);
|
toast(e.message);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
$("#inspect").onclick = () => inspect();
|
||||||
|
|||||||
@@ -60,15 +60,22 @@
|
|||||||
maxlength="60"
|
maxlength="60"
|
||||||
placeholder="Bijvoorbeeld: Woonkamer"
|
placeholder="Bijvoorbeeld: Woonkamer"
|
||||||
/></label>
|
/></label>
|
||||||
<div class="columns">
|
<nav class="editor-nav" aria-label="Scherm beheren">
|
||||||
|
<button id="nav-tiles" class="quiet">Tegels instellen</button>
|
||||||
|
<button id="nav-settings" class="quiet">
|
||||||
|
Algemene instellingen
|
||||||
|
</button>
|
||||||
|
<button id="nav-inspector" class="quiet">Inspector</button>
|
||||||
|
</nav>
|
||||||
|
<div class="columns" id="tile-section">
|
||||||
<div class="tile-editor">
|
<div class="tile-editor">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>Je tegels</h3>
|
<h3>Je tegels</h3>
|
||||||
<span id="count">0 / 10</span>
|
<span id="count">0 / 10</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint">
|
<p class="hint">
|
||||||
Sleep om te ordenen of gebruik de pijltjes. Zes tegels per
|
Klik op een tegel om bediening en weergave te kiezen. Sleep om
|
||||||
pagina.
|
te ordenen. Zes tegels per pagina.
|
||||||
</p>
|
</p>
|
||||||
<ol id="tiles"></ol>
|
<ol id="tiles"></ol>
|
||||||
<p id="no-tiles" class="hint">
|
<p id="no-tiles" class="hint">
|
||||||
@@ -87,7 +94,7 @@
|
|||||||
<div id="results" aria-live="polite"></div>
|
<div id="results" aria-live="polite"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<details class="screen-settings" open>
|
<details class="screen-settings" id="general-settings" open>
|
||||||
<summary>
|
<summary>
|
||||||
Scherminstellingen
|
Scherminstellingen
|
||||||
<span class="hint">Helderheid, rustmomenten en weergave</span>
|
<span class="hint">Helderheid, rustmomenten en weergave</span>
|
||||||
@@ -100,7 +107,7 @@
|
|||||||
Gelijke begin- en eindtijd schakelen het nachtvenster uit.
|
Gelijke begin- en eindtijd schakelen het nachtvenster uit.
|
||||||
</p>
|
</p>
|
||||||
</details>
|
</details>
|
||||||
<details class="screen-settings">
|
<details class="screen-settings" id="inspector-section">
|
||||||
<summary>
|
<summary>
|
||||||
Inspector
|
Inspector
|
||||||
<span class="hint"
|
<span class="hint"
|
||||||
@@ -108,6 +115,7 @@
|
|||||||
>
|
>
|
||||||
</summary>
|
</summary>
|
||||||
<button id="inspect" class="quiet">Lees actuele gegevens</button>
|
<button id="inspect" class="quiet">Lees actuele gegevens</button>
|
||||||
|
<div id="inspection-summary"></div>
|
||||||
<pre id="inspection">Selecteer een scherm en lees de gegevens.</pre>
|
<pre id="inspection">Selecteer een scherm en lees de gegevens.</pre>
|
||||||
</details>
|
</details>
|
||||||
<footer class="savebar">
|
<footer class="savebar">
|
||||||
|
|||||||
@@ -655,3 +655,42 @@ pre {
|
|||||||
.tile .tile-options select {
|
.tile .tile-options select {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-nav {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 12px;
|
||||||
|
background: #eaf3fc;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
.editor-nav button {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #c7d9ea;
|
||||||
|
}
|
||||||
|
.tile.selected-tile {
|
||||||
|
border-color: #009fe3;
|
||||||
|
background: #f0f9ff;
|
||||||
|
}
|
||||||
|
.tile-options summary {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #007fb6;
|
||||||
|
font-weight: 650;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
.tile-options > button {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
.inspection-tile {
|
||||||
|
padding: 14px;
|
||||||
|
margin: 10px 0;
|
||||||
|
border: 1px solid #d8e3ed;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
#general-settings,
|
||||||
|
#inspector-section,
|
||||||
|
#tile-section {
|
||||||
|
scroll-margin-top: 16px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: ESP Screen Manager
|
name: ESP Screen Manager
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
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
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ int main() {
|
|||||||
moving.update(50, 80);
|
moving.update(50, 80);
|
||||||
moving.update(50, 50);
|
moving.update(50, 50);
|
||||||
assert(!moving.accept(1200, 2)); // excursion stays cancelled, even on return
|
assert(!moving.accept(1200, 2)); // excursion stays cancelled, even on return
|
||||||
|
assert(moving.accept_slider(1200, 202)); // captured drag is a valid slider gesture
|
||||||
|
assert(!moving.accept(1201, 2)); // no parent action after slider release
|
||||||
|
assert(!moving.accept_slider(1202, 202)); // one send per contact
|
||||||
|
moving.begin(1250);
|
||||||
|
assert(!moving.accept_slider(1260, 202)); // noise remains rejected
|
||||||
|
assert(!moving.accept_slider(1350, 202)); // same-control bounce remains rejected
|
||||||
cyd::TouchGuard rollover;
|
cyd::TouchGuard rollover;
|
||||||
rollover.begin(std::numeric_limits<uint32_t>::max() - 30);
|
rollover.begin(std::numeric_limits<uint32_t>::max() - 30);
|
||||||
assert(rollover.accept(50, 1)); // millis wraps after 49 days
|
assert(rollover.accept(50, 1)); // millis wraps after 49 days
|
||||||
|
|||||||
Reference in New Issue
Block a user