Skip to content

Commit 552aa7f

Browse files
committed
0.7
1 parent 67db036 commit 552aa7f

File tree

8 files changed

+56
-99
lines changed

8 files changed

+56
-99
lines changed

custom_components/reterminal_dashboard/CONTRIBUTING_AGENTS.md

Lines changed: 0 additions & 40 deletions
This file was deleted.

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> ESPHome Designer <span
23-
style="font-size: 10px; color: var(--muted); vertical-align: middle;">v0.6.4</span></h1>
23+
style="font-size: 10px; color: var(--muted); vertical-align: middle;">v0.7.0</span></h1>
2424
<div style="margin-top: 4px; font-size: 10px;">
2525
<a href="https://github.com/koosoli/esphomedesigner" target="_blank"
2626
style="color: var(--accent); text-decoration: none; display: flex; align-items: center; gap: 4px;">

custom_components/reterminal_dashboard/frontend/features/sensor_text/render.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
const entityId = widget.entity_id || "";
55
const title = widget.title || "";
66
const format = props.value_format || "label_value";
7-
const precision = parseInt(props.precision, 10);
7+
let precision = parseInt(props.precision, 10);
8+
if (isNaN(precision)) precision = 2; // Default to 2 to match export logic
89
const unitProp = props.unit || "";
910
const labelFontSize = props.label_font_size || 14;
1011
const valueFontSize = props.value_font_size || 20;

custom_components/reterminal_dashboard/frontend/js/core/properties.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ class PropertiesPanel {
387387
AppState.updateWidget(widget.id, { title: v });
388388
});
389389
this.addSelect("Display Format", props.value_format || "label_value", ["label_value", "label_newline_value", "value_only"], (v) => updateProp("value_format", v));
390-
this.addLabeledInput("Precision", "number", props.precision !== undefined ? props.precision : -1, (v) => updateProp("precision", parseInt(v, 10)));
390+
this.addLabeledInput("Precision", "number", props.precision !== undefined ? props.precision : 2, (v) => updateProp("precision", parseInt(v, 10)));
391391
this.addLabeledInputWithDataList("Prefix", "text", props.prefix || "", ["€", "$", "£", "¥", "CHF", "kr"], (v) => updateProp("prefix", v));
392392
this.addLabeledInputWithDataList("Postfix", "text", props.postfix || "", [" kWh", " W", " V", " A", " °C", " %", " ppm", " lx"], (v) => updateProp("postfix", v));
393393

