mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
NTPSettings::apply() waited up to 5 s in getLocalTime() for a clock that can't be set before the network is up, then returned before it set TZ. Every cold boot was 5 s slower and ran without its time zone. It now calls configTzTime(), which sets the zone and starts SNTP without waiting, and save() goes through apply(). /setgeneral checked ntpServer twice, so a request that changed only the zone was ignored. A restore ignored the result of ShadeConfigFile::restore() and rebooted either way, so a bad file failed with no word to the user. The /restore reply now validates the file and answers 400, which the restore dialog shows, and an incomplete upload gets a 400 instead of no reply. A failed restore changes nothing, so loop() no longer reboots after one. /shades.tmp, a full copy of the backup with its rolling codes, is deleted after every restore and after /updateShadeConfig. The 6 extra bytes in the 2026-10-18 backup: TRANS_REC_SIZE stayed 74 when noiseDetection was added to the 80-byte transceiver record, so every backup since declares it short. It is 80 now. The size check stays a warning so those backups still restore, and the sum moves to expectedFileBytes() with tests. The repeater record length is checked as a warning only, since validate() also runs on shades.cfg at boot. Commands for an unknown shade, tilt or group now log a warning, and a socket that never pinged says so. KNOWN_ISSUES.md is updated, including the real cause of the ~365 ms loop stall (radio_tx busy-waits each frame on loop()'s core). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
2.9 KiB
C++
85 lines
2.9 KiB
C++
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/queue.h>
|
|
#include <LittleFS.h>
|
|
#include "esp_log.h"
|
|
#include "LoopQueue.h"
|
|
#include "ConfigFile.h"
|
|
#include "Somfy.h"
|
|
#include "SomfySnapshot.h"
|
|
#include "SomfyEditor.h"
|
|
#include "Utils.h"
|
|
|
|
static const char *TAG = "LoopQueue";
|
|
|
|
extern SomfyShadeController somfy;
|
|
extern ShadeRequests shadeRequests;
|
|
extern SnapshotSlots snapshotSlots;
|
|
extern rebootDelay_t rebootDelay;
|
|
|
|
static restore_options_t restoreOptions(uint8_t parts) {
|
|
restore_options_t opts;
|
|
opts.settings = parts & RESTORE_SETTINGS;
|
|
opts.shades = parts & RESTORE_SHADES;
|
|
opts.network = parts & RESTORE_NETWORK;
|
|
opts.transceiver = parts & RESTORE_TRANSCEIVER;
|
|
opts.repeaters = parts & RESTORE_REPEATERS;
|
|
opts.mqtt = parts & RESTORE_MQTT;
|
|
return opts;
|
|
}
|
|
|
|
void LoopQueue::begin() {
|
|
if(!this->queue) this->queue = xQueueCreate(LOOP_QUEUE_SIZE, sizeof(loop_item_t));
|
|
}
|
|
bool LoopQueue::submit(const loop_item_t &item) {
|
|
if(!this->queue) return false;
|
|
// async_tcp also services the sockets, so it must never wait on loop() or
|
|
// the radio.
|
|
if(xQueueSend(static_cast<QueueHandle_t>(this->queue), &item, 0) != pdTRUE) {
|
|
ESP_LOGE(TAG, "Loop queue full, dropping item");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
void LoopQueue::drain() {
|
|
if(!this->queue) return;
|
|
QueueHandle_t q = static_cast<QueueHandle_t>(this->queue);
|
|
loop_item_t item;
|
|
// Items run in order, so only the one at the head can be held back.
|
|
while(xQueuePeek(q, &item, 0) == pdTRUE) {
|
|
if(holdLoopItem(item, somfy.transceiver.txSuspended(), somfy.transceiver.txHasRoom())) break;
|
|
// loop() is the only consumer so this is the item we peeked.
|
|
xQueueReceive(q, &item, 0);
|
|
switch(item.type) {
|
|
case loop_item_type_t::shade_request:
|
|
if(!shadeRequests.run(item.request))
|
|
ESP_LOGW(TAG, "Radio didn't take shade request %u", static_cast<uint8_t>(item.request.type));
|
|
break;
|
|
case loop_item_type_t::restore_shades: {
|
|
restore_options_t opts = restoreOptions(item.restoreParts);
|
|
const bool restored = ShadeConfigFile::restore(&somfy, "/shades.tmp", opts);
|
|
// It holds the whole backup, rolling codes included, and has no use now.
|
|
LittleFS.remove("/shades.tmp");
|
|
// A failed restore stops before it changes anything, so there is
|
|
// nothing to reboot for.
|
|
if(!restored) {
|
|
ESP_LOGE(TAG, "Restore failed, keeping the current settings");
|
|
break;
|
|
}
|
|
ESP_LOGI(TAG, "Rebooting ESP for restored settings...");
|
|
rebootDelay.reboot = true;
|
|
rebootDelay.rebootTime = millis() + 1000;
|
|
break;
|
|
}
|
|
case loop_item_type_t::load_shades:
|
|
somfy.loadShadesFile("/shades.tmp");
|
|
LittleFS.remove("/shades.tmp");
|
|
break;
|
|
case loop_item_type_t::snapshot_fill:
|
|
snapshotSlots.fill(item.slot, fillSnapshot);
|
|
break;
|
|
case loop_item_type_t::model_edit:
|
|
runModelEdit(item.edit);
|
|
break;
|
|
}
|
|
}
|
|
}
|