dashboard: add temperature group priority

This commit is contained in:
2026-06-11 00:28:31 -06:00
parent 049d64edc2
commit 5e2f2080cb
8 changed files with 101 additions and 12 deletions
+11
View File
@@ -944,3 +944,14 @@ Supported group values:
- `other` - `other`
The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display. The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display.
### Temperature Sensor Priority
Temperature sensors support a numeric `priority` field. Lower numbers are displayed first within a grouped dashboard tile.
Example:
- Fridge Zone 1: `group=fridge`, `priority=1`
- Fridge Zone 2: `group=fridge`, `priority=2`
The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`.
+11
View File
@@ -262,3 +262,14 @@ Supported group values:
- `other` - `other`
The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display. The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display.
### Temperature Sensor Priority
Temperature sensors support a numeric `priority` field. Lower numbers are displayed first within a grouped dashboard tile.
Example:
- Fridge Zone 1: `group=fridge`, `priority=1`
- Fridge Zone 2: `group=fridge`, `priority=2`
The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`.
+11
View File
@@ -329,3 +329,14 @@ Supported group values:
- `other` - `other`
The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display. The dashboard overview combines multiple online sensors in the same group into one tile. For example, two fridge sensors may display as `34°/45°` under a single `Fridge` tile. Sensors marked `weather: true` continue to be treated as the outside/weather sensor for dashboard display.
### Temperature Sensor Priority
Temperature sensors support a numeric `priority` field. Lower numbers are displayed first within a grouped dashboard tile.
Example:
- Fridge Zone 1: `group=fridge`, `priority=1`
- Fridge Zone 2: `group=fridge`, `priority=2`
The dashboard displays the grouped fridge tile in priority order, for example `34°/45°`.
@@ -1,6 +1,6 @@
#pragma once #pragma once
static const char *DASHBOARD_VERSION = "0.1.38-outside-status-temp"; static const char *DASHBOARD_VERSION = "0.1.39-temp-priority";
// 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";
@@ -667,6 +667,16 @@ static void parse_status_json(const String &body)
bool tile_has_value[4] = {false, false, false, false}; bool tile_has_value[4] = {false, false, false, false};
String outside_status = "OUT --"; String outside_status = "OUT --";
struct TempDisplayItem {
int tile_index;
int priority;
int rounded_f;
String name;
};
TempDisplayItem items[8];
int item_count = 0;
for (JsonObject temp : doc["temps"].as<JsonArray>()) { for (JsonObject temp : doc["temps"].as<JsonArray>()) {
bool enabled = temp["enabled"] | false; bool enabled = temp["enabled"] | false;
bool online = temp["online"] | false; bool online = temp["online"] | false;
@@ -691,9 +701,6 @@ static void parse_status_json(const String &body)
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);
// Weather-tagged sensors are excluded above. A manually configured outside
// group can still be shown later on a dedicated Temps page, but the main
// overview reserves the top status strip for outside/weather temp.
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));
@@ -702,12 +709,34 @@ static void parse_status_json(const String &body)
} }
int index = temp_group_index(group); int index = temp_group_index(group);
if (index == 2) { if (index < 0 || index > 3) {
index = 3; // Keep main temp card from using an Outside tile. index = 3;
} }
int rounded = (int)round(temp_f); int priority = temp["priority"] | 99;
String value = String(rounded);
if (item_count < 8) {
items[item_count] = {index, priority, (int)round(temp_f), name};
item_count++;
}
}
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)
) {
TempDisplayItem tmp = items[a];
items[a] = items[b];
items[b] = tmp;
}
}
}
for (int i = 0; i < item_count; i++) {
int index = items[i].tile_index;
String value = String(items[i].rounded_f);
value += "°"; value += "°";
if (tile_has_value[index]) { if (tile_has_value[index]) {
@@ -718,6 +747,7 @@ static void parse_status_json(const String &body)
tile_has_value[index] = true; tile_has_value[index] = true;
if (index == 0) { if (index == 0) {
String name = items[i].name;
name.replace(" Area", ""); name.replace(" Area", "");
name.replace(" Zone", ""); name.replace(" Zone", "");
tile_names[index] = name.length() ? name : "Cabin"; tile_names[index] = name.length() ? name : "Cabin";
@@ -39,7 +39,8 @@ void loadDefaultConfig() {
"", "",
i < appConfig.tempSensorCount, i < appConfig.tempSensorCount,
false, false,
"cabin" "cabin",
i + 1
}; };
} }
@@ -106,6 +107,10 @@ void loadConfig() {
(prefix + "group").c_str(), (prefix + "group").c_str(),
appConfig.tempSensors[i].group appConfig.tempSensors[i].group
); );
appConfig.tempSensors[i].priority = preferences.getInt(
(prefix + "priority").c_str(),
appConfig.tempSensors[i].priority
);
} }
appConfig.bms.enabled = preferences.getBool("bms_enabled", appConfig.bms.enabled); appConfig.bms.enabled = preferences.getBool("bms_enabled", appConfig.bms.enabled);
@@ -142,6 +147,7 @@ void saveConfig() {
preferences.putBool((prefix + "enabled").c_str(), appConfig.tempSensors[i].enabled); preferences.putBool((prefix + "enabled").c_str(), appConfig.tempSensors[i].enabled);
preferences.putBool((prefix + "weather").c_str(), appConfig.tempSensors[i].weather); preferences.putBool((prefix + "weather").c_str(), appConfig.tempSensors[i].weather);
preferences.putString((prefix + "group").c_str(), appConfig.tempSensors[i].group); preferences.putString((prefix + "group").c_str(), appConfig.tempSensors[i].group);
preferences.putInt((prefix + "priority").c_str(), appConfig.tempSensors[i].priority);
} }
preferences.putBool("bms_enabled", appConfig.bms.enabled); preferences.putBool("bms_enabled", appConfig.bms.enabled);
@@ -195,7 +201,9 @@ void printConfig() {
Serial.print(" / address "); Serial.print(" / address ");
Serial.print(appConfig.tempSensors[i].address); Serial.print(appConfig.tempSensors[i].address);
Serial.print(" / group "); Serial.print(" / group ");
Serial.println(appConfig.tempSensors[i].group); Serial.print(appConfig.tempSensors[i].group);
Serial.print(" / priority ");
Serial.println(appConfig.tempSensors[i].priority);
} }
Serial.println(" BMS:"); Serial.println(" BMS:");
@@ -23,6 +23,7 @@ struct TempSensorConfig {
bool enabled; bool enabled;
bool weather; bool weather;
String group; String group;
int priority;
}; };
struct BmsConfig { struct BmsConfig {
@@ -1192,6 +1192,9 @@ function renderConfigControls(data){
<option value="other">Other</option> <option value="other">Other</option>
</select> </select>
<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}')">
<div class="tempConfigControls"> <div class="tempConfigControls">
<label class="tempCheck"> <label class="tempCheck">
<input id="tempEnabled${i}" type="checkbox" onchange="markConfigFieldTouched('tempEnabled${i}'); saveTempConfig()"> <input id="tempEnabled${i}" type="checkbox" onchange="markConfigFieldTouched('tempEnabled${i}'); saveTempConfig()">
@@ -1225,6 +1228,11 @@ function renderConfigControls(data){
if(group && !configTouched["tempGroup"+i] && document.activeElement!==group){ if(group && !configTouched["tempGroup"+i] && document.activeElement!==group){
group.value=t.group||"cabin"; group.value=t.group||"cabin";
} }
const priority=$("tempPriority"+i);
if(priority && !configTouched["tempPriority"+i] && document.activeElement!==priority){
priority.value=t.priority||i+1;
}
}); });
} }
@@ -1268,6 +1276,7 @@ async function saveTempConfig(){
const enabled=$("tempEnabled"+i)?.checked || false; const enabled=$("tempEnabled"+i)?.checked || false;
const weather=$("tempWeather"+i)?.checked || false; const weather=$("tempWeather"+i)?.checked || false;
const group=$("tempGroup"+i)?.value || temps[i].group || "cabin"; const group=$("tempGroup"+i)?.value || temps[i].group || "cabin";
const priority=parseInt($("tempPriority"+i)?.value || temps[i].priority || (i+1),10);
await fetch(api("/config/temp"),{ await fetch(api("/config/temp"),{
method:"POST", method:"POST",
@@ -1278,7 +1287,8 @@ async function saveTempConfig(){
address:temps[i].address||"", address:temps[i].address||"",
enabled:enabled, enabled:enabled,
weather:weather, weather:weather,
group:group group:group,
priority:priority
}) })
}); });
} }
@@ -1288,7 +1298,7 @@ async function saveTempConfig(){
alert("Temperature config saved"); alert("Temperature config saved");
clearConfigTouched([ clearConfigTouched([
"tempEnabledCount", "tempEnabledCount",
...temps.flatMap((_,i)=>["tempName"+i,"tempEnabled"+i,"tempWeather"+i,"tempGroup"+i]) ...temps.flatMap((_,i)=>["tempName"+i,"tempEnabled"+i,"tempWeather"+i,"tempGroup"+i,"tempPriority"+i])
]); ]);
} }
@@ -2017,6 +2027,7 @@ void buildConfigDocument(JsonObject doc) {
temp["enabled"] = appConfig.tempSensors[i].enabled; temp["enabled"] = appConfig.tempSensors[i].enabled;
temp["weather"] = appConfig.tempSensors[i].weather; temp["weather"] = appConfig.tempSensors[i].weather;
temp["group"] = appConfig.tempSensors[i].group; temp["group"] = appConfig.tempSensors[i].group;
temp["priority"] = appConfig.tempSensors[i].priority;
} }
} }
@@ -2211,6 +2222,7 @@ void applyImportedConfig(JsonObject doc) {
if (temp["enabled"].is<bool>()) appConfig.tempSensors[index].enabled = temp["enabled"].as<bool>(); if (temp["enabled"].is<bool>()) appConfig.tempSensors[index].enabled = temp["enabled"].as<bool>();
if (temp["weather"].is<bool>()) appConfig.tempSensors[index].weather = temp["weather"].as<bool>(); if (temp["weather"].is<bool>()) appConfig.tempSensors[index].weather = temp["weather"].as<bool>();
if (temp["group"].is<String>()) appConfig.tempSensors[index].group = temp["group"].as<String>(); if (temp["group"].is<String>()) appConfig.tempSensors[index].group = temp["group"].as<String>();
if (temp["priority"].is<int>()) appConfig.tempSensors[index].priority = temp["priority"].as<int>();
} }
} }
@@ -2317,6 +2329,7 @@ void buildStatusDocument(JsonDocument& doc) {
temp["enabled"] = appConfig.tempSensors[i].enabled; temp["enabled"] = appConfig.tempSensors[i].enabled;
temp["weather"] = appConfig.tempSensors[i].weather; temp["weather"] = appConfig.tempSensors[i].weather;
temp["group"] = appConfig.tempSensors[i].group; temp["group"] = appConfig.tempSensors[i].group;
temp["priority"] = appConfig.tempSensors[i].priority;
temp["online"] = sensors.online[i]; temp["online"] = sensors.online[i];
if (sensors.online[i]) { if (sensors.online[i]) {
temp["temperature_f"] = sensors.tempsF[i]; temp["temperature_f"] = sensors.tempsF[i];
@@ -2462,6 +2475,7 @@ void sendConfigResponse(Stream& output, bool ok = true) {
temp["enabled"] = appConfig.tempSensors[i].enabled; temp["enabled"] = appConfig.tempSensors[i].enabled;
temp["weather"] = appConfig.tempSensors[i].weather; temp["weather"] = appConfig.tempSensors[i].weather;
temp["group"] = appConfig.tempSensors[i].group; temp["group"] = appConfig.tempSensors[i].group;
temp["priority"] = appConfig.tempSensors[i].priority;
} }
serializeJson(doc, output); serializeJson(doc, output);
@@ -3444,6 +3458,7 @@ void handleUpdateTempSensorConfig() {
if (doc["enabled"].is<bool>()) appConfig.tempSensors[index].enabled = doc["enabled"].as<bool>(); if (doc["enabled"].is<bool>()) appConfig.tempSensors[index].enabled = doc["enabled"].as<bool>();
if (doc["weather"].is<bool>()) appConfig.tempSensors[index].weather = doc["weather"].as<bool>(); if (doc["weather"].is<bool>()) appConfig.tempSensors[index].weather = doc["weather"].as<bool>();
if (doc["group"].is<String>()) appConfig.tempSensors[index].group = doc["group"].as<String>(); if (doc["group"].is<String>()) appConfig.tempSensors[index].group = doc["group"].as<String>();
if (doc["priority"].is<int>()) appConfig.tempSensors[index].priority = doc["priority"].as<int>();
sendOkConfig(); sendOkConfig();
} }
@@ -3585,6 +3600,7 @@ void handleTempClear() {
appConfig.tempSensors[i].enabled = false; appConfig.tempSensors[i].enabled = false;
appConfig.tempSensors[i].weather = false; appConfig.tempSensors[i].weather = false;
appConfig.tempSensors[i].group = "cabin"; appConfig.tempSensors[i].group = "cabin";
appConfig.tempSensors[i].priority = i + 1;
sensors.tempsF[i] = -127.0; sensors.tempsF[i] = -127.0;
sensors.online[i] = false; sensors.online[i] = false;
} }
@@ -3622,6 +3638,7 @@ void handleTempClear() {
appConfig.tempSensors[configIndex].enabled = false; appConfig.tempSensors[configIndex].enabled = false;
appConfig.tempSensors[configIndex].weather = false; appConfig.tempSensors[configIndex].weather = false;
appConfig.tempSensors[configIndex].group = "cabin"; appConfig.tempSensors[configIndex].group = "cabin";
appConfig.tempSensors[configIndex].priority = configIndex + 1;
sensors.tempsF[configIndex] = -127.0; sensors.tempsF[configIndex] = -127.0;
sensors.online[configIndex] = false; sensors.online[configIndex] = false;