Skip to content

Commit 8a6bb0f

Browse files
committed
0.6.2
1 parent 3789c61 commit 8a6bb0f

File tree

4 files changed

+65
-14
lines changed

4 files changed

+65
-14
lines changed

custom_components/reterminal_dashboard/frontend/editor.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<aside class="sidebar">
2121
<div>
2222
<h1><span class="logo-dot"></span> reTerminal Designer <span
23-
style="font-size: 10px; color: var(--muted); vertical-align: middle;">v0.6.1</span></h1>
23+
style="font-size: 10px; color: var(--muted); vertical-align: middle;">v0.6.2</span></h1>
2424
<div style="margin-top: 4px; font-size: 10px;">
2525
<a href="https://github.com/koosoli/ReTerminalDesigner" target="_blank"
2626
style="color: var(--accent); text-decoration: none; display: flex; align-items: center; gap: 4px;">

custom_components/reterminal_dashboard/frontend/js/io/yaml_export.js

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,10 @@ function generateSnippetLocally() {
455455
// Adjust this value if text appears misaligned on the e-ink display
456456
const TEXT_Y_OFFSET = 0;
457457

458+
// Global offset for rectangle widgets to match device rendering
459+
// Negative value moves rectangles UP on the display
460+
const RECT_Y_OFFSET = -15;
461+
458462
// Helper to wrap widget rendering with conditional logic
459463
const wrapWithCondition = (lines, w, contentFn) => {
460464
const p = w.props || {};
@@ -1058,21 +1062,50 @@ function generateSnippetLocally() {
10581062
const entityId = (w.entity_id || "").trim();
10591063
const localSensorId = entityId.replace(/^sensor\./, "").replace(/\./g, "_").replace(/-/g, "_") || "none";
10601064

1065+
// Get line styling options
1066+
const lineType = (p.line_type || "SOLID").toUpperCase();
1067+
const lineThickness = parseInt(p.line_thickness || 3, 10);
1068+
const border = p.border !== false;
1069+
const continuous = !!p.continuous;
1070+
10611071
lines.push(` - id: ${safeId}`);
1062-
lines.push(` sensor: ${localSensorId}`);
10631072
lines.push(` duration: ${duration}`);
10641073
lines.push(` width: ${width}`);
10651074
lines.push(` height: ${height}`);
1075+
lines.push(` border: ${border}`);
10661076

10671077
// Grid configuration (only output if grid is enabled)
10681078
if (gridEnabled && xGrid) lines.push(` x_grid: ${xGrid}`);
10691079
if (gridEnabled && yGrid) lines.push(` y_grid: ${yGrid}`);
10701080

1081+
// Traces section (required for ESPHome graph component)
1082+
// This defines the actual data line(s) to be drawn
1083+
lines.push(` traces:`);
1084+
lines.push(` - sensor: ${localSensorId}`);
1085+
// ALWAYS output line_thickness - ESPHome default is 1px which is invisible on e-paper
1086+
// Our designer default is 3px which provides good visibility
1087+
lines.push(` line_thickness: ${lineThickness}`);
1088+
if (lineType !== "SOLID") {
1089+
lines.push(` line_type: ${lineType}`);
1090+
}
1091+
if (continuous) {
1092+
lines.push(` continuous: true`);
1093+
}
1094+
1095+
// Min/Max value configuration (required for Y-axis scaling)
1096+
// These go after traces in ESPHome YAML
1097+
const minValue = p.min_value;
1098+
const maxValue = p.max_value;
1099+
if (minValue !== undefined && minValue !== null && String(minValue).trim() !== "") {
1100+
lines.push(` min_value: ${minValue}`);
1101+
}
1102+
if (maxValue !== undefined && maxValue !== null && String(maxValue).trim() !== "") {
1103+
lines.push(` max_value: ${maxValue}`);
1104+
}
1105+
10711106
// Range configuration
10721107
if (maxRange !== null) lines.push(` max_range: ${maxRange}`);
10731108
if (minRange !== null) lines.push(` min_range: ${minRange}`);
1074-
1075-
// Color is handled in lambda draw call
10761109
});
10771110
lines.push("");
10781111
}
@@ -1605,6 +1638,7 @@ function generateSnippetLocally() {
16051638
const fill = p.fill;
16061639
const opacity = parseInt(p.opacity || 100, 10);
16071640
const isGray = colorProp.toLowerCase() === "gray";
1641+
const rectY = w.y + RECT_Y_OFFSET;
16081642

16091643
lines.push(` // widget:shape_rect id:${w.id} type:shape_rect x:${w.x} y:${w.y} w:${w.width} h:${w.height} fill:${fill} border:${borderWidth} opacity:${opacity} color:${colorProp} ${getCondProps(w)}`);
16101644

@@ -1615,15 +1649,15 @@ function generateSnippetLocally() {
16151649
lines.push(` for (int dy = 0; dy < ${w.height}; dy++) {`);
16161650
lines.push(` for (int dx = 0; dx < ${w.width}; dx++) {`);
16171651
lines.push(` if ((dx + dy) % 2 == 0) {`);
1618-
lines.push(` it.draw_pixel_at(${w.x}+dx, ${w.y}+dy, COLOR_ON);`);
1652+
lines.push(` it.draw_pixel_at(${w.x}+dx, ${rectY}+dy, COLOR_ON);`);
16191653
lines.push(` }`);
16201654
lines.push(` }`);
16211655
lines.push(` }`);
16221656
} else {
1623-
lines.push(` it.filled_rectangle(${w.x}, ${w.y}, ${w.width}, ${w.height}, ${color});`);
1657+
lines.push(` it.filled_rectangle(${w.x}, ${rectY}, ${w.width}, ${w.height}, ${color});`);
16241658
}
16251659
} else {
1626-
lines.push(` it.rectangle(${w.x}, ${w.y}, ${w.width}, ${w.height}, ${color});`);
1660+
lines.push(` it.rectangle(${w.x}, ${rectY}, ${w.width}, ${w.height}, ${color});`);
16271661
// TODO: Handle border width > 1 for non-filled rects if needed (by drawing multiple rects or using a helper)
16281662
}
16291663

