A priority stop always went to the front of the radio queue, ahead of
an earlier frame of the same remote that was still waiting. The older
rolling code then went out last and the motor rejected it.
Split "may use the reserved slots" (priority) from "goes to the front"
(front) and only send a stop to the front when its remote has nothing
queued at the back. The radio task and flushes track which back jobs
have been taken so Transceiver::isQueued() can tell.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
markTxPending ran inside sendCommandAfter before callers set the new
target, so isAtTarget() kept idle shades from ever being held, while
shades already moving were frozen although their motor kept running.
Hold a shade only when it was idle before the command and release the
hold when the command didn't set it moving. The single mark in
sendCommandAfter now covers group members too, so the second pass in
SomfyGroup::sendCommand is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- endFrequencyScan always resumes TX; applying radio settings ends a scan.
- Frequency scans time out after 5 minutes.
- Reboot when an OTA upload is abandoned mid-transfer.
- /sendRemoteCommand returns 503 when the radio can't take the frame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Hold a shade's simulated movement until its command is on air.
- Send stops at the target as priority jobs.
- If a stop can't be sent or is dropped, simulate the run to the limit.
- Roll back targets when a movement command is dropped.
- Only store a favorite position when SETMY was queued.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Send functions return whether the radio took the command.
- Shade, group and tilt state only change when the command was queued.
- Rolling code is committed only after the frame is queued.
- repeatFrame advances the repeat counter only on success.
- Remove canTransmit/canQueueTx; queueTx is the single authority.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Serialize queueTx and flushTx so a queued job can't be lost to a flush.
- Settings changes preempt only the job in flight; the queue survives.
- Flush drains the queue and reports each job as dropped.
- Track suspend reasons (scan, OTA) separately.
- Report started/dropped for every job on a single event queue.
- Add priority jobs that go to the front and may use reserved slots.
- Remove the MAX_TX_REPEATS clamp.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Abort in-flight transmissions on flush so config save, frequency scan
and OTA never wait on the radio lock for a whole job
- Suspend TX during frequency scan and OTA
- Refuse commands the radio queue cannot take before consuming a rolling
code or moving simulated shade state; shift movement start when a job
goes on air late
- Apply radio config changes under the radio lock
- Re-check receiver idle before repeater transmissions
- Return 503 from web handlers when the command queue is full
- Restore 200 ms settle delay before the set-My command
- Report RSSI 0 instead of a stale value when the radio is busy
- Encode each frame once and centralise the repeat clamp
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Web command handlers ran on the async_tcp task and blocked up to 3s
waiting for loop() to bit-bang radio frames. async_tcp also services the
WebSocket on port 8080, so pongs stalled and Home Assistant
(ping_timeout=15) disconnected when several covers were commanded.
- Add a radio_tx FreeRTOS task with a job queue; sendFrame/repeatFrame
and repeater frames enqueue instead of transmitting inline.
- Serialize CC1101 access with a recursive mutex (TX task, apply, end,
frequency scan, RSSI read).
- Make Web::queueCommand fire-and-forget; drop the done semaphore.
- Route /setMyPosition and /unpairShade through the command queue.
- Clamp repeats to MAX_TX_REPEATS (40).
- Pin AsyncTCP to core 0 on dual-core targets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HA 2026.9.2 added strict device registry validation rejecting discovery
messages where via_device equals identifiers[0] ('A device can not be
its own via device'). ESPSomfy sets via_device = identifiers[0], causing
all cover entities to go unavailable on every ESP32 reboot.
via_device is for proxy/gateway relationships. ESPSomfy shades ARE the
ESP32 device — there is no proxy. Removing the field is semantically
correct and has no functional impact.
Fixes#32
The RTW decoder formula encKey - 133 was off by one versus the
RTW encoder (which maps command N to encKey N + 132). This caused
every non-My RTW command to decode as the wrong button (e.g. Up
decoded as My, Down as MyUp). Also adds rawCmd diagnostic field to
received frames for protocol debugging.