mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
Merge remote-tracking branch 'origin/main' into feat/esp32c5-build
This commit is contained in:
commit
247944bd2b
18 changed files with 198 additions and 39 deletions
|
|
@ -608,7 +608,9 @@ bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename,
|
|||
else {
|
||||
this->file.seek(this->file.position() + this->header.netRecordSize, SeekSet);
|
||||
}
|
||||
if(opts.shades) s->commit();
|
||||
// The repeaters are saved in shades.cfg too. Left to somfy.loop(), they
|
||||
// race the reboot.
|
||||
if(opts.shades || opts.repeaters) s->commit();
|
||||
if(opts.transceiver)
|
||||
{
|
||||
// Read into a copy so the radio task never transmits on a half restored config.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "Utils.h"
|
||||
#include "Secrets.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "Config";
|
||||
|
|
@ -149,6 +150,7 @@ bool ConfigSettings::begin() {
|
|||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Chip Model ESP32-%s", this->chipModel);
|
||||
this->hasOtaSlot = esp_ota_get_next_update_partition(nullptr) != nullptr;
|
||||
this->fwVersion.parse(FW_VERSION);
|
||||
uint64_t mac = ESP.getEfuseMac();
|
||||
for(int i=0; i<17; i=i+8) {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,9 @@ class ConfigSettings: BaseSettings {
|
|||
char serverId[10] = "";
|
||||
char hostname[32] = "ESPSomfyRTS";
|
||||
char chipModel[10] = "ESP32";
|
||||
// Whether the partition table has a second app slot for /updateFirmware
|
||||
// to write. Builds with one 3 MB slot (C6, debug) are flashed over USB.
|
||||
bool hasOtaSlot = true;
|
||||
conn_types_t connType = conn_types_t::unset;
|
||||
appver_t fwVersion;
|
||||
appver_t appVersion;
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@
|
|||
FIELD(hostname, "hn", 's', 0) \
|
||||
FIELD(model, "md", 's', 0) \
|
||||
FIELD(chipModel, "cp", 's', 0) \
|
||||
FIELD(hasOtaSlot, "os", 'b', 1) \
|
||||
FIELD(connType, "ct", 's', 0) \
|
||||
/* memory, wifi and ethernet */ \
|
||||
FIELD(freeHeap, "fr", 'i', 0) \
|
||||
|
|
|
|||
|
|
@ -93,7 +93,11 @@ void registerHttpRoutes(AsyncWebServer &server) {
|
|||
|
||||
server.on(AsyncURIMatcher::exact("/restore"), HTTP_POST,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
if(webServer.uploadSuccess) {
|
||||
// An upload is restored once: a later /restore without a file must not
|
||||
// find this one.
|
||||
const bool uploaded = webServer.uploadSuccess;
|
||||
webServer.uploadSuccess = false;
|
||||
if(uploaded) {
|
||||
restore_options_t opts;
|
||||
if(asyncHasParam(request, "data")) {
|
||||
String dataStr = asyncParam(request, "data");
|
||||
|
|
@ -101,6 +105,7 @@ void registerHttpRoutes(AsyncWebServer &server) {
|
|||
JsonDocument doc;
|
||||
DeserializationError err = deserializeJson(doc, dataStr);
|
||||
if(err) {
|
||||
LittleFS.remove("/shades.tmp");
|
||||
request->send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}");
|
||||
return;
|
||||
}
|
||||
|
|
@ -113,6 +118,13 @@ void registerHttpRoutes(AsyncWebServer &server) {
|
|||
ESP_LOGD(TAG, "No restore options sent. Using defaults...");
|
||||
opts.shades = true;
|
||||
}
|
||||
// Otherwise the device would reboot having restored nothing.
|
||||
const uint8_t parts = restoreParts(opts);
|
||||
if(parts == 0) {
|
||||
LittleFS.remove("/shades.tmp");
|
||||
request->send(400, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No part of the backup was chosen, so nothing was restored.\"}"));
|
||||
return;
|
||||
}
|
||||
// Checked here so the client hears about a bad file. It only reads
|
||||
// the file; the shades are still changed on loop() alone.
|
||||
if(!ShadeConfigFile::validateFile("/shades.tmp")) {
|
||||
|
|
@ -124,7 +136,7 @@ void registerHttpRoutes(AsyncWebServer &server) {
|
|||
// codes, and then reboots.
|
||||
loop_item_t item;
|
||||
item.type = loop_item_type_t::restore_shades;
|
||||
item.restoreParts = restoreParts(opts);
|
||||
item.restoreParts = parts;
|
||||
if(!queueLoopItem(request, item)) return;
|
||||
request->send(200, _encoding_json, "{\"status\":\"Success\",\"desc\":\"Restoring Shade settings\"}");
|
||||
}
|
||||
|
|
@ -152,15 +164,21 @@ void registerHttpRoutes(AsyncWebServer &server) {
|
|||
// updateFirmware - file upload
|
||||
server.on(AsyncURIMatcher::exact("/updateFirmware"), HTTP_POST,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
// Nothing was written or stopped, so there is nothing to reboot for.
|
||||
if(!settings.hasOtaSlot) {
|
||||
request->send(400, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"This device has no OTA slot, so its firmware is flashed over USB.\"}"));
|
||||
return;
|
||||
}
|
||||
// errorString() is one of the updater's fixed messages, none with a quote.
|
||||
if(Update.hasError())
|
||||
request->send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating firmware: \"}");
|
||||
request->send(500, _encoding_json, String("{\"status\":\"ERROR\",\"desc\":\"Error updating firmware: ") + Update.errorString() + "\"}");
|
||||
else
|
||||
request->send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated firmware\"}");
|
||||
rebootDelay.reboot = true;
|
||||
rebootDelay.rebootTime = millis() + 500;
|
||||
},
|
||||
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
if(!uploadAllowed(request, index)) return;
|
||||
if(!uploadAllowed(request, index) || !settings.hasOtaSlot) return;
|
||||
if(index == 0) {
|
||||
webServer.uploadSuccess = false;
|
||||
ESP_LOGI(TAG, "Update: %s", filename.c_str());
|
||||
|
|
@ -195,7 +213,7 @@ void registerHttpRoutes(AsyncWebServer &server) {
|
|||
server.on(AsyncURIMatcher::exact("/updateApplication"), HTTP_POST,
|
||||
[](AsyncWebServerRequest *request) {
|
||||
if(Update.hasError())
|
||||
request->send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating application: \"}");
|
||||
request->send(500, _encoding_json, String("{\"status\":\"ERROR\",\"desc\":\"Error updating application: ") + Update.errorString() + "\"}");
|
||||
else
|
||||
request->send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated application\"}");
|
||||
rebootDelay.reboot = true;
|
||||
|
|
|
|||
|
|
@ -143,13 +143,18 @@ bool Web::createAPIToken(const IPAddress ipAddress, char *token) {
|
|||
// =====================================================
|
||||
// Async API Handlers
|
||||
// =====================================================
|
||||
// Helper: get a query param as String, or empty if missing
|
||||
// The library keeps query parameters and form fields apart, and looks up only
|
||||
// the first unless asked. /restore's options come as a form field.
|
||||
static const AsyncWebParameter *queryOrFormParam(AsyncWebServerRequest *request, const char *name) {
|
||||
const AsyncWebParameter *p = request->getParam(name);
|
||||
return p ? p : request->getParam(name, true);
|
||||
}
|
||||
String asyncParam(AsyncWebServerRequest *request, const char *name) {
|
||||
if(request->hasParam(name)) return request->getParam(name)->value();
|
||||
return String();
|
||||
const AsyncWebParameter *p = queryOrFormParam(request, name);
|
||||
return p ? p->value() : String();
|
||||
}
|
||||
bool asyncHasParam(AsyncWebServerRequest *request, const char *name) {
|
||||
return request->hasParam(name);
|
||||
return queryOrFormParam(request, name) != nullptr;
|
||||
}
|
||||
|
||||
// -- Serialization helpers --
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ extern const char _encoding_json[];
|
|||
// Hands an item to loop(), which owns the shades. When it can't be the
|
||||
// request is answered with the reason and false is returned.
|
||||
bool queueLoopItem(AsyncWebServerRequest *request, const loop_item_t &item);
|
||||
// A query parameter, or an empty string when it is missing.
|
||||
// A query parameter or form field, or an empty string when it is missing.
|
||||
String asyncParam(AsyncWebServerRequest *request, const char *name);
|
||||
bool asyncHasParam(AsyncWebServerRequest *request, const char *name);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ static void writeHeader(ProtoWriter &w) {
|
|||
w.add(proto_field_t::hostname, settings.hostname);
|
||||
w.add(proto_field_t::model, "ESPSomfyRTS");
|
||||
w.add(proto_field_t::chipModel, settings.chipModel);
|
||||
w.add(proto_field_t::hasOtaSlot, settings.hasOtaSlot);
|
||||
w.add(proto_field_t::fw, settings.fwVersion.name);
|
||||
w.add(proto_field_t::connType, net.connTypeName());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue