dashboard: show two prioritized temperature groups
This commit is contained in:
@@ -955,3 +955,11 @@ Example:
|
|||||||
- Fridge Zone 2: `group=fridge`, `priority=2`
|
- Fridge Zone 2: `group=fridge`, `priority=2`
|
||||||
|
|
||||||
The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`.
|
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 ##°`.
|
||||||
|
|||||||
@@ -273,3 +273,11 @@ Example:
|
|||||||
- Fridge Zone 2: `group=fridge`, `priority=2`
|
- Fridge Zone 2: `group=fridge`, `priority=2`
|
||||||
|
|
||||||
The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`.
|
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 ##°`.
|
||||||
|
|||||||
@@ -340,3 +340,11 @@ Example:
|
|||||||
- Fridge Zone 2: `group=fridge`, `priority=2`
|
- Fridge Zone 2: `group=fridge`, `priority=2`
|
||||||
|
|
||||||
The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`.
|
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 ##°`.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#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.
|
// Update these if your Cargo ESP AP credentials are different.
|
||||||
static const char *CARGO_WIFI_SSID = "OverlandController";
|
static const char *CARGO_WIFI_SSID = "OverlandController";
|
||||||
|
|||||||
@@ -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_current_label = nullptr;
|
||||||
static lv_obj_t *battery_estimate_label = nullptr;
|
static lv_obj_t *battery_estimate_label = nullptr;
|
||||||
static lv_obj_t *battery_soc_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_value_labels[2] = {nullptr};
|
||||||
static lv_obj_t *temp_tile_name_labels[4] = {nullptr};
|
static lv_obj_t *temp_tile_name_labels[2] = {nullptr};
|
||||||
static lv_obj_t *system_status_label = nullptr;
|
static lv_obj_t *system_status_label = nullptr;
|
||||||
static lv_obj_t *wifi_icon_label = nullptr;
|
static lv_obj_t *wifi_icon_label = nullptr;
|
||||||
static lv_obj_t *wifi_bars_obj[4] = {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_current_text;
|
||||||
static String last_battery_estimate_text;
|
static String last_battery_estimate_text;
|
||||||
static String last_battery_soc_text;
|
static String last_battery_soc_text;
|
||||||
static String last_temp_tile_value_text[4];
|
static String last_temp_tile_value_text[2];
|
||||||
static String last_temp_tile_name_text[4];
|
static String last_temp_tile_name_text[2];
|
||||||
static String last_system_status_text;
|
static String last_system_status_text;
|
||||||
static String last_outside_status_temp_text;
|
static String last_outside_status_temp_text;
|
||||||
static int last_wifi_bar_count = -1;
|
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";
|
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)
|
static int temp_group_index(const String &group)
|
||||||
{
|
{
|
||||||
if (group == "cabin") return 0;
|
if (group == "cabin") return 0;
|
||||||
@@ -662,19 +681,15 @@ static void parse_status_json(const String &body)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (doc["temps"].is<JsonArray>()) {
|
if (doc["temps"].is<JsonArray>()) {
|
||||||
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 --";
|
String outside_status = "OUT --";
|
||||||
|
|
||||||
struct TempDisplayItem {
|
struct TempDisplayItem {
|
||||||
int tile_index;
|
String group;
|
||||||
int priority;
|
int priority;
|
||||||
int rounded_f;
|
int rounded_f;
|
||||||
String name;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TempDisplayItem items[8];
|
TempDisplayItem items[4];
|
||||||
int item_count = 0;
|
int item_count = 0;
|
||||||
|
|
||||||
for (JsonObject temp : doc["temps"].as<JsonArray>()) {
|
for (JsonObject temp : doc["temps"].as<JsonArray>()) {
|
||||||
@@ -698,9 +713,10 @@ static void parse_status_json(const String &body)
|
|||||||
const char *raw_name = temp["name"] | "";
|
const char *raw_name = temp["name"] | "";
|
||||||
const char *raw_group = temp["group"] | "";
|
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);
|
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") {
|
if (group == "outside") {
|
||||||
outside_status = "OUT ";
|
outside_status = "OUT ";
|
||||||
outside_status += String((int)round(temp_f));
|
outside_status += String((int)round(temp_f));
|
||||||
@@ -708,15 +724,12 @@ static void parse_status_json(const String &body)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = temp_group_index(group);
|
if (item_count < 4) {
|
||||||
if (index < 0 || index > 3) {
|
items[item_count] = {
|
||||||
index = 3;
|
group,
|
||||||
}
|
temp["priority"] | 99,
|
||||||
|
(int)round(temp_f)
|
||||||
int priority = temp["priority"] | 99;
|
};
|
||||||
|
|
||||||
if (item_count < 8) {
|
|
||||||
items[item_count] = {index, priority, (int)round(temp_f), name};
|
|
||||||
item_count++;
|
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 a = 0; a < item_count - 1; a++) {
|
||||||
for (int b = a + 1; b < item_count; b++) {
|
for (int b = a + 1; b < item_count; b++) {
|
||||||
if (
|
if (
|
||||||
items[b].tile_index < items[a].tile_index ||
|
items[b].priority < items[a].priority ||
|
||||||
(items[b].tile_index == items[a].tile_index && items[b].priority < items[a].priority)
|
(items[b].priority == items[a].priority && items[b].group < items[a].group)
|
||||||
) {
|
) {
|
||||||
TempDisplayItem tmp = items[a];
|
TempDisplayItem tmp = items[a];
|
||||||
items[a] = items[b];
|
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++) {
|
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);
|
String value = String(items[i].rounded_f);
|
||||||
value += "°";
|
value += "°";
|
||||||
|
|
||||||
if (tile_has_value[index]) {
|
if (group_values[slot].length() > 0) {
|
||||||
tile_values[index] += "/";
|
group_values[slot] += "/";
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group_values[slot] += value;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_outside_status_temp(outside_status);
|
update_outside_status_temp(outside_status);
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 2; 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_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], tile_names[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_align(battery_current_label, LV_ALIGN_TOP_MID, 0, 161);
|
||||||
lv_obj_add_flag(battery_current_label, LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(battery_current_label, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
const int temp_tile_w = 112;
|
const int temp_tile_w = 236;
|
||||||
const int temp_tile_h = 130;
|
const int temp_tile_h = 132;
|
||||||
const int temp_tile_x[4] = {8, 140, 8, 140};
|
const int temp_tile_y[2] = {18, 160};
|
||||||
const int temp_tile_y[4] = {18, 18, 160, 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_t *tile = lv_obj_create(temp_card);
|
||||||
lv_obj_set_size(tile, temp_tile_w, temp_tile_h);
|
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_align(tile, LV_ALIGN_TOP_MID, 0, temp_tile_y[i]);
|
||||||
lv_obj_set_style_radius(tile, 16, 0);
|
lv_obj_set_style_radius(tile, 18, 0);
|
||||||
lv_obj_set_style_bg_color(tile, lv_color_hex(0x17202A), 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_color(tile, lv_color_hex(0x2F3A46), 0);
|
||||||
lv_obj_set_style_border_width(tile, 2, 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);
|
temp_tile_value_labels[i] = lv_label_create(tile);
|
||||||
lv_label_set_text(temp_tile_value_labels[i], "--");
|
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_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_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 - 8);
|
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_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);
|
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_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_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_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);
|
lv_obj_t *vehicle_label = lv_label_create(vehicle_card);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#define MAX_RELAYS 2
|
#define MAX_RELAYS 2
|
||||||
#define MAX_TEMP_SENSORS 8
|
#define MAX_TEMP_SENSORS 4
|
||||||
#define HARDWARE_PROFILE "generic_esp32_2ch_relay"
|
#define HARDWARE_PROFILE "generic_esp32_2ch_relay"
|
||||||
|
|
||||||
struct RelayConfig {
|
struct RelayConfig {
|
||||||
|
|||||||
@@ -1184,13 +1184,14 @@ function renderConfigControls(data){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="inputHelp" for="tempGroup${i}">Group</label>
|
<label class="inputHelp" for="tempGroup${i}">Group</label>
|
||||||
<select id="tempGroup${i}" onchange="markConfigFieldTouched('tempGroup${i}')">
|
<input id="tempGroup${i}" list="tempGroupOptions" placeholder="cabin, fridge, etc." oninput="markConfigFieldTouched('tempGroup${i}')">
|
||||||
<option value="cabin">Cabin</option>
|
<datalist id="tempGroupOptions">
|
||||||
<option value="fridge">Fridge</option>
|
<option value="cabin">
|
||||||
<option value="outside">Outside</option>
|
<option value="fridge">
|
||||||
<option value="battery">Battery</option>
|
<option value="outside">
|
||||||
<option value="other">Other</option>
|
<option value="battery">
|
||||||
</select>
|
<option value="other">
|
||||||
|
</datalist>
|
||||||
|
|
||||||
<label class="inputHelp" for="tempPriority${i}">Priority</label>
|
<label class="inputHelp" for="tempPriority${i}">Priority</label>
|
||||||
<input id="tempPriority${i}" type="number" min="1" max="99" step="1" placeholder="1" oninput="markConfigFieldTouched('tempPriority${i}')">
|
<input id="tempPriority${i}" type="number" min="1" max="99" step="1" placeholder="1" oninput="markConfigFieldTouched('tempPriority${i}')">
|
||||||
|
|||||||
Reference in New Issue
Block a user