feat: PIPE_WRITE sliding-window client (0x0080-0x0082) + partial-region refresh#128
Open
davelee98 wants to merge 9 commits into
Open
feat: PIPE_WRITE sliding-window client (0x0080-0x0082) + partial-region refresh#128davelee98 wants to merge 9 commits into
davelee98 wants to merge 9 commits into
Conversation
…ctive repeat
Adds the client side of the windowed image-transfer protocol. A single
0x0080 probe negotiates window ("max queue size", tokens in flight) and
ack cadence ("blocks per ack") and doubles as the capability check: old
firmware ignores the unknown opcode and the client falls back to the
untouched legacy 0x70/0x71/0x72 flow after a 2 s timeout, cached per
connection. New constructor kwargs blocks_per_ack=8 / max_queue_size=16
(<=1 disables pipe mode).
_send_pipe_chunks implements QUIC-style selective repeat: span-based
token rule (next_to_send - window_base < W), 1-byte rolling seq, ACK
mask expansion with mod-256 rollover, retransmit of exactly the missing
chunks re-paced per new ACK (rewind fallback when the receiver doesn't
buffer out-of-order), PTO probes, MAX_RETX/MAX_PTO abort budgets, and a
short tail-flush probe so sub-cadence tails don't stall 5 s. Uncompressed
transfers complete via the firmware's unsolicited END ack (no explicit
END, etag reported uncommitted) mirroring the legacy auto-finish
contract; compressed transfers send explicit END with etag. Fully
functional under session encryption: seq rides as the first CCM
plaintext byte, every transmission (incl. retransmits) gets a fresh
nonce, and W<=32 keeps the replay window safe.
Transport gains write_command(drain_stale=False) so in-stream writes
don't drain pending ACK notifications. 87 new unit tests cover
negotiation, windowing, loss recovery, encryption, and legacy
regression; legacy upload-flow tests pin max_queue_size=1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ient The pipe (sliding-window) client shares version 7.11.2 with feat/ble-speed, so HA/pip treat the git-branch requirement as already satisfied and never reinstall from this branch — leaving HA on the pipe-less code. Bump to a 0.dev prerelease so the requirement is unambiguously unsatisfied and a fresh install pulls this branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Small mono partial updates now ride the pipe (0x0080 flags bit1 PIPE_FLAG_PARTIAL + 12-byte LE geometry; ACK flags bit1 confirms), gaining the window's throughput/robustness and the e-paper partial waveform. Legacy 0x76 retained as fallback via new START NACK codes 0x05 (etag mismatch) / 0x06 (partial unsupported) / 0x07 (rect invalid). PIPE_WRITE (full and partial) is now hard-gated on the config's transmission_modes bit 0x10 (supports_pipe_write); ungated devices never see a 0x0080 probe. With the gate, the START wait becomes a normal 30s command timeout (TIMEOUT_PIPE_START) rather than a 2s discovery probe. Hardening: sender and END_ACK waiter now bound pathological ACK streams that never make progress. Works unchanged under an encrypted session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ient Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The partial-refresh upload wrapped _run_pipe_upload in a broad `except ProtocolError` that downgraded any failure to a silent full-upload fallback. AuthenticationError and IntegrityCheckError are ProtocolError subclasses, so an auth/session failure (which must surface so callers can trigger reauth) or a decrypt-integrity rejection was swallowed. Re-raise those two families before the fallback; genuine protocol NACKs still fall back to a full upload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously only a missing-service error triggered a single cache-clear retry, so an ESPHome proxy serving a stale CCCD handle (ATT "Invalid handle" during notify setup) bypassed recovery and could loop unbounded. Recovery now covers handle-level mismatches too, is capped at MAX_CACHE_RETRIES attempts, and fails fast without retrying on device-absent errors.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
- ruff-format the pipe-write source and tests - drop unused total_size param from _run_pipe_upload (and call sites/tests) - remove superfluous parens after not; scope inline pylint disables for the device class attribute count and the selective-repeat sender complexity Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
g4bri3lDev
reviewed
Jul 12, 2026
g4bri3lDev
requested changes
Jul 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a PIPE_WRITE sliding-window client (protocol 0x0080–0x0082) for image transfer over BLE, modeled on QUIC-style selective repeat, plus partial-region refresh layered on the same window.
PIPE_WRITE full-frame (0x0080–0x0082)
blocks_per_ack,max_queue_size) are negotiated via the 0x0080 START handshake, which stays authoritative once the gate passes.transmission_modesbit0x10(supports_pipe_write): a device whose config lacks the bit never receives a 0x0080 probe and stays on the legacy path. Previously the bit was advisory and the probe alone decided.TIMEOUT_PIPE_START, 30 s) rather than a 2 s discovery probe. Silence still falls back to legacy and is cached per connection.Partial-region refresh over the window
PIPE_FLAG_PARTIAL+ 12-byte LE geometry; ACK flags bit1 confirms.0x05(etag mismatch) /0x06(partial unsupported) /0x07(rect invalid) drive the fallback ladder.supports_pipe_write+partial_update_support. Works unchanged under an encrypted session.Robustness fixes
Testing
uv run pytest -qgreen; new suitestests/unit/test_pipe_write.py(769 lines) andtests/unit/test_pipe_write_sender.py(648 lines), plus expanded partial/clear-cache/upload-flow coverage.Notes for reviewers
7.12.0.devNversion bumps that were used to force HA client reinstalls during development; squash/adjust as you prefer for release.FINDINGS.mdis an internal performance/correctness analysis doc — happy to drop it from this PR if it doesn't belong upstream.🤖 Generated with Claude Code