Skip to content

Commit 3f543f5

Browse files
authored
Merge pull request #111 from koosoli/testing
0.8.5
2 parents 407f418 + 2fcc383 commit 3f543f5

File tree

16 files changed

+784
-234
lines changed

16 files changed

+784
-234
lines changed

custom_components/reterminal_dashboard/frontend/editor.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<header class="main-header">
2222
<div class="main-header-title">
2323
<h2><span class="logo-dot"></span> ESPHome Designer</h2>
24-
<span>Visual YAML Editor <small style="opacity: 0.5; margin-left: 8px;">v0.8.4</small> <span
24+
<span>Visual YAML Editor <small style="opacity: 0.5; margin-left: 8px;">v0.8.5b31</small> <span
2525
id="currentLayoutDevice" style="margin-left:8px; color:var(--accent);"></span></span>
2626
</div>
2727
<div class="main-header-actions desktop-only">

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@
2727
// Header
2828
const header = document.createElement("div");
2929
header.style.textAlign = "center";
30-
header.style.padding = "10px";
30+
header.style.padding = "2px";
3131
header.style.borderBottom = "1px solid " + (props.text_color || "black");
3232
// Ensure header doesn't grow too large
3333
header.style.flexShrink = "0";
3434

3535
const bigDate = document.createElement("div");
3636
bigDate.style.fontSize = Math.min((props.font_size_date || 100) / 2, 80) + "px";
3737
bigDate.style.fontWeight = "100";
38-
bigDate.style.lineHeight = "1";
38+
bigDate.style.lineHeight = "0.8";
3939
bigDate.innerText = date;
4040
header.appendChild(bigDate);
4141

@@ -60,8 +60,8 @@
6060
const grid = document.createElement("div");
6161
grid.style.display = "grid";
6262
grid.style.gridTemplateColumns = "repeat(7, 1fr)";
63-
grid.style.padding = "5px";
64-
grid.style.gap = "2px";
63+
grid.style.padding = "2px";
64+
grid.style.gap = "1px";
6565
grid.style.flexShrink = "0";
6666

6767
const gridFontSize = (props.font_size_grid || 14) + "px";
@@ -117,7 +117,7 @@
117117

118118
// Mock Events
119119
const events = document.createElement("div");
120-
events.style.padding = "10px";
120+
events.style.padding = "5px";
121121
events.style.fontSize = (props.font_size_event || 18) + "px";
122122
events.style.flexGrow = "1"; // Allow events area to fill remaining space
123123
events.style.overflow = "hidden"; // Clip if too small

