processFrame worked out a StepUp/StepDown target with the same formula in
eight places, each with its own divide-by-zero guard. 4dd4a98 fixed one
that guarded on downTime but divided by upTime. stepPosition() takes the
travel time once, for both the guard and the maths, so they can't drift
apart again, and it is now covered by native tests.
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>
Review flagged the outer My/Favorite losing to the nested Up/Down it queues.
Every such nested send is from a simMy shade, where the Up/Down is what
starts the motor, so the behaviour stays. The hold moves into TxHold in
ShadeMotion with a comment explaining the order and unit tests pinning it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
checkMovement worked out the position four times, nearly copy-pasted,
for lift down, lift up, tilt down and tilt up. All four now call
travelPosition(), which keeps the same arithmetic, including how the
elapsed time wraps with millis(). Tilt up with no tilt time still
stops the tilt direction in the caller.
transformPosition and transformCommand keep their behaviour through
the new displayPosition() and flipCommand() helpers. The native env
builds ShadeMotion.cpp so all of this can be unit tested on the PC.
No behaviour change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>