From 417fad267663f9a1e550eb79cda666a7300cc49e Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 11 Jun 2026 00:39:37 -0600 Subject: [PATCH] dashboard: show two prioritized temperature groups --- docs/API.md | 8 ++ docs/dashboard-esp32s3.md | 8 ++ docs/project-state.md | 8 ++ .../esp32-s3-dashboard/dashboard_config.h | 2 +- .../esp32-s3-dashboard/esp32-s3-dashboard.ino | 136 ++++++++++-------- .../esp32/overland-controller/app_config.h | 2 +- .../overland-controller.ino | 15 +- 7 files changed, 112 insertions(+), 67 deletions(-) diff --git a/docs/API.md b/docs/API.md index db2250b..6b57311 100644 --- a/docs/API.md +++ b/docs/API.md @@ -955,3 +955,11 @@ Example: - Fridge Zone 2: `group=fridge`, `priority=2` The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`. + +### Temperature Slot and Group Display + +The Cargo ESP currently supports 4 temperature sensor slots. + +Temperature sensor `group` values are free-form text in the WebUI. Entering a new group name effectively creates that group. The dashboard overview shows the first two available non-weather groups by priority. Multiple sensors in the same group are combined into one tile, such as `34°/45°`. + +Sensors marked `weather: true` are excluded from the temperature card and shown in the top status strip as `OUT ##°`. diff --git a/docs/dashboard-esp32s3.md b/docs/dashboard-esp32s3.md index be24ac2..de78b1a 100644 --- a/docs/dashboard-esp32s3.md +++ b/docs/dashboard-esp32s3.md @@ -273,3 +273,11 @@ Example: - Fridge Zone 2: `group=fridge`, `priority=2` The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`. + +### Temperature Slot and Group Display + +The Cargo ESP currently supports 4 temperature sensor slots. + +Temperature sensor `group` values are free-form text in the WebUI. Entering a new group name effectively creates that group. The dashboard overview shows the first two available non-weather groups by priority. Multiple sensors in the same group are combined into one tile, such as `34°/45°`. + +Sensors marked `weather: true` are excluded from the temperature card and shown in the top status strip as `OUT ##°`. diff --git a/docs/project-state.md b/docs/project-state.md index 5730dab..6aacfb8 100644 --- a/docs/project-state.md +++ b/docs/project-state.md @@ -340,3 +340,11 @@ Example: - Fridge Zone 2: `group=fridge`, `priority=2` The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`. + +### Temperature Slot and Group Display + +The Cargo ESP currently supports 4 temperature sensor slots. + +Temperature sensor `group` values are free-form text in the WebUI. Entering a new group name effectively creates that group. The dashboard overview shows the first two available non-weather groups by priority. Multiple sensors in the same group are combined into one tile, such as `34°/45°`. + +Sensors marked `weather: true` are excluded from the temperature card and shown in the top status strip as `OUT ##°`. diff --git a/firmware/esp32-s3-dashboard/dashboard_config.h b/firmware/esp32-s3-dashboard/dashboard_config.h index 08230f0..fed8af1 100644 --- a/firmware/esp32-s3-dashboard/dashboard_config.h +++ b/firmware/esp32-s3-dashboard/dashboard_config.h @@ -1,6 +1,6 @@ #pragma once -static const char *DASHBOARD_VERSION = "0.1.39-temp-priority"; +static const char *DASHBOARD_VERSION = "0.1.40-temp-two-groups"; // Update these if your Cargo ESP AP credentials are different. static const char *CARGO_WIFI_SSID = "OverlandController"; diff --git a/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino b/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino index 463d746..36a3c5a 100644 --- a/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino +++ b/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino @@ -19,8 +19,8 @@ static lv_obj_t *battery_charge_pulse_arc = nullptr; static lv_obj_t *battery_current_label = nullptr; static lv_obj_t *battery_estimate_label = nullptr; static lv_obj_t *battery_soc_label = nullptr; -static lv_obj_t *temp_tile_value_labels[4] = {nullptr}; -static lv_obj_t *temp_tile_name_labels[4] = {nullptr}; +static lv_obj_t *temp_tile_value_labels[2] = {nullptr}; +static lv_obj_t *temp_tile_name_labels[2] = {nullptr}; static lv_obj_t *system_status_label = nullptr; static lv_obj_t *wifi_icon_label = nullptr; static lv_obj_t *wifi_bars_obj[4] = {nullptr}; @@ -59,8 +59,8 @@ static bool pending_relay_previous_state = false; static String last_battery_current_text; static String last_battery_estimate_text; static String last_battery_soc_text; -static String last_temp_tile_value_text[4]; -static String last_temp_tile_name_text[4]; +static String last_temp_tile_value_text[2]; +static String last_temp_tile_name_text[2]; static String last_system_status_text; static String last_outside_status_temp_text; static int last_wifi_bar_count = -1; @@ -112,6 +112,25 @@ static String normalize_temp_group(String group, const String &name, bool weathe return group.length() ? group : "cabin"; } + +static String display_temp_group_name(String group) +{ + group.trim(); + group.toLowerCase(); + + if (group == "cabin") return "Cabin"; + if (group == "fridge") return "Fridge"; + if (group == "battery") return "Battery"; + if (group == "other") return "Other"; + + if (group.length() == 0) { + return "Temp"; + } + + group.setCharAt(0, toupper(group.charAt(0))); + return group; +} + static int temp_group_index(const String &group) { if (group == "cabin") return 0; @@ -662,19 +681,15 @@ static void parse_status_json(const String &body) } if (doc["temps"].is()) { - String tile_names[4] = {"Cabin", "Fridge", "Battery", "Other"}; - String tile_values[4] = {"--", "--", "--", "--"}; - bool tile_has_value[4] = {false, false, false, false}; String outside_status = "OUT --"; struct TempDisplayItem { - int tile_index; + String group; int priority; int rounded_f; - String name; }; - TempDisplayItem items[8]; + TempDisplayItem items[4]; int item_count = 0; for (JsonObject temp : doc["temps"].as()) { @@ -698,9 +713,10 @@ static void parse_status_json(const String &body) const char *raw_name = temp["name"] | ""; const char *raw_group = temp["group"] | ""; - String name = raw_name && raw_name[0] ? String(raw_name) : String("Cabin"); + String name = raw_name && raw_name[0] ? String(raw_name) : String("cabin"); String group = normalize_temp_group(String(raw_group), name, false); + // Outside/weather is shown in the status strip, not the main temp card. if (group == "outside") { outside_status = "OUT "; outside_status += String((int)round(temp_f)); @@ -708,15 +724,12 @@ static void parse_status_json(const String &body) continue; } - int index = temp_group_index(group); - if (index < 0 || index > 3) { - index = 3; - } - - int priority = temp["priority"] | 99; - - if (item_count < 8) { - items[item_count] = {index, priority, (int)round(temp_f), name}; + if (item_count < 4) { + items[item_count] = { + group, + temp["priority"] | 99, + (int)round(temp_f) + }; item_count++; } } @@ -724,8 +737,8 @@ static void parse_status_json(const String &body) for (int a = 0; a < item_count - 1; a++) { for (int b = a + 1; b < item_count; b++) { if ( - items[b].tile_index < items[a].tile_index || - (items[b].tile_index == items[a].tile_index && items[b].priority < items[a].priority) + items[b].priority < items[a].priority || + (items[b].priority == items[a].priority && items[b].group < items[a].group) ) { TempDisplayItem tmp = items[a]; items[a] = items[b]; @@ -734,38 +747,46 @@ static void parse_status_json(const String &body) } } + String group_names[2] = {"--", "--"}; + String group_values[2] = {"--", "--"}; + int group_count = 0; + for (int i = 0; i < item_count; i++) { - int index = items[i].tile_index; + int slot = -1; + + for (int g = 0; g < group_count; g++) { + if (group_names[g] == display_temp_group_name(items[i].group)) { + slot = g; + break; + } + } + + if (slot < 0) { + if (group_count >= 2) { + continue; + } + + slot = group_count; + group_names[slot] = display_temp_group_name(items[i].group); + group_values[slot] = ""; + group_count++; + } + String value = String(items[i].rounded_f); value += "°"; - if (tile_has_value[index]) { - tile_values[index] += "/"; - tile_values[index] += value; - } else { - tile_values[index] = value; - tile_has_value[index] = true; - - if (index == 0) { - String name = items[i].name; - name.replace(" Area", ""); - name.replace(" Zone", ""); - tile_names[index] = name.length() ? name : "Cabin"; - } else if (index == 1) { - tile_names[index] = "Fridge"; - } else if (index == 2) { - tile_names[index] = "Battery"; - } else { - tile_names[index] = "Other"; - } + if (group_values[slot].length() > 0) { + group_values[slot] += "/"; } + + group_values[slot] += value; } update_outside_status_temp(outside_status); - for (int i = 0; i < 4; i++) { - set_label_text_if_changed(temp_tile_value_labels[i], last_temp_tile_value_text[i], tile_values[i]); - set_label_text_if_changed(temp_tile_name_labels[i], last_temp_tile_name_text[i], tile_names[i]); + for (int i = 0; i < 2; i++) { + set_label_text_if_changed(temp_tile_value_labels[i], last_temp_tile_value_text[i], group_values[i]); + set_label_text_if_changed(temp_tile_name_labels[i], last_temp_tile_name_text[i], group_names[i]); } } @@ -989,16 +1010,15 @@ static void create_overland_overview_screen() lv_obj_align(battery_current_label, LV_ALIGN_TOP_MID, 0, 161); lv_obj_add_flag(battery_current_label, LV_OBJ_FLAG_HIDDEN); - const int temp_tile_w = 112; - const int temp_tile_h = 130; - const int temp_tile_x[4] = {8, 140, 8, 140}; - const int temp_tile_y[4] = {18, 18, 160, 160}; + const int temp_tile_w = 236; + const int temp_tile_h = 132; + const int temp_tile_y[2] = {18, 160}; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 2; i++) { lv_obj_t *tile = lv_obj_create(temp_card); lv_obj_set_size(tile, temp_tile_w, temp_tile_h); - lv_obj_align(tile, LV_ALIGN_TOP_LEFT, temp_tile_x[i], temp_tile_y[i]); - lv_obj_set_style_radius(tile, 16, 0); + lv_obj_align(tile, LV_ALIGN_TOP_MID, 0, temp_tile_y[i]); + lv_obj_set_style_radius(tile, 18, 0); lv_obj_set_style_bg_color(tile, lv_color_hex(0x17202A), 0); lv_obj_set_style_border_color(tile, lv_color_hex(0x2F3A46), 0); lv_obj_set_style_border_width(tile, 2, 0); @@ -1009,18 +1029,18 @@ static void create_overland_overview_screen() temp_tile_value_labels[i] = lv_label_create(tile); lv_label_set_text(temp_tile_value_labels[i], "--"); lv_obj_set_style_text_color(temp_tile_value_labels[i], lv_color_hex(0xFFFFFF), 0); - lv_obj_set_style_text_font(temp_tile_value_labels[i], &lv_font_montserrat_30, 0); - lv_obj_set_width(temp_tile_value_labels[i], temp_tile_w - 8); + lv_obj_set_style_text_font(temp_tile_value_labels[i], &lv_font_montserrat_40, 0); + lv_obj_set_width(temp_tile_value_labels[i], temp_tile_w - 10); lv_obj_set_style_text_align(temp_tile_value_labels[i], LV_TEXT_ALIGN_CENTER, 0); - lv_obj_align(temp_tile_value_labels[i], LV_ALIGN_TOP_MID, 0, 26); + lv_obj_align(temp_tile_value_labels[i], LV_ALIGN_TOP_MID, 0, 22); temp_tile_name_labels[i] = lv_label_create(tile); - lv_label_set_text(temp_tile_name_labels[i], i == 0 ? "Cabin" : i == 1 ? "Fridge" : i == 2 ? "Battery" : "Other"); + lv_label_set_text(temp_tile_name_labels[i], "--"); lv_obj_set_style_text_color(temp_tile_name_labels[i], lv_color_hex(0xB8C0C8), 0); lv_obj_set_style_text_font(temp_tile_name_labels[i], &lv_font_montserrat_26, 0); - lv_obj_set_width(temp_tile_name_labels[i], temp_tile_w - 8); + lv_obj_set_width(temp_tile_name_labels[i], temp_tile_w - 10); lv_obj_set_style_text_align(temp_tile_name_labels[i], LV_TEXT_ALIGN_CENTER, 0); - lv_obj_align(temp_tile_name_labels[i], LV_ALIGN_TOP_MID, 0, 78); + lv_obj_align(temp_tile_name_labels[i], LV_ALIGN_TOP_MID, 0, 86); } lv_obj_t *vehicle_label = lv_label_create(vehicle_card); diff --git a/firmware/esp32/overland-controller/app_config.h b/firmware/esp32/overland-controller/app_config.h index 9ffa42d..556fe41 100644 --- a/firmware/esp32/overland-controller/app_config.h +++ b/firmware/esp32/overland-controller/app_config.h @@ -3,7 +3,7 @@ #include #define MAX_RELAYS 2 -#define MAX_TEMP_SENSORS 8 +#define MAX_TEMP_SENSORS 4 #define HARDWARE_PROFILE "generic_esp32_2ch_relay" struct RelayConfig { diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 37792bc..45c23c9 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -1184,13 +1184,14 @@ function renderConfigControls(data){ - + + +