dashboard: use status field filtering

This commit is contained in:
2026-06-09 23:17:06 -06:00
parent 626ebadc94
commit 8d59a72554
3 changed files with 127 additions and 96 deletions
+10
View File
@@ -65,3 +65,13 @@ Current dashboard firmware files:
- `esp_panel_board_custom_conf.h` - Waveshare board/display configuration
This refactor keeps behavior unchanged while preparing for later `api` and `ui` file splits.
## Status Field Filtering
The dashboard uses field-filtered `/api/v1/status` calls:
- Fast poll: `/api/v1/status?fields=battery,temps,relays`
- Slow poll: `/api/v1/status?fields=network,system,config`
This keeps the endpoint model simple while reducing payload size, JSON parsing, and UI churn.
@@ -1,10 +1,12 @@
#pragma once
static const char *DASHBOARD_VERSION = "0.0.8-refactor-config-model";
static const char *DASHBOARD_VERSION = "0.0.9-status-fields";
// Update these if your Cargo ESP AP credentials are different.
static const char *CARGO_WIFI_SSID = "OverlandController";
static const char *CARGO_WIFI_PASSWORD = "overland1234";
static const char *CARGO_API_STATUS_URL = "http://192.168.4.1/api/v1/status";
static const char *CARGO_API_FAST_STATUS_URL = "http://192.168.4.1/api/v1/status?fields=battery,temps,relays";
static const char *CARGO_API_SLOW_STATUS_URL = "http://192.168.4.1/api/v1/status?fields=network,system,config";
static const char *CARGO_API_RELAY_SET_URL = "http://192.168.4.1/api/v1/relay/set";
@@ -29,6 +29,7 @@ static uint32_t touch_count = 0;
static unsigned long last_wifi_attempt_ms = 0;
static unsigned long last_wifi_label_update_ms = 0;
static unsigned long last_status_poll_ms = 0;
static unsigned long last_metadata_poll_ms = 0;
static String last_battery_text;
static String last_temps_text;
@@ -264,33 +265,42 @@ static void parse_status_json(const String &body)
status_model.api_ok = true;
status_model.http_code = 200;
JsonObject battery = doc["battery"];
status_model.bms_connected = battery["connected"] | false;
status_model.soc = battery["soc"] | -1;
status_model.voltage = battery["voltage"] | 0.0;
status_model.current = battery["current"] | 0.0;
status_model.battery_temp_f = battery["temperature_f"] | 0.0;
status_model.remaining_ah = battery["remaining_ah"] | 0.0;
status_model.capacity_ah = battery["capacity_ah"] | 0.0;
status_model.cell_delta_mv = battery["cell_delta_mv"] | -1;
if (doc["battery"].is<JsonObject>()) {
JsonObject battery = doc["battery"];
status_model.bms_connected = battery["connected"] | status_model.bms_connected;
status_model.soc = battery["soc"] | status_model.soc;
status_model.voltage = battery["voltage"] | status_model.voltage;
status_model.current = battery["current"] | status_model.current;
status_model.battery_temp_f = battery["temperature_f"] | status_model.battery_temp_f;
status_model.remaining_ah = battery["remaining_ah"] | status_model.remaining_ah;
status_model.capacity_ah = battery["capacity_ah"] | status_model.capacity_ah;
status_model.cell_delta_mv = battery["cell_delta_mv"] | status_model.cell_delta_mv;
}
JsonObject config = doc["config"];
status_model.hardware_profile = String((const char *)(config["hardware_profile"] | "unknown"));
status_model.output_count = config["output_count"] | 0;
if (doc["config"].is<JsonObject>()) {
JsonObject config = doc["config"];
status_model.hardware_profile = String((const char *)(config["hardware_profile"] | status_model.hardware_profile.c_str()));
status_model.output_count = config["output_count"] | status_model.output_count;
}
JsonObject system = doc["system"];
status_model.firmware_version = String((const char *)(system["firmware_version"] | "unknown"));
status_model.uptime_seconds = system["uptime_seconds"] | 0;
if (doc["system"].is<JsonObject>()) {
JsonObject system = doc["system"];
status_model.firmware_version = String((const char *)(system["firmware_version"] | status_model.firmware_version.c_str()));
status_model.uptime_seconds = system["uptime_seconds"] | status_model.uptime_seconds;
}
JsonObject network = doc["network"];
status_model.cargo_ap_ssid = String((const char *)(network["ap_ssid"] | "unknown"));
status_model.cargo_sta_ip = String((const char *)(network["sta_ip"] | "n/a"));
if (doc["network"].is<JsonObject>()) {
JsonObject network = doc["network"];
status_model.cargo_ap_ssid = String((const char *)(network["ap_ssid"] | status_model.cargo_ap_ssid.c_str()));
status_model.cargo_sta_ip = String((const char *)(network["sta_ip"] | status_model.cargo_sta_ip.c_str()));
}
String temps = "";
JsonArray temp_array = doc["temps"];
int shown_temps = 0;
if (doc["temps"].is<JsonArray>()) {
String temps = "";
JsonArray temp_array = doc["temps"];
int shown_temps = 0;
for (JsonObject temp : temp_array) {
for (JsonObject temp : temp_array) {
bool enabled = temp["enabled"] | false;
bool online = temp["online"] | false;
@@ -318,21 +328,25 @@ static void parse_status_json(const String &body)
temps += "\n";
shown_temps++;
if (shown_temps >= 5) {
break;
if (shown_temps >= 5) {
break;
}
}
if (temps.length() == 0) {
temps = "No temp sensors reported";
}
status_model.temps_text = temps;
}
if (temps.length() == 0) {
temps = "No temp sensors reported";
}
if (doc["relays"].is<JsonArray>()) {
String outputs = "";
JsonArray relay_array = doc["relays"];
String outputs = "";
JsonArray relay_array = doc["relays"];
relay_count = 0;
relay_count = 0;
for (JsonObject relay : relay_array) {
for (JsonObject relay : relay_array) {
const char *id = relay["id"] | "";
const char *name = relay["name"] | relay["id"] | "Output";
bool enabled = relay["enabled"] | false;
@@ -360,18 +374,17 @@ static void parse_status_json(const String &body)
outputs += on_off(state);
}
outputs += "\n";
outputs += "\n";
}
if (outputs.length() == 0) {
outputs = "No outputs reported";
}
status_model.outputs_text = outputs;
update_relay_buttons();
}
if (outputs.length() == 0) {
outputs = "No outputs reported";
}
status_model.temps_text = temps;
status_model.outputs_text = outputs;
update_relay_buttons();
Serial.println("Parsed status summary:");
Serial.print(" SOC: ");
Serial.println(status_model.soc);
@@ -448,6 +461,58 @@ static void update_wifi_label()
set_label_text_if_changed(wifi_label, last_wifi_text, text);
}
static bool fetch_status_fields(const char *url, const char *label)
{
if (WiFi.status() != WL_CONNECTED) {
set_label_text_if_changed(system_label, last_system_text, "API: waiting for WiFi");
return false;
}
Serial.print("GET ");
Serial.println(url);
HTTPClient http;
http.setTimeout(5000);
http.begin(url);
int code = http.GET();
status_model.http_code = code;
Serial.print(label);
Serial.print(" HTTP code: ");
Serial.println(code);
if (code == 200) {
String body = http.getString();
Serial.print(label);
Serial.print(" body length: ");
Serial.println(body.length());
parse_status_json(body);
http.end();
return true;
}
status_model.api_ok = false;
String system;
system += "API: error\n";
system += label;
system += "\nHTTP: ";
system += String(code);
if (code <= 0) {
system += "\n";
system += http.errorToString(code);
}
Serial.println(system);
set_label_text_if_changed(system_label, last_system_text, system);
http.end();
return false;
}
static void poll_status_api()
{
if (WiFi.status() != WL_CONNECTED) {
@@ -455,61 +520,15 @@ static void poll_status_api()
return;
}
if (millis() - last_status_poll_ms < 5000) {
return;
if (millis() - last_status_poll_ms >= 2000) {
last_status_poll_ms = millis();
fetch_status_fields(CARGO_API_FAST_STATUS_URL, "fast status");
}
last_status_poll_ms = millis();
String requesting;
requesting += "API: requesting\n";
requesting += "/api/v1/status\n";
requesting += "Dashboard IP:\n";
requesting += WiFi.localIP().toString();
set_label_text_if_changed(system_label, last_system_text, requesting);
Serial.print("GET ");
Serial.println(CARGO_API_STATUS_URL);
HTTPClient http;
http.setTimeout(5000);
http.begin(CARGO_API_STATUS_URL);
int code = http.GET();
status_model.http_code = code;
Serial.print("HTTP code: ");
Serial.println(code);
if (code == 200) {
String body = http.getString();
Serial.print("Body length: ");
Serial.println(body.length());
Serial.println(body);
String received;
received += "API: received\nHTTP: 200\nBytes: ";
received += String(body.length());
set_label_text_if_changed(system_label, last_system_text, received);
parse_status_json(body);
} else {
status_model.api_ok = false;
String system;
system += "API: error\nHTTP: ";
system += String(code);
if (code <= 0) {
system += "\n";
system += http.errorToString(code);
}
Serial.println(system);
set_label_text_if_changed(system_label, last_system_text, system);
if (millis() - last_metadata_poll_ms >= 30000) {
last_metadata_poll_ms = millis();
fetch_status_fields(CARGO_API_SLOW_STATUS_URL, "slow status");
}
http.end();
}
static lv_obj_t *create_card(lv_obj_t *parent, const char *title, int width, int height, lv_align_t align, int x, int y)
@@ -624,7 +643,7 @@ static void create_overland_status_screen()
lv_obj_align(touch_count_label, LV_ALIGN_TOP_LEFT, 0, 142);
lv_obj_t *footer = lv_label_create(screen);
lv_label_set_text(footer, "Polling Cargo ESP /api/v1/status every 5 seconds");
lv_label_set_text(footer, "Fast: battery/temps/relays every 2s | Slow: system/config/network every 30s");
lv_obj_set_style_text_color(footer, lv_color_hex(0x7D8996), 0);
lv_obj_set_width(footer, 900);
lv_obj_set_style_text_align(footer, LV_TEXT_ALIGN_CENTER, 0);