Skip to content

feat: send 0x71 image-data chunks with BLE write-without-response (+ deep sleep command)#126

Merged
g4bri3lDev merged 3 commits into
OpenDisplay:mainfrom
davelee98:feat/ble-speed
Jul 11, 2026
Merged

feat: send 0x71 image-data chunks with BLE write-without-response (+ deep sleep command)#126
g4bri3lDev merged 3 commits into
OpenDisplay:mainfrom
davelee98:feat/ble-speed

Conversation

@davelee98

Copy link
Copy Markdown
Contributor

Summary

This PR contains two changes (two commits), with the BLE throughput change built on top of the deep-sleep command:

1. BLE Write Without Response for image-data chunks (3f1a0c1)

Switches DIRECT_WRITE_DATA (0x71) chunks from write-with-response to BLE Write Without Response, removing the ATT write-confirmation round-trip per chunk (~2× fewer round-trips; the largest win is over an ESPHome BLE proxy, where each ATT round-trip crosses the network).

  • The per-chunk application ACK is still awaited, so exactly one write is in flight at a time and firmware flow control is fully preserved — no firmware change required. Both ESP32 (PROPERTY_WRITE_NR) and nRF (BLEWriteWithoutResponse) already advertise the property and ACK every 0x71 chunk.
  • write_command() gains a response flag, threaded through _write() and opted into only by the two 0x71 loops (_send_data_chunks, _send_partial_chunks). All other commands keep write-with-response.
  • The characteristic is probed for the write-without-response property and falls back to write-with-response when absent.

2. Deep sleep command 0x0052 (f78908c)

  • CommandCode.DEEP_SLEEP = 0x0052 and build_deep_sleep_command()
  • OpenDisplayDevice.deep_sleep(): sends 0x0052 and tolerates the link dropping during/after the command (ESP32 power-latch ACK-then-power-off, ESP32 no-latch immediate sleep, Silabs Flex ACK-then-close-and-EM4). A disconnect or missing ACK is treated as success; an explicit 0xFF52 not-supported error frame raises ProtocolError.
  • PowerOption.deep_sleep_enabled: power_mode == BATTERY and deep_sleep_time_seconds > 0, mirroring the firmware sleep-entry condition.
  • CLI: opendisplay sleep <device> subcommand.

Happy to split these into separate PRs if preferred.

Testing

  • 533 unit tests pass (pytest tests/unit), including new tests for the write-without-response probe/fallback (tests/unit/test_connection_write_no_response.py) and the deep-sleep command across all firmware response shapes (tests/unit/test_device_deep_sleep.py).
  • Verified on hardware via the Home Assistant integration: image sends over an ESPHome BLE proxy show the expected reduction in per-chunk latency, with firmware ACK flow control intact.

🤖 Generated with Claude Code

davelee98 and others added 3 commits July 6, 2026 19:41
Add the deep-sleep command to put a device into deep sleep, plus a
convenience predicate for detecting sleep-configured devices. This is
Phase 0 of the OpenDisplay deep-sleep plan (D9).

- CommandCode.DEEP_SLEEP = 0x0052 and build_deep_sleep_command()
- OpenDisplayDevice.deep_sleep(): sends 0x0052 and tolerates the link
  dropping during/after the command (ESP32 power-latch ACK-then-power-off,
  ESP32 no-latch immediate sleep, Silabs Flex ACK-then-close-and-EM4). A
  disconnect or missing ACK is treated as success; an explicit 0xFF52
  not-supported error frame raises ProtocolError.
- PowerOption.deep_sleep_enabled: power_mode == BATTERY and
  deep_sleep_time_seconds > 0, mirroring the firmware sleep-entry condition.
- CLI: `opendisplay sleep <device>` subcommand.
- Unit tests for the builder, the device method (all firmware response
  shapes), and the config predicate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: add deep sleep command (0x0052)
Switch DIRECT_WRITE_DATA (0x71) chunks from write-with-response to BLE
Write Without Response, removing the ATT write-confirmation round-trip
per chunk (~2x fewer round-trips, largest win over an ESPHome BLE proxy).

The per-chunk application ACK is still awaited, so exactly one write is
in flight and firmware flow control is preserved — no firmware change is
required. Both ESP32 (PROPERTY_WRITE_NR) and nRF (BLEWriteWithoutResponse)
already advertise the property and ACK every 0x71 chunk.

write_command() takes a response flag, threaded through _write() and opted
into only by the two 0x71 loops (_send_data_chunks, _send_partial_chunks).
The characteristic is probed for the write-without-response property and
falls back to write-with-response when it is absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@davelee98 davelee98 requested a review from g4bri3lDev as a code owner July 11, 2026 04:50
@davelee98

Copy link
Copy Markdown
Contributor Author

write-without response was tested and working, but does not speed up transfers by itself. Deep sleep command is implemented but needs to be further enhanced with a "Sleep For X Seconds" to do what user intends. Otherwise if deep sleep is commanded on a target without a preconfigured sleep timer, it won't do anything on the firmware side. This update has some dependencies for the latest home assistant deep sleep integration updates.

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.00000% with 24 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/opendisplay/cli.py 0.00% 22 Missing ⚠️
src/opendisplay/device.py 92.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@g4bri3lDev g4bri3lDev merged commit af9894b into OpenDisplay:main Jul 11, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants