Skip to content

Add classic HomeKit Secure Video recording#493

Open
RaHehl wants to merge 1 commit into
ikalchev:devfrom
RaHehl:legacy-hksv
Open

Add classic HomeKit Secure Video recording#493
RaHehl wants to merge 1 commit into
ikalchev:devfrom
RaHehl:legacy-hksv

Conversation

@RaHehl

@RaHehl RaHehl commented Jul 4, 2026

Copy link
Copy Markdown

Summary

Adds HomeKit Secure Video (classic HKSV) to the Camera accessory: an accessory can now record event-triggered fragmented-MP4 clips to a HomeKit Home Hub over the HomeKit Data Stream (HDS).

Verified end-to-end against a real iOS 26.5 Home Hub — motion triggers the hub to open a recording stream and pull fMP4 fragments over the encrypted HDS connection.

Non-breaking and opt-in: existing cameras are unaffected; recording is enabled via a recording option.

What's included

  • HDS transport — framing + ChaCha20-Poly1305 key derivation (hds.py), the opack message codec (hds_protocol.py), the TCP listener/dispatch (hds_server.py), the dataSend recording fragment transfer (hds_recording.py).
  • Recording configuration TLV codecs (hksv_recording.py) matching the HAP spec / HAP-NodeJS (supported vs. selected encoding, list separators, per-attribute resolutions).
  • Camera wiringCameraRecordingManagement, CameraOperatingMode, DataStreamTransportManagement, an event-trigger MotionSensor, the recording-stream delegate hook. Streaming is set up only when video options are present, so a recording-only camera is possible.
  • SetupDataStreamTransport write-response — the setter returns the encoded response (TCP port + accessory key salt) so the HAP layer delivers it in the write response (first user of the wr mechanism).
  • Integration helpersfmp4_recording_packets(reader) turns any fragmented-MP4 byte source (ffmpeg stdout, an aiohttp/go2rtc body, ...) into the init packet + fragments a delegate yields; Camera.set_motion_detected(bool) drives the trigger. A consumer supplies a keyframe-aligned fMP4 source and a motion signal rather than reimplementing framing/protocol.

Robustness (asyncio / resources)

  • Pre-handshake receive buffer capped + handshake timeout so an unauthenticated peer on the port can't make us buffer unboundedly; frames over a 1 MiB payload cap are rejected.
  • Pending transport registrations bounded; malformed frames/messages and handler errors contained (bounded opack recursion, guarded dispatch); a stale dataSend close can't stop the current stream.
  • Recording task + delegate torn down exactly once on close and/or connection loss, and the delegate's async generator is aclose()d so its subprocess is released deterministically.
  • Fragment delivery honours transport write back-pressure (pause/resume_writing).

Testing

  • New unit tests: HDS transfer/chunking, connection-loss cleanup, double-stop, back-pressure, buffer/pending bounds, config round-trips (incl. multi-value), setup/eligibility, the write-response handshake, the fMP4 framer. Full suite green.
  • flake8 + pylint (repo configs) clean; black/isort applied.
  • Manually verified end-to-end on real iOS 26.5 hardware (pair -> enable recording -> motion -> fMP4 fragments over HDS).

Notes

  • The shared Version characteristic (0x37) is narrowed to paired-read only (per spec; it previously also advertised events the accessory never emitted). Two to_HAP test expectations updated accordingly.
  • Includes a small Python 3.14 compat guard for the removed asyncio.SafeChildWatcher.
  • Happy to add a CHANGELOG.md entry if you'd like it in this PR — left it out so you can place it under the version/section you prefer.

Implements HomeKit Secure Video (classic HKSV) so an accessory can record
event-triggered fragmented-MP4 clips to a HomeKit Home Hub over the HomeKit
Data Stream (HDS). Verified end-to-end against a real iOS 26.5 Home Hub:
motion triggers the hub to open a recording stream and pull fMP4 fragments
over the encrypted HDS connection.

Contents:
- HDS transport: framing + ChaCha20-Poly1305 key derivation (pyhap/hds.py),
  the message/opack protocol codec (hds_protocol.py), the TCP listener and
  connection dispatch (hds_server.py), and the dataSend recording fragment
  transfer (hds_recording.py).