custom_components/reterminal_dashboard/frontend/js/core/widget_factory.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class WidgetFactory {
8484
font_weight: 400,
8585
italic: false,
8686
unit: "",
87-
precision: -1,
87+
precision: 2,
8888
text_align: "TOP_LEFT",
8989
label_align: "TOP_LEFT",
9090
value_align: "TOP_LEFT",

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

Lines changed: 47 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ function generateSnippetLocally() {
236236

237237
// 6. Sensors (Battery, SHT4x, etc + Widget Sensors)
238238
const widgetSensorLines = [];
239-
const processedSensorIds = new Set(); // Prevent duplicates
239+
const processedSensorIds = new Set(); // For numeric sensors
240+
const processedTextSensorEntities = new Set(); // For text sensors
240241
const haTextSensorLines = []; // For text_sensor HA imports
241242

242243
pagesLocal.forEach(p => {
@@ -253,42 +254,57 @@ function generateSnippetLocally() {
253254
const isLocal = !!props.is_local_sensor;
254255

255256
// Only import HA entities, not local ones
256-
if (entity && !isLocal && !processedSensorIds.has(entity)) {
257-
processedSensorIds.add(entity);
257+
if (entity && !isLocal) {
258258
const entityId = entity.replace(/[^a-zA-Z0-9_]/g, "_");
259259

260260
if (isTextSensor || entity.startsWith("text_sensor.")) {
261-
haTextSensorLines.push(` - platform: homeassistant`);
262-
haTextSensorLines.push(` id: ${entityId}`);
263-
haTextSensorLines.push(` entity_id: ${entity}`);
264-
haTextSensorLines.push(` internal: true`);
261+
// Text Sensor Mode: Use specific ID and allow parallel registration
262+
if (!processedTextSensorEntities.has(entity)) {
263+
processedTextSensorEntities.add(entity);
264+
haTextSensorLines.push(` - platform: homeassistant`);
265+
haTextSensorLines.push(` id: ${entityId}_txt`);
266+
haTextSensorLines.push(` entity_id: ${entity}`);
267+
haTextSensorLines.push(` internal: true`);
268+
}
265269
} else {
266-
widgetSensorLines.push(` - platform: homeassistant`);
267-
widgetSensorLines.push(` id: ${entityId}`);
268-
widgetSensorLines.push(` entity_id: ${entity}`);
269-
widgetSensorLines.push(` internal: true`);
270+
// Numeric Sensor Mode: Use standard ID and checks
271+
if (!processedSensorIds.has(entity)) {
272+
processedSensorIds.add(entity);
273+
widgetSensorLines.push(` - platform: homeassistant`);
274+
widgetSensorLines.push(` id: ${entityId}`);
275+
widgetSensorLines.push(` entity_id: ${entity}`);
276+
widgetSensorLines.push(` internal: true`);
277+
}
270278
}
271279
}
272280

273281
// Handle secondary entity
274-
if (entity2 && !isLocal && !processedSensorIds.has(entity2)) {
275-
processedSensorIds.add(entity2);
282+
if (entity2 && !isLocal) {
276283
const entityId2 = entity2.replace(/[^a-zA-Z0-9_]/g, "_");
277284

278285
if (isTextSensor || entity2.startsWith("text_sensor.")) {
279-
haTextSensorLines.push(` - platform: homeassistant`);
280-
haTextSensorLines.push(` id: ${entityId2}`);
281-
haTextSensorLines.push(` entity_id: ${entity2}`);
282-
haTextSensorLines.push(` internal: true`);
286+
if (!processedTextSensorEntities.has(entity2)) {
287+
processedTextSensorEntities.add(entity2);
288+
haTextSensorLines.push(` - platform: homeassistant`);
289+
haTextSensorLines.push(` id: ${entityId2}_txt`);
290+
haTextSensorLines.push(` entity_id: ${entity2}`);
291+
haTextSensorLines.push(` internal: true`);
292+
}
283293
} else {
284-
widgetSensorLines.push(` - platform: homeassistant`);
285-
widgetSensorLines.push(` id: ${entityId2}`);
286-
widgetSensorLines.push(` entity_id: ${entity2}`);
287-
widgetSensorLines.push(` internal: true`);
294+
if (!processedSensorIds.has(entity2)) {
295+
processedSensorIds.add(entity2);
296+
widgetSensorLines.push(` - platform: homeassistant`);
297+
widgetSensorLines.push(` id: ${entityId2}`);
298+
widgetSensorLines.push(` entity_id: ${entity2}`);
299+
widgetSensorLines.push(` internal: true`);
300+
}
288301
}
289302
}
290303
}
291304

305+
306+
307+
292308
// Also collect graph widget entities
293309
if (t === "graph") {
294310
const entity = w.entity_id || "";
@@ -555,8 +571,8 @@ function generateSnippetLocally() {
555571

556572
// Collect weather entities used by sensor_text and weather_icon widgets
557573
const weatherEntitiesUsed = new Set();
558-
// Collect text_sensor entities used by sensor_text widgets
559-
const textSensorEntitiesUsed = new Set();
574+
// (Redundant textSensorEntitiesUsed logic removed)
575+
560576
for (const page of pagesLocal) {
561577
if (!page || !Array.isArray(page.widgets)) continue;
562578
for (const w of page.widgets) {
@@ -567,18 +583,11 @@ function generateSnippetLocally() {
567583
if ((t === "sensor_text" || t === "weather_icon") && entityId.startsWith("weather.")) {
568584
weatherEntitiesUsed.add(entityId);
569585
}
570-
if (t === "sensor_text" && entityId.startsWith("text_sensor.")) {
571-
textSensorEntitiesUsed.add(entityId);
572-
}
573-
// Also add sensor.* entities marked as text sensors to the text_sensor block
574-
if (t === "sensor_text" && entityId.startsWith("sensor.") && p.is_text_sensor) {
575-
textSensorEntitiesUsed.add(entityId);
576-
}
577586
}
578587
}
579588

580-
// Check if we need text_sensor block
581-
const needsTextSensors = quoteRssWidgets.length > 0 || weatherForecastWidgets.length > 0 || weatherEntitiesUsed.size > 0 || textSensorEntitiesUsed.size > 0 || calendarWidgets.length > 0;
589+
// Check if we need text_sensor block (secondary block for extras)
590+
const needsTextSensors = quoteRssWidgets.length > 0 || weatherForecastWidgets.length > 0 || weatherEntitiesUsed.size > 0 || calendarWidgets.length > 0;
582591

583592
if (needsTextSensors) {
584593
lines.push("text_sensor:");
@@ -622,20 +631,6 @@ function generateSnippetLocally() {
622631
lines.push("");
623632
}
624633

625-
// Add text_sensor entity sensors (for sensor_text widgets using text_sensor.* or sensor.* entities marked as text sensors)
626-
if (textSensorEntitiesUsed.size > 0) {
627-
lines.push(" # Text Sensor Entity Sensors (from Home Assistant)");
628-
for (const entityId of textSensorEntitiesUsed) {
629-
// Handle both text_sensor.* and sensor.* prefixes
630-
const safeId = entityId.replace(/^(text_sensor|sensor)\./, "").replace(/\./g, "_").replace(/-/g, "_");
631-
lines.push(` - platform: homeassistant`);
632-
lines.push(` id: ${safeId}`);
633-
lines.push(` entity_id: ${entityId}`);
634-
lines.push(` internal: true`);
635-
}
636-
lines.push("");
637-
}
638-
639634
// Add weather forecast condition sensors
640635
if (weatherForecastWidgets.length > 0) {
641636
lines.push(" # Weather Forecast Condition Sensors");
@@ -1040,7 +1035,8 @@ function generateSnippetLocally() {
10401035
const weight = parseInt(p.font_weight || 400);
10411036
const italic = !!p.italic;
10421037
const valueFormat = p.value_format || "label_value";
1043-
const precision = parseInt(p.precision, 10);
1038+
let precision = parseInt(p.precision, 10);
1039+
if (isNaN(precision)) precision = 2; // Default to 2 decimals if not set
10441040
const prefix = (p.prefix || "").replace(/"/g, '\\"');
10451041
const postfix = (p.postfix || "").replace(/"/g, '\\"');
10461042
const unit = (p.unit || "").replace(/"/g, '\\"');
@@ -1063,7 +1059,7 @@ function generateSnippetLocally() {
10631059
const valueFontId = addFont(family, weight, valueFontSize, italic);
10641060

10651061
// Widget metadata comment - include all properties for round-trip persistence
1066-
lines.push(` // widget:sensor_text id:${w.id} type:sensor_text x:${w.x} y:${w.y} w:${w.width} h:${w.height} ent:${entity} entity_2:${entity2} title:"${title}" format:${valueFormat} label_font:${labelFontSize} value_font:${valueFontSize} color:${colorProp} label_align:${align} value_align:${align} precision:${isNaN(precision) ? -1 : precision} unit:"${unit}" prefix:"${prefix}" postfix:"${postfix}" separator:"${separator}" local:${isLocalSensor} text_sensor:${isTextSensor} font_family:"${family}" font_weight:${weight} italic:${italic}`);
1062+
lines.push(` // widget:sensor_text id:${w.id} type:sensor_text x:${w.x} y:${w.y} w:${w.width} h:${w.height} ent:${entity} entity_2:${entity2} title:"${title}" format:${valueFormat} label_font:${labelFontSize} value_font:${valueFontSize} color:${colorProp} label_align:${align} value_align:${align} precision:${precision} unit:"${unit}" prefix:"${prefix}" postfix:"${postfix}" separator:"${separator}" local:${isLocalSensor} text_sensor:${isTextSensor} font_family:"${family}" font_weight:${weight} italic:${italic}`);
10671063

10681064
if (!entity) {
10691065
lines.push(` it.printf(${w.x}, ${w.y}, id(${valueFontId}), ${color}, TextAlign::TOP_LEFT, "No Entity");`);
@@ -1077,7 +1073,7 @@ function generateSnippetLocally() {
10771073

10781074
// Get value as string - handle text sensors vs numeric sensors
10791075
if (isTextSensor || entity.startsWith("text_sensor.")) {
1080-
lines.push(` std::string val1 = id(${entityId}).state;`);
1076+
lines.push(` std::string val1 = id(${entityId}_txt).state;`);
10811077
} else {
10821078
// Numeric sensor with optional precision
10831079
if (!isNaN(precision) && precision >= 0) {
@@ -1091,8 +1087,8 @@ function generateSnippetLocally() {
10911087

10921088
// Handle secondary entity if present
10931089
if (entityId2) {
1094-
if (isTextSensor) {
1095-
lines.push(` std::string val2 = id(${entityId2}).state;`);
1090+
if (isTextSensor || (entity2 && entity2.startsWith("text_sensor."))) {
1091+
lines.push(` std::string val2 = id(${entityId2}_txt).state;`);
10961092
} else {
10971093
if (!isNaN(precision) && precision >= 0) {
10981094
lines.push(` char buf2[32];`);

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/ESPHomeDesigner",
55
"issue_tracker": "https://github.com/koosoli/ESPHomeDesigner/issues",
66
"configuration_url": "/reterminal-dashboard",
7-
"version": "0.6.4",
7+
"version": "0.7",
88
"after_dependencies": [
99
"http",
1010
"frontend",

release_notes.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44

5-
## v0.6.4 - Experimental LVGL & Enhancements
5+
## v0.7.0 - Experimental LVGL & Enhancements
66

77
**Release Date:** December 9, 2025
88

@@ -18,8 +18,8 @@
1818
- **Dark Mode Option**: Added a toggle in Device Settings to enable global "Dark Mode" (black background with white widgets). Individual pages can override this setting via Page Settings with options: "Use Global Setting", "Light Mode", or "Dark Mode".
1919
- **Gray Color Support**: Full support for "Gray" color has been implemented for icons, text, and all other widgets.
2020
- **Sensor Text Intelligence**:
21-
- **Smart Detection**: New logic automatically detects if a sensor state is a string or a number.
22-
- **Manual Override**: Added "Is Text Sensor" checkbox to force text handling if auto-detection fails.
21+
- **Smart Type Handling**: Decoupled text vs. numeric sensor registration. "Is Text Sensor" now forces a unique text-based internal ID, fixing "NaN" issues when an entity is previously registered as a number (e.g. in a graph).
22+
- **Default Precision**: Sensor text widgets now default to 2 decimal places (e.g. `23.50`) instead of raw float output, improving default legibility. Precision can still be set to `-1` for raw output.
2323

2424
- **Experimental LVGL Widgets**: Added experimental support for LVGL `button`, `arc`, `chart` (Line/Bar), `slider`, `bar`, `image`, and `qrcode` widgets.
2525
- **Text Sensor Enhancements**:

0 commit comments

Comments
 (0)