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>
Move the pieces of the radio TX path that don't need FreeRTOS or NVS out of
Somfy.cpp and Web.cpp so they build in the native test env: the TX job and
event types and job encoding (TxBuffer), the queue rules (TxQueuePolicy.h),
a shade's pending radio jobs (ShadeTxState), the next rolling code
(RollingCode.h) and the web command queue types (WebCommand.h).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>