@@ -2277,6 +2311,7 @@ function generateSnippetLocally() {
22772311
const color = getColorConst(colorProp);
22782312
const opacity = parseInt(p.opacity || 100, 10);
22792313
const isGray = colorProp.toLowerCase() === "gray";
2314+
const rectY = w.y + RECT_Y_OFFSET;
22802315
lines.push(` // widget:shape_rect id:${w.id} type:shape_rect x:${w.x} y:${w.y} w:${w.width} h:${w.height} fill:${fill} border:${borderWidth} opacity:${opacity} color:${colorProp} ${getCondProps(w)}`);
22812316
if (fill) {
22822317
if (isGray) {
@@ -2285,19 +2320,19 @@ function generateSnippetLocally() {
22852320
lines.push(` for (int dy = 0; dy < ${w.height}; dy++) {`);
22862321
lines.push(` for (int dx = 0; dx < ${w.width}; dx++) {`);
22872322
lines.push(` if ((dx + dy) % 2 == 0) {`);
2288-
lines.push(` it.draw_pixel_at(${w.x}+dx, ${w.y}+dy, COLOR_ON);`);
2323+
lines.push(` it.draw_pixel_at(${w.x}+dx, ${rectY}+dy, COLOR_ON);`);
22892324
lines.push(` }`);
22902325
lines.push(` }`);
22912326
lines.push(` }`);
22922327
} else {
2293-
lines.push(` it.filled_rectangle(${w.x}, ${w.y}, ${w.width}, ${w.height}, ${color});`);
2328+
lines.push(` it.filled_rectangle(${w.x}, ${rectY}, ${w.width}, ${w.height}, ${color});`);
22942329
}
22952330
} else {
22962331
if (borderWidth <= 1) {
2297-
lines.push(` it.rectangle(${w.x}, ${w.y}, ${w.width}, ${w.height}, ${color});`);
2332+
lines.push(` it.rectangle(${w.x}, ${rectY}, ${w.width}, ${w.height}, ${color});`);
22982333
} else {
22992334
lines.push(` for (int i=0; i<${borderWidth}; i++) {`);
2300-
lines.push(` it.rectangle(${w.x}+i, ${w.y}+i, ${w.width}-2*i, ${w.height}-2*i, ${color});`);
2335+
lines.push(` it.rectangle(${w.x}+i, ${rectY}+i, ${w.width}-2*i, ${w.height}-2*i, ${color});`);
23012336
lines.push(` }`);
23022337
}
23032338
}
@@ -2380,11 +2415,13 @@ function generateSnippetLocally() {
23802415
const url = p.url || "";
23812416
const invert = !!p.invert;
23822417
const renderMode = p.render_mode || "Auto";
2418+
// Sanitize widget ID to match the online_image: component declaration
2419+
const onlineImageId = `online_image_${w.id}`.replace(/-/g, "_");
23832420
lines.push(` // widget:online_image id:${w.id} type:online_image x:${w.x} y:${w.y} w:${w.width} h:${w.height} url:"${url}" invert:${invert} render_mode:"${renderMode}" ${getCondProps(w)}`);
23842421
if (invert) {
2385-
lines.push(` it.image(${w.x}, ${w.y}, id(online_image_${w.id}), COLOR_OFF, COLOR_ON);`);
2422+
lines.push(` it.image(${w.x}, ${w.y}, id(${onlineImageId}), COLOR_OFF, COLOR_ON);`);
23862423
} else {
2387-
lines.push(` it.image(${w.x}, ${w.y}, id(online_image_${w.id}));`);
2424+
lines.push(` it.image(${w.x}, ${w.y}, id(${onlineImageId}));`);
23882425
}
23892426
} else if (t === "puppet") {
23902427
const url = p.image_url || "";

custom_components/reterminal_dashboard/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"documentation": "https://github.com/koosoli/ReTerminalDesigner",
55
"issue_tracker": "https://github.com/koosoli/ReTerminalDesigner/issues",
66
"configuration_url": "/reterminal-dashboard",
7-
"version": "0.6.1",
7+
"version": "0.6.2",
88
"after_dependencies": [
99
"http",
1010
"frontend",

release_notes.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22

33

44

5+
## v0.6.2 - Rendering Fixes
6+
7+
**Release Date:** December 6, 2025
8+
9+
### 🐛 Bug Fixes
10+
11+
#### Rectangle Widget
12+
- **Y Offset Alignment**: Added `RECT_Y_OFFSET` constant (-15px) to align rectangle widget rendering on e-ink display with canvas preview
13+
14+
#### Graph Widget
15+
- **Min/Max Value Regression**: Fixed regression where `min_value` and `max_value` were not being output to the graph component YAML declaration, causing graphs to display empty
16+
17+
---
18+
519
## v0.6.1
620

721
### 🐛 Bug Fixes

0 commit comments

Comments
 (0)