- Recording configuration TLV codecs matching the HAP spec / HAP-NodeJS
  (hksv_recording.py): supported vs. selected encoding, list separators,
  per-attribute resolutions; encode/decode are faithful inverses in both
  directions (single value <-> scalar, several <-> list).
- Camera wiring (camera.py): CameraRecordingManagement, the Camera Operating
  Mode, the DataStreamTransportManagement service, an HKSV event-trigger
  MotionSensor, and the recording-stream delegate hook. Stream management is
  set up only when the options describe video, so a recording-only camera is
  possible (streaming_status is None for it).
- Recording eligibility characteristics iOS validates before managing
  recording: Active (0x0B0) on CameraRTPStreamManagement, StatusActive on the
  MotionSensor, PeriodicSnapshotsActive on the operating mode, and uint8
  timed-write HomeKitCameraActive/RecordingAudioActive. The shared Version
  characteristic becomes paired-read only (per the HAP spec; it previously
  also advertised events, which the accessory never emitted - existing
  to_HAP expectations updated accordingly).
- SetupDataStreamTransport is a write-response characteristic: the setter
  returns the encoded response (TCP listening port + accessory key salt) so
  the HAP layer delivers it in the write response and the controller can open
  the HDS connection.
- Integration helpers: fmp4_recording_packets() turns any fragmented-MP4
  byte source (ffmpeg stdout, an aiohttp/go2rtc response, ...) into the init
  packet + media fragments a recording delegate yields, and set_motion_detected()
  drives the event trigger - so a consumer supplies a keyframe-aligned fMP4
  source and a motion signal rather than reimplementing the framing.
- Framework hooks: the pair-verify shared secret is exposed per session for
  HDS key derivation (and popped on disconnect), and setters can opt into the
  sender's client address (introspection cached per setter, not per write).
- accessory_driver guards the asyncio.SafeChildWatcher usage which was
  removed in Python 3.14.

Robustness (asyncio / resource management):
- The pre-handshake receive buffer is capped and connections that never
  complete the control handshake time out and close, so an unauthenticated
  peer on the advertised port cannot make the process buffer unboundedly.
  Frames announcing more than the 1 MiB payload cap are rejected.
- Pending transport registrations are bounded; malformed frames/messages and
  handler errors are contained (bounded opack recursion, guarded dispatch)
  rather than tearing down the link uncaught. A stale dataSend close for an
  earlier stream cannot stop the current one.
- The recording task and its delegate are torn down exactly once on
  dataSend close and/or connection loss (a close-callback list, not a single
  slot; a second cancel cannot abort the in-flight generator aclose()), and
  the delegate's async generator is aclose()d so its subprocess is released
  deterministically.
- Fragment delivery honours transport write back-pressure (pause/resume) so a
  slow controller cannot make the accessory buffer a whole fMP4 fragment.

Tests: HDS fragment transfer and chunking, connection-loss cleanup, the
close-then-disconnect double-stop, write back-pressure, pre-bind buffer cap
and pending bounds, recording configuration round-trips (incl. multi-value),
recording management setup/eligibility, and the SetupDataStreamTransport
write-response handshake (through the HAP path, plus a single-argument-setter
regression guard).
@RaHehl

RaHehl commented Jul 4, 2026

Copy link
Copy Markdown
Author

Some context on where this fits:

This implements the classic HKSV recording path — HDS + CameraRecordingManagement + fragmented-MP4, the H.264 baseline every controller understands and the recording model current iOS uses. It works on today's iOS (verified live end-to-end) and stands on its own.

It's also intended as the foundation for Apple's newer HomeKit Secure Video spec — the HomeKit Secure Video Open Source Compatibility Guide (HEVC / multi-tier RTP, WebRTC, CMAF-ingest recording). That path keeps the classic H.264 recording configuration as the baseline and layers HEVC tiers on top, so a working classic HKSV implementation is the prerequisite. Landing this first gives HKSV a complete, working baseline now, with the HEVC / multi-tier layer as a follow-up.

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.

1 participant