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 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-13 15:07:20 +02:00
parent 45776f64ce
commit ea520731a9
2 changed files with 10 additions and 16 deletions

View file

@ -69,16 +69,13 @@ static bool queueSetMyPosition(AsyncWebServerRequest *request, SomfyShade *shade
cmd.tiltPosition = tilt; cmd.tiltPosition = tilt;
return queueRadioCommand(request, cmd); 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) { static bool queueUnpairShade(AsyncWebServerRequest *request, SomfyShade *shade) {
web_command_t cmd = {}; web_command_t cmd = {};
cmd.type = web_cmd_t::unpair_shade; cmd.type = web_cmd_t::unpair_shade;
cmd.shadeId = shade->getShadeId(); cmd.shadeId = shade->getShadeId();
cmd.wasPaired = shade->paired; return queueRadioCommand(request, cmd);
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;
} }
// The radio and MQTT are shut down once an upload starts and only a reboot // 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 // brings them back. The completion handler schedules it but never runs if the
@ -220,11 +217,6 @@ void Web::processQueue() {
shade->paired = false; shade->paired = false;
shade->save(); shade->save();
} }
else {
// The motor is still paired so undo what the response showed.
shade->paired = cmd.wasPaired;
shade->emitState();
}
} }
break; break;
} }
@ -318,14 +310,16 @@ static void serializeShadeRef(SomfyShade *shade, JsonFormatter &json) {
json.addElem("hasLight", shade->hasLight()); json.addElem("hasLight", shade->hasLight());
json.addElem("repeats", shade->repeats); 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("shadeId", shade->getShadeId());
json.addElem("roomId", shade->roomId); json.addElem("roomId", shade->roomId);
json.addElem("name", shade->name); json.addElem("name", shade->name);
json.addElem("remoteAddress", (uint32_t)shade->getRemoteAddress()); json.addElem("remoteAddress", (uint32_t)shade->getRemoteAddress());
json.addElem("upTime", (uint32_t)shade->upTime); json.addElem("upTime", (uint32_t)shade->upTime);
json.addElem("downTime", (uint32_t)shade->downTime); 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("lastRollingCode", (uint32_t)shade->lastRollingCode);
json.addElem("position", shade->transformPosition(shade->currentPos)); json.addElem("position", shade->transformPosition(shade->currentPos));
json.addElem("tiltType", static_cast<uint8_t>(shade->tiltType)); json.addElem("tiltType", static_cast<uint8_t>(shade->tiltType));
@ -366,6 +360,7 @@ static void serializeShade(SomfyShade *shade, JsonFormatter &json) {
} }
json.endArray(); json.endArray();
} }
static void serializeShade(SomfyShade *shade, JsonFormatter &json) { serializeShade(shade, json, shade->paired); }
static void serializeGroupRef(SomfyGroup *group, JsonFormatter &json) { static void serializeGroupRef(SomfyGroup *group, JsonFormatter &json) {
group->updateFlags(); group->updateFlags();
json.addElem("groupId", group->getGroupId()); json.addElem("groupId", group->getGroupId());
@ -1900,7 +1895,7 @@ void Web::begin() {
AsyncJsonResp resp; AsyncJsonResp resp;
resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginResponse(request, g_async_content, sizeof(g_async_content));
resp.beginObject(); resp.beginObject();
serializeShade(shade, resp); serializeShade(shade, resp, false);
resp.endObject(); resp.endObject();
resp.endResponse(); resp.endResponse();
} }
@ -1919,7 +1914,7 @@ void Web::begin() {
AsyncJsonResp resp; AsyncJsonResp resp;
resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginResponse(request, g_async_content, sizeof(g_async_content));
resp.beginObject(); resp.beginObject();
serializeShade(shade, resp); serializeShade(shade, resp, false);
resp.endObject(); resp.endObject();
resp.endResponse(); resp.endResponse();
} }

View file

@ -36,7 +36,6 @@ struct web_command_t {
int8_t tiltPosition; // for setPositions/tilt int8_t tiltPosition; // for setPositions/tilt
int8_t sunny; // for sensor int8_t sunny; // for sensor
int8_t windy; // 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. // Only setting positions never goes on air.