From ea520731a90e3067d3cdac8891aaf36f2eafa7ad Mon Sep 17 00:00:00 2001 From: cjkas Date: Tue, 13 Oct 2026 15:07:20 +0200 Subject: [PATCH] fix: unpair without changing the shade on the web server task queueUnpairShade cleared shade->paired on the async_tcp task while loop() owns the shade. A second unpair queued before the first ran recorded wasPaired=false and, if its Prog was refused, put back the wrong value. The shade is now only changed by loop() once the Prog is queued, and the response reports paired=false through a serializeShade overload instead. No native test: Web.cpp can't be built by the native env. Co-Authored-By: Claude Opus 5.5 --- src/Web.cpp | 25 ++++++++++--------------- src/WebCommand.h | 1 - 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Web.cpp b/src/Web.cpp index ac3a918..0250f9d 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -69,16 +69,13 @@ static bool queueSetMyPosition(AsyncWebServerRequest *request, SomfyShade *shade cmd.tiltPosition = tilt; return queueRadioCommand(request, cmd); } +// The main loop clears paired and saves it once the Prog is queued. The +// shade belongs to the main loop so it isn't changed here. static bool queueUnpairShade(AsyncWebServerRequest *request, SomfyShade *shade) { web_command_t cmd = {}; cmd.type = web_cmd_t::unpair_shade; cmd.shadeId = shade->getShadeId(); - cmd.wasPaired = shade->paired; - if(!queueRadioCommand(request, cmd)) return false; - // Reflect the change in the response. The main loop saves it once the Prog - // is queued or puts it back if it can't be. - shade->paired = false; - return true; + return queueRadioCommand(request, cmd); } // The radio and MQTT are shut down once an upload starts and only a reboot // brings them back. The completion handler schedules it but never runs if the @@ -220,11 +217,6 @@ void Web::processQueue() { shade->paired = false; shade->save(); } - else { - // The motor is still paired so undo what the response showed. - shade->paired = cmd.wasPaired; - shade->emitState(); - } } break; } @@ -318,14 +310,16 @@ static void serializeShadeRef(SomfyShade *shade, JsonFormatter &json) { json.addElem("hasLight", shade->hasLight()); json.addElem("repeats", shade->repeats); } -static void serializeShade(SomfyShade *shade, JsonFormatter &json) { +// paired is passed in for a response that reports the change the main loop +// is about to make. +static void serializeShade(SomfyShade *shade, JsonFormatter &json, bool paired) { json.addElem("shadeId", shade->getShadeId()); json.addElem("roomId", shade->roomId); json.addElem("name", shade->name); json.addElem("remoteAddress", (uint32_t)shade->getRemoteAddress()); json.addElem("upTime", (uint32_t)shade->upTime); json.addElem("downTime", (uint32_t)shade->downTime); - json.addElem("paired", shade->paired); + json.addElem("paired", paired); json.addElem("lastRollingCode", (uint32_t)shade->lastRollingCode); json.addElem("position", shade->transformPosition(shade->currentPos)); json.addElem("tiltType", static_cast(shade->tiltType)); @@ -366,6 +360,7 @@ static void serializeShade(SomfyShade *shade, JsonFormatter &json) { } json.endArray(); } +static void serializeShade(SomfyShade *shade, JsonFormatter &json) { serializeShade(shade, json, shade->paired); } static void serializeGroupRef(SomfyGroup *group, JsonFormatter &json) { group->updateFlags(); json.addElem("groupId", group->getGroupId()); @@ -1900,7 +1895,7 @@ void Web::begin() { AsyncJsonResp resp; resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - serializeShade(shade, resp); + serializeShade(shade, resp, false); resp.endObject(); resp.endResponse(); } @@ -1919,7 +1914,7 @@ void Web::begin() { AsyncJsonResp resp; resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - serializeShade(shade, resp); + serializeShade(shade, resp, false); resp.endObject(); resp.endResponse(); } diff --git a/src/WebCommand.h b/src/WebCommand.h index 0928791..e1c473c 100644 --- a/src/WebCommand.h +++ b/src/WebCommand.h @@ -36,7 +36,6 @@ struct web_command_t { int8_t tiltPosition; // for setPositions/tilt int8_t sunny; // for sensor int8_t windy; // for sensor - bool wasPaired; // for unpair, restored if the Prog can't be sent }; // Only setting positions never goes on air.