From 59c963e309b91a7cc89fec43053b3c4b66b91300 Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 4 Jun 2026 01:36:59 -0600 Subject: [PATCH] Add configuration HTTP API --- .../xterra-controller/xterra-controller.ino | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/firmware/esp32/xterra-controller/xterra-controller.ino b/firmware/esp32/xterra-controller/xterra-controller.ino index 3f2b6cf..a043dd4 100644 --- a/firmware/esp32/xterra-controller/xterra-controller.ino +++ b/firmware/esp32/xterra-controller/xterra-controller.ino @@ -316,6 +316,161 @@ void handleDebugSerial() { } } + +void sendConfigJson() { + DynamicJsonDocument doc(4096); + + doc["device_name"] = appConfig.deviceName; + + JsonArray relaysArray = doc.createNestedArray("relays"); + for (int i = 0; i < MAX_RELAYS; i++) { + JsonObject relay = relaysArray.createNestedObject(); + relay["id"] = appConfig.relays[i].id; + relay["name"] = appConfig.relays[i].name; + relay["pin"] = appConfig.relays[i].pin; + relay["enabled"] = appConfig.relays[i].enabled; + } + + JsonObject bms = doc.createNestedObject("bms"); + bms["enabled"] = appConfig.bms.enabled; + bms["name"] = appConfig.bms.name; + bms["address"] = appConfig.bms.address; + bms["address_type"] = appConfig.bms.addressType; + + JsonArray temps = doc.createNestedArray("temperature_sensors"); + for (int i = 0; i < MAX_TEMP_SENSORS; i++) { + JsonObject temp = temps.createNestedObject(); + temp["id"] = appConfig.tempSensors[i].id; + temp["name"] = appConfig.tempSensors[i].name; + temp["address"] = appConfig.tempSensors[i].address; + temp["enabled"] = appConfig.tempSensors[i].enabled; + } + + String output; + serializeJson(doc, output); + server.send(200, "application/json", output); +} + +void sendOkConfig() { + saveConfig(); + sendConfigJson(); +} + +int findRelayConfigIndex(const String& id) { + for (int i = 0; i < MAX_RELAYS; i++) { + if (appConfig.relays[i].id == id) { + return i; + } + } + return -1; +} + +int findTempSensorConfigIndex(const String& id) { + for (int i = 0; i < MAX_TEMP_SENSORS; i++) { + if (appConfig.tempSensors[i].id == id) { + return i; + } + } + return -1; +} + +void handleGetConfig() { + sendConfigJson(); +} + +void handleUpdateRelayConfig() { + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, server.arg("plain")); + + if (error) { + server.send(400, "application/json", "{\"ok\":false,\"error\":\"invalid_json\"}"); + return; + } + + String id = doc["id"] | ""; + int index = findRelayConfigIndex(id); + + if (index < 0) { + server.send(404, "application/json", "{\"ok\":false,\"error\":\"unknown_relay\"}"); + return; + } + + if (doc["name"].is()) { + appConfig.relays[index].name = doc["name"].as(); + } + + if (doc["enabled"].is()) { + appConfig.relays[index].enabled = doc["enabled"].as(); + } + + sendOkConfig(); +} + +void handleUpdateBmsConfig() { + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, server.arg("plain")); + + if (error) { + server.send(400, "application/json", "{\"ok\":false,\"error\":\"invalid_json\"}"); + return; + } + + if (doc["enabled"].is()) { + appConfig.bms.enabled = doc["enabled"].as(); + } + + if (doc["name"].is()) { + appConfig.bms.name = doc["name"].as(); + } + + if (doc["address"].is()) { + appConfig.bms.address = doc["address"].as(); + } + + if (doc["address_type"].is()) { + appConfig.bms.addressType = doc["address_type"].as(); + } + + sendOkConfig(); +} + +void handleUpdateTempSensorConfig() { + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, server.arg("plain")); + + if (error) { + server.send(400, "application/json", "{\"ok\":false,\"error\":\"invalid_json\"}"); + return; + } + + String id = doc["id"] | ""; + int index = findTempSensorConfigIndex(id); + + if (index < 0) { + server.send(404, "application/json", "{\"ok\":false,\"error\":\"unknown_temp_sensor\"}"); + return; + } + + if (doc["name"].is()) { + appConfig.tempSensors[index].name = doc["name"].as(); + } + + if (doc["address"].is()) { + appConfig.tempSensors[index].address = doc["address"].as(); + } + + if (doc["enabled"].is()) { + appConfig.tempSensors[index].enabled = doc["enabled"].as(); + } + + sendOkConfig(); +} + +void handleFactoryResetConfig() { + factoryResetConfig(); + sendConfigJson(); +} + void handleStatus() { DynamicJsonDocument doc(2048); buildStatusDocument(doc); @@ -399,6 +554,13 @@ void setup() { server.on("/status", handleStatus); + server.on("/config", HTTP_GET, handleGetConfig); + server.on("/config/relay", HTTP_POST, handleUpdateRelayConfig); + server.on("/config/bms", HTTP_POST, handleUpdateBmsConfig); + server.on("/config/temp", HTTP_POST, handleUpdateTempSensorConfig); + server.on("/config/factory-reset", HTTP_POST, handleFactoryResetConfig); + + server.on("/relay/starlink/on", handleStarlinkOn); server.on("/relay/starlink/off", handleStarlinkOff);