mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 13:52:15 +01:00
The 19 routes that changed rooms, shades, groups and repeaters on the web
server task (add, save, delete, link and unlink, setPaired, the sort
orders) now copy their request into a model_edit_t, queue it on the
LoopQueue and reply {status:accepted,requestId:N}. loop() applies it
(SomfyJson.cpp, next to fromJSON) and broadcasts requestResult {requestId,
ok, id, code, desc}. One edit at a time; a second gets 503, a body over
4 KB gets 413.
ModelEditor.h/.cpp holds the rules, tested natively: the lowest free id,
the next sort order, a free remote address, adding to and removing from
the group-member and repeater lists, and the GPIO pin-conflict check. They
replace the getNext*Id/getMax*Order copies, the remote-address search,
the list code and the inline pin checks. The pin check now uses the
protocol being saved and checks every pin against Ethernet, not only up.
/getNextRoom, /getNextShade, /getNextGroup, /groupOptions and a new
/repeaters (the UI already called it) are ModelSnapshot reads.
The web UI's editJSON() sends an edit, waits for its requestResult, then
reads the item back from /shade, /group, /room or /repeaters, so the
callbacks that used the old reply bodies are unchanged. No result within
5 s offers a reload; reads and edits told Busy are retried. It now also
refreshes its lists on shadeAdded/shadeRemoved/groupAdded/groupRemoved
from other clients, and procRoomRemoved keeps the right rooms.
Also fixed: a shade that fails validation is no longer added; linked
repeaters and sort orders are saved; linking reports a full list.
27 new native tests, 319 in all. Flash: esp32devdbg -11,484 bytes,
esp32dev -7,180 bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75 lines
2.5 KiB
C++
75 lines
2.5 KiB
C++
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/queue.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);
|
|
ShadeConfigFile::restore(&somfy, "/shades.tmp", opts);
|
|
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");
|
|
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;
|
|
}
|
|
}
|
|
}
|