dashboard: add grouped temperature overview tiles

This commit is contained in:
2026-06-10 23:58:40 -06:00
parent b393e8d704
commit c4138468f2
8 changed files with 194 additions and 86 deletions
@@ -19,10 +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 *inside_temp_value_label = nullptr;
static lv_obj_t *inside_temp_name_label = nullptr;
static lv_obj_t *outside_temp_value_label = nullptr;
static lv_obj_t *outside_temp_name_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 *system_status_label = nullptr;
static lv_obj_t *wifi_icon_label = nullptr;
static lv_obj_t *wifi_bars_obj[4] = {nullptr};
@@ -60,10 +58,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_inside_temp_value_text;
static String last_inside_temp_name_text;
static String last_outside_temp_value_text;
static String last_outside_temp_name_text;
static String last_temp_tile_value_text[4];
static String last_temp_tile_name_text[4];
static String last_system_status_text;
static int last_wifi_bar_count = -1;
static bool last_cargo_api_ok = false;
@@ -74,18 +70,61 @@ static String on_off(bool value)
}
static String compact_temp_name(const char *raw_name)
static String normalize_temp_group(String group, const String &name, bool weather)
{
String name = raw_name && raw_name[0] ? String(raw_name) : String("Inside");
group.toLowerCase();
group.trim();
name.replace(" Area", "");
name.replace(" Zone", "");
if (name.length() > 14) {
name = name.substring(0, 14);
if (weather || group == "weather" || group == "outside") {
return "outside";
}
return name;
if (group == "fridge" || group == "freezer" || group == "cooler") {
return "fridge";
}
if (group == "battery" || group == "bms") {
return "battery";
}
if (group == "cabin" || group == "inside" || group == "interior") {
return "cabin";
}
String lower_name = name;
lower_name.toLowerCase();
if (lower_name.indexOf("fridge") >= 0 || lower_name.indexOf("freezer") >= 0 || lower_name.indexOf("cooler") >= 0) {
return "fridge";
}
if (lower_name.indexOf("outside") >= 0 || lower_name.indexOf("ambient") >= 0) {
return "outside";
}
if (lower_name.indexOf("battery") >= 0 || lower_name.indexOf("bms") >= 0) {
return "battery";
}
return group.length() ? group : "cabin";
}
static int temp_group_index(const String &group)
{
if (group == "cabin") return 0;
if (group == "fridge") return 1;
if (group == "outside") return 2;
return 3;
}
static String temp_group_label(int index, const String &fallback)
{
if (index == 0) return fallback.length() ? fallback : "Cabin";
if (index == 1) return "Fridge";
if (index == 2) return "Outside";
if (index == 3) return "Other";
return "Temp";
}
static String format_hours(float hours)
@@ -611,10 +650,9 @@ static void parse_status_json(const String &body)
}
if (doc["temps"].is<JsonArray>()) {
String inside_name = "Inside";
String inside_value = "--";
String outside_name = "Outside";
String outside_value = "--";
String tile_names[4] = {"Cabin", "Fridge", "Outside", "Other"};
String tile_values[4] = {"--", "--", "--", "--"};
bool tile_has_value[4] = {false, false, false, false};
for (JsonObject temp : doc["temps"].as<JsonArray>()) {
bool enabled = temp["enabled"] | false;
@@ -625,23 +663,38 @@ static void parse_status_json(const String &body)
continue;
}
String value = String((float)(temp["temperature_f"] | 0.0), 1);
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 group = normalize_temp_group(String(raw_group), name, weather);
int index = temp_group_index(group);
int rounded = (int)round((float)(temp["temperature_f"] | 0.0));
String value = String(rounded);
value += "°";
if (weather) {
outside_name = "Outside";
outside_value = value;
if (tile_has_value[index]) {
tile_values[index] += "/";
tile_values[index] += value;
} else {
const char *name = temp["name"] | "Inside";
inside_name = compact_temp_name(name);
inside_value = value;
tile_values[index] = value;
tile_has_value[index] = true;
if (index == 0) {
name.replace(" Area", "");
name.replace(" Zone", "");
tile_names[index] = name.length() ? name : "Cabin";
} else {
tile_names[index] = temp_group_label(index, name);
}
}
}
set_label_text_if_changed(inside_temp_value_label, last_inside_temp_value_text, inside_value);
set_label_text_if_changed(inside_temp_name_label, last_inside_temp_name_text, inside_name);
set_label_text_if_changed(outside_temp_value_label, last_outside_temp_value_text, outside_value);
set_label_text_if_changed(outside_temp_name_label, last_outside_temp_name_text, outside_name);
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]);
}
}
if (doc["relays"].is<JsonArray>()) {
@@ -864,59 +917,39 @@ 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);
lv_obj_t *inside_tile = lv_obj_create(temp_card);
lv_obj_set_size(inside_tile, 236, 132);
lv_obj_align(inside_tile, LV_ALIGN_TOP_MID, 0, 22);
lv_obj_set_style_radius(inside_tile, 18, 0);
lv_obj_set_style_bg_color(inside_tile, lv_color_hex(0x17202A), 0);
lv_obj_set_style_border_color(inside_tile, lv_color_hex(0x2F3A46), 0);
lv_obj_set_style_border_width(inside_tile, 2, 0);
lv_obj_set_style_pad_all(inside_tile, 0, 0);
lv_obj_clear_flag(inside_tile, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_scrollbar_mode(inside_tile, LV_SCROLLBAR_MODE_OFF);
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};
inside_temp_value_label = lv_label_create(inside_tile);
lv_label_set_text(inside_temp_value_label, "--");
lv_obj_set_style_text_color(inside_temp_value_label, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(inside_temp_value_label, &lv_font_montserrat_48, 0);
lv_obj_set_width(inside_temp_value_label, 220);
lv_obj_set_style_text_align(inside_temp_value_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(inside_temp_value_label, LV_ALIGN_TOP_MID, 0, 18);
for (int i = 0; i < 4; 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_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);
lv_obj_set_style_pad_all(tile, 0, 0);
lv_obj_clear_flag(tile, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_scrollbar_mode(tile, LV_SCROLLBAR_MODE_OFF);
inside_temp_name_label = lv_label_create(inside_tile);
lv_label_set_text(inside_temp_name_label, "Inside");
lv_obj_set_style_text_color(inside_temp_name_label, lv_color_hex(0xB8C0C8), 0);
lv_obj_set_style_text_font(inside_temp_name_label, &lv_font_montserrat_26, 0);
lv_obj_set_width(inside_temp_name_label, 220);
lv_obj_set_style_text_align(inside_temp_name_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(inside_temp_name_label, LV_ALIGN_TOP_MID, 0, 84);
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_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_t *outside_tile = lv_obj_create(temp_card);
lv_obj_set_size(outside_tile, 236, 132);
lv_obj_align(outside_tile, LV_ALIGN_BOTTOM_MID, 0, -22);
lv_obj_set_style_radius(outside_tile, 18, 0);
lv_obj_set_style_bg_color(outside_tile, lv_color_hex(0x17202A), 0);
lv_obj_set_style_border_color(outside_tile, lv_color_hex(0x2F3A46), 0);
lv_obj_set_style_border_width(outside_tile, 2, 0);
lv_obj_set_style_pad_all(outside_tile, 0, 0);
lv_obj_clear_flag(outside_tile, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_scrollbar_mode(outside_tile, LV_SCROLLBAR_MODE_OFF);
outside_temp_value_label = lv_label_create(outside_tile);
lv_label_set_text(outside_temp_value_label, "--");
lv_obj_set_style_text_color(outside_temp_value_label, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(outside_temp_value_label, &lv_font_montserrat_48, 0);
lv_obj_set_width(outside_temp_value_label, 220);
lv_obj_set_style_text_align(outside_temp_value_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(outside_temp_value_label, LV_ALIGN_TOP_MID, 0, 18);
outside_temp_name_label = lv_label_create(outside_tile);
lv_label_set_text(outside_temp_name_label, "Outside");
lv_obj_set_style_text_color(outside_temp_name_label, lv_color_hex(0xB8C0C8), 0);
lv_obj_set_style_text_font(outside_temp_name_label, &lv_font_montserrat_26, 0);
lv_obj_set_width(outside_temp_name_label, 220);
lv_obj_set_style_text_align(outside_temp_name_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(outside_temp_name_label, LV_ALIGN_TOP_MID, 0, 84);
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 ? "Outside" : "Other");
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_20, 0);
lv_obj_set_width(temp_tile_name_labels[i], temp_tile_w - 8);
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_t *vehicle_label = lv_label_create(vehicle_card);
lv_label_set_text(vehicle_label, "Vehicle\n\nOffline\n\nFuture:\nCoolant\nTrans Temp\n4WD\nTilt/Roll");