fix(device-image): honor X-Draw-Pending to skip rotation during recovery
CI / test (push) Has been cancelled

When the firmware sends X-Draw-Pending: 1, its drawNeeded NVS flag
survived a power-loss-during-draw — it has the bytes for the previous
image in its cached /img.bin and just needs another chance to finish
painting them. Return the device's current image (no rotation advance),
which lands as a 304 since the device claims the same image-id.

Crucially this overrides the X-Boot-Reason: cold force-resync. The
typical mid-draw-interruption cause IS a reset that turns the next
wake into a cold boot, so without this override force-resync chases
a fresh image every interruption and the device cycles through the
rotation leaving torn frames on the 13.3 panel.

Locked image still wins (user intent overrides recovery). Old firmware
that doesn't send the header is unaffected — branch is gated on the
header being present.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 17:43:13 -04:00
parent f6321412aa
commit 38ea9b3d06
2 changed files with 105 additions and 2 deletions
+14 -2
View File
@@ -170,12 +170,24 @@ class DeviceImageController extends AbstractController
// 3. Schedule says due (the normal case).
//
// Timer wakes after first-image otherwise stay schedule-gated.
$bootReason = strtolower((string) $request->headers->get('X-Boot-Reason', ''));
$forceResync = ($bootReason === 'cold');
//
// Recovery override: X-Draw-Pending: 1 means the device's drawNeeded
// NVS flag survived a power-loss-during-draw. We give it back its
// own current image so the firmware can finish repainting from its
// cached /img.bin. This explicitly overrides the cold-boot
// force-resync, because the typical interrupted-draw cause IS a
// reset that turns the next wake into a cold boot — without this
// bypass, force-resync chases a fresh image every interruption and
// the device churns through the rotation leaving torn frames.
$bootReason = strtolower((string) $request->headers->get('X-Boot-Reason', ''));
$forceResync = ($bootReason === 'cold');
$wantsBootstrap = $currentImageId < 0;
$drawPending = $request->headers->get('X-Draw-Pending') === '1';
if ($device->getLockedImage() !== null) {
$image = $device->getLockedImage();
} elseif ($drawPending) {
$image = $device->getCurrentImage();
} elseif ($forceResync || $wantsBootstrap || $this->rotation->isDue($device)) {
$image = $this->rotation->advance($device);
} else {