Skip to content

Commit b63e7d3

Browse files
committed
Restore LVGL schema parity & tests
Restore legacy LVGL schema parity and add tests + tooling to detect regressions. Updated multiple LVGL plugins (button, chart, keyboard, meter, qrcode, spinner, textarea, progress_bar) to accept legacy aliases and unified props (e.g. color/text_color, textarea_id/textarea, placeholder/placeholder_text, spin_time/time, size, scale_width, checkable) and to preserve equivalent export behavior. Enhanced extraction and diff tooling to parse legacy renderer branches, handle generic root keys, property aliases, and ignore known regressions. Updated current_keys.json and parity_report.json to reflect parity, and added vitest tests for LVGL schema parity and progress_bar properties.
1 parent 1e99fb3 commit b63e7d3

File tree

14 files changed

+658
-386
lines changed

14 files changed

+658
-386
lines changed

custom_components/esphome_designer/frontend/features/lvgl_button/plugin.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,19 @@ import { getWeightsForFont, clampFontWeight } from '../../js/core/font_weights.j
77

88
const render = (el, widget, { getColorStyle }) => {
99
const props = widget.props || {};
10+
const textColor = props.color || props.text_color || "theme_auto";
1011
el.innerHTML = "";
1112
el.style.display = "flex";
1213
el.style.alignItems = "center";
1314
el.style.justifyContent = "center";
1415
el.style.boxSizing = "border-box";
1516
el.style.backgroundColor = getColorStyle(props.bg_color || "white");
16-
el.style.border = `${props.border_width || 2}px solid ${getColorStyle(props.text_color || "black")}`;
17+
el.style.border = `${props.border_width || 2}px solid ${getColorStyle(textColor)}`;
1718
el.style.borderRadius = `${props.radius || 5}px`;
1819

1920
const text = document.createElement("span");
2021
text.textContent = props.text || "BTN";
21-
text.style.color = getColorStyle(props.text_color || "black");
22+
text.style.color = getColorStyle(textColor);
2223
text.style.fontFamily = props.font_family || "Roboto, sans-serif";
2324
text.style.fontSize = (props.font_size || 14) + "px";
2425
text.style.fontWeight = props.font_weight || 400;
@@ -29,6 +30,7 @@ const render = (el, widget, { getColorStyle }) => {
2930

3031
const exportLVGL = (w, { common, convertColor, formatOpacity, _profile, getLVGLFont }) => {
3132
const p = w.props || {};
33+
const textColor = p.color || p.text_color;
3234

3335
// Robust entity ID detection: check top-level, props.entity_id, and props.entity
3436
const entityId = (w.entity_id || p.entity_id || p.entity || "").trim();
@@ -39,7 +41,7 @@ const exportLVGL = (w, { common, convertColor, formatOpacity, _profile, getLVGLF
3941
bg_color: convertColor(p.bg_color),
4042
bg_opa: "cover",
4143
border_width: p.border_width,
42-
border_color: convertColor(p.text_color),
44+
border_color: convertColor(textColor),
4345
radius: p.radius,
4446
opa: formatOpacity(p.opa),
4547
on_click: undefined,
@@ -49,7 +51,7 @@ const exportLVGL = (w, { common, convertColor, formatOpacity, _profile, getLVGLF
4951
align: "center",
5052
text: `"${p.text || 'BTN'}"`,
5153
text_font: getLVGLFont ? getLVGLFont(p.font_family, p.font_size, p.font_weight, p.italic) : undefined,
52-
text_color: convertColor(p.text_color)
54+
text_color: convertColor(textColor)
5355
}
5456
}
5557
]
@@ -83,9 +85,10 @@ export default {
8385
defaults: {
8486
text: "Button",
8587
bg_color: "theme_auto_inverse",
86-
text_color: "theme_auto",
88+
color: "theme_auto",
8789
border_width: 2,
8890
radius: 5,
91+
checkable: false,
8992
opa: 255,
9093
font_size: 14,
9194
font_family: "Roboto",
@@ -105,10 +108,11 @@ export default {
105108
{
106109
section: "Appearance",
107110
fields: [
108-
{ key: "text_color", label: "Text/Border Color", type: "color", default: "theme_auto" },
111+
{ key: "color", label: "Text/Border Color", type: "color", default: "theme_auto" },
109112
{ key: "bg_color", label: "Background color", type: "color", default: "theme_auto_inverse" },
110113
{ key: "border_width", label: "Border width", type: "number", default: 2 },
111114
{ key: "radius", label: "Corner Radius", type: "number", default: 5 },
115+
{ key: "checkable", label: "Checkable (Toggle)", type: "checkbox", default: false },
112116
{ key: "opa", label: "Opacity (0 - 255)", type: "number", default: 255 },
113117
{ key: "opacity", label: "Opacity (0 - 255)", type: "number", default: 255 }
114118
]

custom_components/esphome_designer/frontend/features/lvgl_chart/plugin.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export default {
131131
name: "Chart",
132132
category: "LVGL",
133133
defaults: {
134+
entity_id: "",
134135
min: 0,
135136
max: 100,
136137
color: "blue",
@@ -147,7 +148,8 @@ export default {
147148
{
148149
section: "Content",
149150
fields: [
150-
{ key: "title", label: "Chart Title", type: "text", default: "Chart" }
151+
{ key: "title", label: "Chart Title", type: "text", default: "Chart" },
152+
{ key: "entity_id", target: "root", label: "Entity ID", type: "entity_picker", default: "" }
151153
]
152154
},
153155
{

custom_components/esphome_designer/frontend/features/lvgl_keyboard/plugin.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ const render = (el, widget, { _getColorStyle }) => {
3434

3535
const exportLVGL = (w, { common, formatOpacity }) => {
3636
const p = w.props || {};
37+
const textareaId = w.textarea_id || p.textarea_id || w.textarea || p.textarea || "";
3738
return {
3839
keyboard: {
3940
...common,
4041
mode: p.mode || "TEXT_LOWER",
42+
textarea: textareaId || undefined,
4143
opa: formatOpacity(p.opa)
4244
}
4345
};
@@ -50,6 +52,7 @@ export default {
5052
defaults: {
5153
mode: "TEXT_LOWER",
5254
opa: 255,
55+
textarea_id: "",
5356
textarea: "",
5457
opacity: 255
5558
},
@@ -58,7 +61,7 @@ export default {
5861
section: "Keyboard Settings",
5962
fields: [
6063
{ key: "mode", label: "Initial Mode", type: "select", options: ["TEXT_LOWER", "TEXT_UPPER", "SPECIAL", "NUMBER", "USER_1", "USER_2", "USER_3", "USER_4"], default: "TEXT_LOWER" },
61-
{ key: "textarea", target: "root", label: "Target Textarea ID", type: "text", default: "" }
64+
{ key: "textarea_id", target: "root", label: "Target Textarea ID", type: "text", default: "" }
6265
]
6366
},
6467
{

custom_components/esphome_designer/frontend/features/lvgl_meter/plugin.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ export default {
192192
bg_color: "lightgray",
193193
color: "black",
194194
indicator_color: "red",
195+
scale_width: 10,
195196
opa: 255,
196197
entity_id: "",
197198
tick_count: 11,
@@ -220,6 +221,7 @@ export default {
220221
fields: [
221222
{ key: "color", label: "Tick/Label Color", type: "color", default: "black" },
222223
{ key: "bg_color", label: "Arc/Scale Color", type: "color", default: "lightgray" },
224+
{ key: "scale_width", label: "Scale Width", type: "number", default: 10 },
223225
{ key: "tick_count", label: "Ticks", type: "number", default: 11 },
224226
{ key: "tick_length", label: "Tick Length", type: "number", default: 10 },
225227
{ key: "label_gap", label: "Label Gap", type: "number", default: 10 }

custom_components/esphome_designer/frontend/features/lvgl_qrcode/plugin.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const render = (el, widget, { getColorStyle }) => {
4646
const exportLVGL = (w, { common, convertColor }) => {
4747
const p = w.props || {};
4848
let qrText = `"${p.text || 'https://github.com/koosoli/ESPHomeDesigner/'}"`;
49+
const size = p.size || Math.min(common.width, common.height);
4950

5051
if (w.entity_id) {
5152
const safeId = w.entity_id.replace(/[^a-zA-Z0-9_]/g, "_");
@@ -56,7 +57,7 @@ const exportLVGL = (w, { common, convertColor }) => {
5657
qrcode: {
5758
...common,
5859
text: qrText,
59-
size: Math.min(common.width, common.height),
60+
size,
6061
dark_color: convertColor(p.color),
6162
light_color: convertColor(p.bg_color || "white")
6263
}
@@ -90,6 +91,7 @@ export default {
9091
text: "https://github.com/koosoli/ESPHomeDesigner/",
9192
color: "black",
9293
bg_color: "white",
94+
size: 100,
9395
scale: 4,
9496
width: 130,
9597
height: 130,
@@ -108,6 +110,7 @@ export default {
108110
{
109111
section: "Appearance",
110112
fields: [
113+
{ key: "size", label: "Size (px)", type: "number", default: 100 },
111114
{ key: "color", label: "Foreground Color", type: "color", default: "black" },
112115
{ key: "bg_color", label: "Background Color", type: "color", default: "white" },
113116
{ key: "scale", label: "Pixel Scale", type: "number", default: 4 },

custom_components/esphome_designer/frontend/features/lvgl_spinner/plugin.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export default {
6363
name: "Spinner",
6464
category: "LVGL",
6565
defaults: {
66+
spin_time: 1000,
6667
time: 1000,
6768
arc_length: 60,
6869
arc_color: "blue",
@@ -74,7 +75,7 @@ export default {
7475
{
7576
section: "Settings",
7677
fields: [
77-
{ key: "time", label: "Cycle Time (ms)", type: "number", default: 1000 },
78+
{ key: "spin_time", label: "Spin Time (ms)", type: "number", default: 1000 },
7879
{ key: "arc_length", label: "Arc Length (deg)", type: "number", default: 60 }
7980
]
8081
},
@@ -91,11 +92,12 @@ export default {
9192
render,
9293
exportLVGL: (w, { common, convertColor }) => {
9394
const props = w.props || {};
95+
const spinTime = props.spin_time ?? props.time ?? 1000;
9496
return {
9597
"spinner": {
9698
...common,
9799
"arc_length": props.arc_length || 60,
98-
"spin_time": `${props.time || 1000}ms`,
100+
"spin_time": `${spinTime}ms`,
99101
"indicator": {
100102
"arc_color": convertColor(props.arc_color || "blue")
101103
},

custom_components/esphome_designer/frontend/features/lvgl_textarea/plugin.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
const render = (el, widget, { _getColorStyle }) => {
77
const props = widget.props || {};
88
const text = props.text || "";
9-
const placeholder = props.placeholder_text || "Enter text...";
9+
const placeholder = props.placeholder_text || props.placeholder || "Enter text...";
1010

1111
el.innerHTML = "";
1212
el.style.display = "flex";
@@ -53,7 +53,7 @@ const exportLVGL = (w, context) => {
5353
obj.type = "textarea";
5454
obj.attrs = {
5555
...obj.attrs,
56-
placeholder_text: props.placeholder,
56+
placeholder_text: props.placeholder_text ?? props.placeholder,
5757
text: props.text,
5858
max_length: props.max_length,
5959
one_line: props.one_line ?? false,
@@ -69,6 +69,7 @@ export default {
6969
category: "LVGL",
7070
defaults: {
7171
text: "",
72+
placeholder: "Enter text...",
7273
placeholder_text: "Enter text...",
7374
max_length: 128,
7475
accepted_chars: "",

custom_components/esphome_designer/frontend/features/progress_bar/plugin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,9 @@ export default {
355355

356356
panel.createSection("Labels", false);
357357
panel.addCheckbox("Display Title", props.show_label !== false, (v) => updateProp("show_label", v));
358+
panel.addLabeledInput("Title", "text", widget.title || "", (v) => {
359+
AppState.updateWidget(widget.id, { title: v });
360+
});
358361
panel.addCheckbox("Display Percentage", props.show_percentage !== false, (v) => updateProp("show_percentage", v));
359362
panel.addLabeledInput("Font Size", "number", props.font_size || 12, (v) => updateProp("font_size", parseInt(v, 10)));
360363
panel.addSelect("Text Align", props.text_align || "CENTER", ["LEFT", "CENTER", "RIGHT"], (v) => updateProp("text_align", v));

0 commit comments

Comments
 (0)