custom_components/reterminal_dashboard/frontend/features/calendar/yaml_export.js

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,19 @@ text_sensor:
160160
int cx = ${x} + (${w} / 2);
161161
162162
// Date
163-
it.printf(cx, ${y} + 5, id(font_roboto_100), color_content, TextAlign::TOP_CENTER, "%d", time.day_of_month);
164-
it.printf(cx, ${y} + 85, id(font_roboto_24), color_content, TextAlign::TOP_CENTER, "%s", id(todays_day_name_${id}).state.c_str());
165-
it.printf(cx, ${y} + 110, id(font_roboto_14), color_content, TextAlign::TOP_CENTER, "%s", id(todays_date_month_year_${id}).state.c_str());
163+
it.printf(cx, ${y} + 0, id(font_roboto_100), color_content, TextAlign::TOP_CENTER, "%d", time.day_of_month);
164+
it.printf(cx, ${y} + 70, id(font_roboto_24), color_content, TextAlign::TOP_CENTER, "%s", id(todays_day_name_${id}).state.c_str());
165+
it.printf(cx, ${y} + 92, id(font_roboto_14), color_content, TextAlign::TOP_CENTER, "%s", id(todays_date_month_year_${id}).state.c_str());
166166
167167
// Calendar Grid
168-
int calendar_y_pos = ${y} + 135;
168+
int calendar_y_pos = ${y} + 115;
169169
170170
// 2. Mock-ish Calendar Rendering for ESPHome (simplified from reference)
171171
char cal[7][7][3];
172172
get_calendar_matrix(time.year, time.month, cal);
173173
174174
int cell_width = (${w} - 40) / 7;
175-
int cell_height = 20;
175+
int cell_height = 17;
176176
int start_x = ${x} + 20;
177177
178178
for (int i = 0; i < 7; i++) {
@@ -199,29 +199,77 @@ text_sensor:
199199
// Requires built-in JSON support in ESPHome (json component)
200200
// Data source: id(calendar_json_${id}).state
201201
202-
json::parse_json(id(calendar_json_${id}).state, [&](JsonObject root) -> bool {
203-
JsonArray entries = root.as<JsonArray>();
204-
int y_cursor = calendar_y_pos + (7 * cell_height) + 10;
205-
206-
it.line(${x} + 20, y_cursor - 5, ${x} + ${w} - 20, y_cursor - 5, color_content);
207-
208-
for (JsonVariant entry : entries) {
209-
if (y_cursor > ${y} + ${h} - 30) break;
210-
211-
const char* summary = entry["summary"]; // "Meeting"
212-
const char* start = entry["start"]; // "2023-10-10T10:00:00"
213-
214-
// Simplified drawing
215-
it.printf(${x} + 20, y_cursor, id(font_roboto_24), color_content, TextAlign::TOP_LEFT, "%d", entry["day"].as<int>());
216-
it.printf(${x} + 55, y_cursor + 4, id(font_roboto_18), color_content, TextAlign::TOP_LEFT, "%.18s", summary);
217-
218-
std::string timeStr = extract_time(start);
219-
it.printf(${x} + ${w} - 20, y_cursor + 4, id(font_roboto_18), color_content, TextAlign::TOP_RIGHT, "%s", timeStr.c_str());
220-
221-
y_cursor += 30;
222-
}
223-
return true;
224-
});
202+
// Robust Manual Parsing for Mixed Types (Array/Object)
203+
ESP_LOGD("calendar", "Raw JSON: %s", id(calendar_json_${id}).state.c_str());
204+
if (id(calendar_json_${id}).state.length() > 5 && id(calendar_json_${id}).state != "unknown") {
205+
StaticJsonDocument<2048> doc;
206+
DeserializationError error = deserializeJson(doc, id(calendar_json_${id}).state);
207+
208+
if (!error) {
209+
JsonVariant root = doc.as<JsonVariant>();
210+
JsonArray days;
211+
212+
if (root.is<JsonObject>() && root.containsKey("days")) {
213+
days = root["days"];
214+
} else if (root.is<JsonArray>()) {
215+
days = root;
216+
} else {
217+
ESP_LOGW("calendar", "Invalid JSON structure: neither object with 'days' nor array");
218+
return true;
219+
}
220+
221+
if (days.isNull() || days.size() == 0) {
222+
ESP_LOGD("calendar", "No days found in JSON");
223+
return true;
224+
}
225+
ESP_LOGD("calendar", "Processing %d days", days.size());
226+
227+
int y_cursor = calendar_y_pos + (7 * cell_height) + 10;
228+
int max_y = ${y} + ${h} - 5;
229+
230+
// Safety: Ensure we have enough space for at least one event
231+
if (y_cursor >= max_y) { ESP_LOGW("calendar", "Widget too small for events"); return true; }
232+
233+
it.filled_rectangle(${x} + 20, y_cursor - 5, ${w} - 40, 2, color_content);
234+
235+
for (JsonVariant dayEntry : days) {
236+
if (y_cursor > max_y) break;
237+
int currentDayNum = dayEntry["day"].as<int>();
238+
239+
auto draw_row = [&](JsonVariant event, bool is_all_day) {
240+
if (y_cursor > max_y) return;
241+
const char* summary = event["summary"] | "No Title";
242+
const char* start = event["start"] | "";
243+
244+
it.printf(${x} + 20, y_cursor, id(font_event_day), color_content, TextAlign::TOP_LEFT, "%d", currentDayNum);
245+
it.printf(${x} + 60, y_cursor + 4, id(font_event), color_content, TextAlign::TOP_LEFT, "%.25s", summary);
246+
247+
if (is_all_day) {
248+
it.printf(${x} + ${w} - 20, y_cursor + 4, id(font_event), color_content, TextAlign::TOP_RIGHT, "All Day");
249+
} else {
250+
std::string timeStr = extract_time(start);
251+
it.printf(${x} + ${w} - 20, y_cursor + 4, id(font_event), color_content, TextAlign::TOP_RIGHT, "%s", timeStr.c_str());
252+
}
253+
y_cursor += 25;
254+
};
255+
256+
if (dayEntry.containsKey("all_day")) {
257+
for (JsonVariant event : dayEntry["all_day"].as<JsonArray>()) {
258+
draw_row(event, true);
259+
if (y_cursor > max_y) break;
260+
}
261+
}
262+
if (dayEntry.containsKey("other")) {
263+
for (JsonVariant event : dayEntry["other"].as<JsonArray>()) {
264+
draw_row(event, false);
265+
if (y_cursor > max_y) break;
266+
}
267+
}
268+
}
269+
} else {
270+
ESP_LOGW("calendar", "JSON Parse Error: %s", error.c_str());
271+
}
272+
}
225273
}
226274
`;
227275

custom_components/reterminal_dashboard/frontend/hardware/waveshare-esp32-universal-epaper-7.5v2.yaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# ============================================================================
22
# ESPHome YAML - Generated by ESPHome Designer
33
# ============================================================================
4-
# TARGET DEVICE: Waveshare Universal e-Paper Raw Panel Driver Board with 7.5" e-Paper
5-
# - Framework: ESP-IDF
4+
# TARGET DEVICE: Waveshare Universal e-Paper Raw Panel Driver Board
5+
# Display: 7.5" e-Paper (V2)
6+
# Resolution: 800x480
7+
# Shape: rect
8+
# Orientation: landscape
9+
# Framework: ESP-IDF
610
# ============================================================================
711
#
812
# BASED ON: https://github.com/agillis/esphome-modular-lvgl-buttons
@@ -31,7 +35,7 @@
3135
esphome:
3236
min_version: 2024.11.0
3337
project:
34-
name: "Waveshare.ESP32-Universal-epaper-7.5v2" # May also work fine fore the 7.5
38+
name: "Waveshare.ESP32-Universal-epaper-7.5v2"
3539
version: "1.0"
3640

3741
esp32:
@@ -51,8 +55,8 @@ display:
5155
number: GPIO25
5256
inverted: true
5357
reset_pin: GPIO26
54-
reset_duration: 2ms
55-
model: 7.50inV2
58+
reset_duration: 10ms
59+
model: 7.50inv2p
5660
rotation:
5761
update_interval: 5min
5862
id: my_display

custom_components/reterminal_dashboard/frontend/js/core/canvas.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,9 @@ class Canvas {
8888
this.canvas.style.height = `${dims.height}px`;
8989

9090
// Apply device shape (e.g. round)
91-
const currentModel = (typeof getDeviceModel === 'function') ? getDeviceModel() : "reterminal_e1001";
92-
const profile = (window.DEVICE_PROFILES && window.DEVICE_PROFILES[currentModel]) ? window.DEVICE_PROFILES[currentModel] : null;
91+
const shape = AppState.getCanvasShape();
9392

94-
if (profile && profile.shape === "round") {
93+
if (shape === "round") {
9594
this.canvas.style.borderRadius = "50%";
9695
this.canvas.style.overflow = "hidden";
9796
this.canvas.style.boxShadow = "0 0 0 10px rgba(0,0,0,0.1)"; // Optional: hint at the bezel

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -894,14 +894,14 @@ class PropertiesPanel {
894894
this.addSectionLabel("Appearance");
895895
this.addCheckbox("Show Background", props.show_background !== false, (v) => updateProp("show_background", v));
896896
if (props.show_background !== false) {
897-
this.addSelect("Background Color", props.background_color || "gray", colors, (v) => updateProp("background_color", v));
897+
this.addSelect("Background Color", props.background_color || "black", colors, (v) => updateProp("background_color", v));
898898
this.addLabeledInput("Border Radius", "number", props.border_radius || 8, (v) => updateProp("border_radius", parseInt(v, 10)));
899899
}
900900

901901
this.addSectionLabel("Sizes & Color");
902902
this.addLabeledInput("Icon Size", "number", props.icon_size || 20, (v) => updateProp("icon_size", parseInt(v, 10)));
903903
this.addLabeledInput("Font Size", "number", props.font_size || 14, (v) => updateProp("font_size", parseInt(v, 10)));
904-
this.addSelect("Foreground Color", props.color || "black", colors, (v) => updateProp("color", v));
904+
this.addSelect("Foreground Color", props.color || "white", colors, (v) => updateProp("color", v));
905905
}
906906
else if (type === "template_nav_bar") {
907907
this.addSectionLabel("Button Visibility");
@@ -912,13 +912,13 @@ class PropertiesPanel {
912912
this.addSectionLabel("Appearance");
913913
this.addCheckbox("Show Background", props.show_background !== false, (v) => updateProp("show_background", v));
914914
if (props.show_background !== false) {
915-
this.addSelect("Background Color", props.background_color || "gray", colors, (v) => updateProp("background_color", v));
915+
this.addSelect("Background Color", props.background_color || "black", colors, (v) => updateProp("background_color", v));
916916
this.addLabeledInput("Border Radius", "number", props.border_radius || 8, (v) => updateProp("border_radius", parseInt(v, 10)));
917917
}
918918

919919
this.addSectionLabel("Sizes & Color");
920920
this.addLabeledInput("Icon Size", "number", props.icon_size || 24, (v) => updateProp("icon_size", parseInt(v, 10)));
921-
this.addSelect("Foreground Color", props.color || "black", colors, (v) => updateProp("color", v));
921+
this.addSelect("Foreground Color", props.color || "white", colors, (v) => updateProp("color", v));
922922
}
923923
else if (type === "touch_area") {
924924

@@ -1855,7 +1855,13 @@ class PropertiesPanel {
18551855
AppState.updateWidget(widget.id, { condition_operator: v });
18561856
});
18571857

1858-
this.addLabeledInput("Condition State", "text", widget.condition_state, (v) => {
1858+
const commonStates = [
1859+
"on", "off", "open", "closed",
1860+
"true", "false", "home", "not_home",
1861+
"locked", "unlocked", "active", "inactive",
1862+
"detected", "clear", "occupied"
1863+
];
1864+
this.addLabeledInputWithDataList("Condition State", "text", widget.condition_state, commonStates, (v) => {
18591865
AppState.updateWidget(widget.id, { condition_state: v });
18601866
});
18611867

custom_components/reterminal_dashboard/frontend/js/core/state.js

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,27 +122,23 @@ class StateStore {
122122

123123
getCanvasDimensions() {
124124
const model = this.state.deviceModel || this.state.settings.device_model || "reterminal_e1001";
125-
// Safe access to device profile
126125
const profile = (window.DEVICE_PROFILES && window.DEVICE_PROFILES[model]) ? window.DEVICE_PROFILES[model] : null;
127126

128-
let width = 800; // Default width
129-
let height = 480; // Default height
127+
let width = this.state.settings.width || 800; // Default width
128+
let height = this.state.settings.height || 480; // Default height
129+
let customRes = !!(this.state.settings.width && this.state.settings.height);
130130

131-
if (profile) {
131+
if (profile && !customRes) {
132132
if (profile.resolution) {
133-
// Use explicit resolution if available
134133
width = profile.resolution.width;
135134
height = profile.resolution.height;
136135
} else if (profile.display_config) {
137-
// Fallback: Try to find dimensions in the display_config
138136
let foundWidth = null;
139137
let foundHeight = null;
140138

141139
const parseDim = (line) => {
142140
const parts = line.split(":");
143-
if (parts.length === 2) {
144-
return parseInt(parts[1].trim(), 10);
145-
}
141+
if (parts.length === 2) return parseInt(parts[1].trim(), 10);
146142
return null;
147143
};
148144

@@ -157,25 +153,26 @@ class StateStore {
157153
}
158154
}
159155

160-
// Specific fallbacks if parsing failed
161-
if (width === 800 && height === 480) { // If still default
156+
if (width === 800 && height === 480) {
162157
if (model.includes("2432s028")) { width = 320; height = 240; }
163158
else if (model.includes("4827s032r")) { width = 480; height = 272; }
164159
}
165160
}
166161

167-
// Apply orientation switch
168162
if (this.state.settings.orientation === ORIENTATIONS.PORTRAIT) {
169163
return { width: Math.min(width, height), height: Math.max(width, height) };
170164
} else {
171-
// Landscape: ensure width > height usually, BUT some devices are natively portrait.
172-
// However, the editor "Landscape" usually implies Width > Height.
173-
// If the device is NATURALLY portrait (like phones), Landscape means rotated.
174-
// So we should return the larger dim as width.
175165
return { width: Math.max(width, height), height: Math.min(width, height) };
176166
}
177167
}
178168

169+
getCanvasShape() {
170+
if (this.state.settings.shape) return this.state.settings.shape;
171+
const model = this.state.deviceModel || this.state.settings.device_model || "reterminal_e1001";
172+
const profile = (window.DEVICE_PROFILES && window.DEVICE_PROFILES[model]) ? window.DEVICE_PROFILES[model] : null;
173+
return profile?.shape || "rect";
174+
}
175+
179176
getPagesPayload() {
180177
const settings = { ...this.state.settings };
181178

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ class WidgetFactory {
517517
case "calendar":
518518
// Standard size for a calendar widget
519519
widget.width = 400;
520-
widget.height = 550;
520+
widget.height = 350;
521521
widget.props = {
522522
entity_id: "sensor.esp_calendar_data",
523523
border_width: 2,
@@ -541,11 +541,11 @@ class WidgetFactory {
541541
show_humidity: true,
542542
show_battery: true,
543543
show_background: true,
544-
background_color: "gray",
544+
background_color: "black",
545545
border_radius: 8,
546546
icon_size: 20,
547547
font_size: 14,
548-
color: defaultColor
548+
color: "white"
549549
};
550550
break;
551551

@@ -557,10 +557,10 @@ class WidgetFactory {
557557
show_home: true,
558558
show_next: true,
559559
show_background: true,
560-
background_color: "gray",
560+
background_color: "black",
561561
border_radius: 8,
562562
icon_size: 24,
563-
color: defaultColor
563+
color: "white"
564564
};
565565
break;
566566

custom_components/reterminal_dashboard/frontend/js/io/devices.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ window.DEVICE_PROFILES = {
139139
buttons: null
140140
},
141141
battery: {
142-
attenuation: "11db",
142+
attenuation: "12db",
143143
multiplier: 2.0,
144144
calibration: { min: 3.30, max: 4.15 }
145145
},
@@ -282,7 +282,7 @@ window.DEVICE_PROFILES = {
282282
main_power_pin: "GPIO2"
283283
},
284284
battery: {
285-
attenuation: "11db",
285+
attenuation: "12db",
286286
multiplier: 2.0,
287287
calibration: { min: 3.27, max: 4.15 } // Standard LiPo
288288
},
@@ -298,7 +298,7 @@ window.DEVICE_PROFILES = {
298298
// Since M5Paper with IT8951E is already swapped (swap_xy: true),
299299
// rotating 180 involves flipping the mirroring state.
300300
// If touch is still inverted after rotation, swap mirror_x/mirror_y below.
301-
transform: { mirror_x: false, mirror_y: true, swap_xy: true },
301+
transform: { mirror_x: true, mirror_y: true, swap_xy: true },
302302
// Calibration matches the IT8951E component's 960x540 coordinate space
303303
calibration: { x_min: 0, x_max: 960, y_min: 0, y_max: 540 }
304304
},

0 commit comments

Comments
 (0)