π«π· Version franΓ§aise
Turn a Raspberry Pi and a camera module into a native HomeKit Secure Video camera like a camera fresh out of the box.
Install β scan the QR code β done.
No Homebridge, no plugins, no cloud account, no admin dashboard to babysit. The camera pairs directly with the Home app, streams live video, detects motion, and records HKSV clips to iCloud that start before the motion happened.
- Live streaming β the Pi's hardware H264 encoder is passed straight through to HomeKit (SRTP, zero re-encoding). Fluid 1080p30 with near-idle CPU. IPv6 controllers are supported (beta β implemented per spec, not yet field-tested on an IPv6-preferred network; reports welcome).
- HomeKit Secure Video β motion-triggered recordings stored in iCloud, viewable directly in the Home app's timeline. A rolling prebuffer (6 s of retained fragments) means every clip starts before the motion event.
- Smart classification β People / Animals / Vehicles detection is done by your Apple home hub (Apple TV / HomePod), exactly like commercial HKSV cameras. The Pi just reports motion, cheaply and reliably.
- Rich notifications β motion alerts with a snapshot on your iPhone.
- Night vision (beta) β under IR illumination the stream and snapshot switch to grayscale (killing the 850 nm pink cast), and dim colour scenes are brightened automatically as the light drops. Detection and correction run on-device, with hysteresis so the picture doesn't flicker.
- Status dashboard β a built-in web page (
http://<pi>.local:8080) shows the pairing QR code and a live health view: overall status, temperature & throttle state, CPU load, RAM/swap, uptime, per-service status, snapshot freshness, HKSV state and last motion. - Lightweight β ~210 MB RAM with an active stream, low CPU load, a handful of small systemd services.
- Private β everything runs on your Pi. The RTSP stream is bound to localhost (never exposed on the network); the only cloud involved is your own iCloud (for HKSV recordings, end-to-end encrypted by Apple).
| Board | Raspberry Pi 4 (any RAM size), Pi Zero 2 W, Pi 3. Not the Pi 5 β it has no hardware H264 encoder, and the project deliberately targets the small boards. |
| Camera | Any CSI camera module supported by libcamera (Camera Module 2/3, HQ, NoIRβ¦) |
| OS | Raspberry Pi OS 64-bit |
| Apple side | iPhone + a home hub (Apple TV 4K or HomePod) |
| For recordings | iCloud+ subscription (any tier β HKSV recordings don't count against your storage) |
Pi Zero 2 W: fully supported, including HKSV. Measured on a real unit: ~194 MB RAM idle, ~212 MB with an active live stream (out of 512 MB). The zram swap does get used (~180 MB, more while HKSV recording is armed) β that's compressed RAM, not the SD card, and it is absorbed without any tuning. A heatsink is strongly recommended: the SoC runs hot under continuous load. Even with a full-board heatsink, expect ~75β80 Β°C and occasional throttling in a closed enclosure β add ventilation holes or a small 5 V fan to stay safely below.
Starting from a blank card, use Raspberry Pi Imager:
- Choose OS β Raspberry Pi OS (other) β Raspberry Pi OS Lite (64-bit). Lite is enough β the camera runs headless, no desktop needed; 64-bit is required on the Zero 2 W.
- Choose Storage β your SD card.
- Click the gear icon (β /
Ctrl+Shift+X) to open the advanced settings, so the Pi boots straight onto your network with no screen or keyboard:- Hostname (e.g.
cam-pi-zero) β you'll reach the camera athttp://<hostname>.local - Enable SSH (password or public key)
- Wi-Fi SSID + password (and your country)
- Username / password, locale and keyboard layout
- Hostname (e.g.
- Write the image, insert the card and power on the Pi.
- SSH in, then continue with Install below:
ssh <username>@<hostname>.local
First, check the camera is detected. The most common first-install snag is a camera libcamera can't see:
rpicam-hello --list-camerasThis should list your sensor (OV5647, IMX219, IMX708β¦). If it reports no cameras available:
- Re-seat the ribbon cable β contacts the right way round, fully clicked in (on a Pi Zero, use the narrow Zero cable).
- Make sure
/boot/firmware/config.txthascamera_auto_detect=1. - If auto-detect still finds nothing, add the explicit overlay for your sensor and reboot:
echo "dtoverlay=imx708" | sudo tee -a /boot/firmware/config.txt # or ov5647 / imx219 sudo reboot
Once the camera shows up, install:
git clone https://github.com/AlexBtlle/pi4-IA-Homekit-Camera.git
cd pi4-IA-Homekit-Camera
sudo bash install.shThe installer sets up everything: system packages, Node.js 22, mediamtx, the Python camera pipeline, the HomeKit app, and three systemd services. Nothing is compiled on your Pi β heavy binaries (mediamtx, the lean ffmpeg that makes live view start in ~0.2 s) are downloaded prebuilt from releases and checksum-verified. At the end it prints your pairing PIN, and the HomeKit service logs a QR code:
journalctl -u pi4cam-homekit -b --no-pager | head -40- Open
http://<pi-hostname>.local:8080in Safari on your iPhone or Mac β the page shows the QR code and PIN for your camera, plus a live status dashboard (services, temperature, memory, motion) - Open Home β + β Add Accessory β scan the QR code (or tap More optionsβ¦ and enter the PIN)
- The "not certified" warning is normal for any DIY accessory β tap Add Anyway
- Long-press the camera tile β settings (gear icon)
- Recording Options β select Stream & Allow Recording
- Choose when to record (e.g. When motion is detected) and which activity (People, Animals, Vehiclesβ¦)
That's it. Walk in front of the camera: a clip appears in the Home app timeline, starting ~4 seconds before you entered the frame.
To update an existing install to the latest version:
cd pi4-IA-Homekit-Camera
git pull
sudo bash install.shThe installer rebuilds what changed and restarts the three services itself. Updates are safe by design:
- Your settings are preserved β
/opt/pi4cam/config.yamlis never overwritten; only keys introduced by the new version are added (the annotated defaults land in/opt/pi4cam/config.yaml.distso you can diff). - No re-pairing β the pairing secrets survive updates, so the camera keeps its identity in the Home app and its HKSV history.
After the update, open http://<pi>.local:8080 and check that everything is green.
ββ pi4cam.service (Python) βββββββββββββββββββββββββββββββββββ
β picamera2 β
β ββ main 1920Γ1080, hardware H264 (keyframe every 1 s) β
β β ββ ffmpeg -c copy β RTSP β mediamtx β
β ββ main YUV420 β JPEG snapshot β /dev/shm every 2 s β
β ββ lores 320Γ240 β OpenCV MOG2 motion detection β
β ββ POST localhost:8989/motion β
β (frame watchdog: restarts on libcamera frontend timeout) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββ mediamtx.service ββββββββββββββββββββββββββββββββββββββββββ
β RTSP fan-out (127.0.0.1:8554) β 1 producer, N consumers β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββ pi4cam-homekit.service (Node, HAP-NodeJS) βββββββββββββββββ
β Standalone HomeKit camera accessory: β
β β’ Live : RTSP β SRTP passthrough (-c:v copy) β
β β’ Snapshot: serves the latest tmpfs JPEG (instant) β
β β’ Motion : MotionSensor + local HTTP endpoint :8989 β
β β’ HKSV : continuous fragmented-MP4 prebuffer (6 s ring) β
β β recording delegate streams init + live β
β fragments to the home hub on motion β
β β’ Web : QR + status dashboard on :8080 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The video is encoded once, in hardware, on the camera. Everything downstream (live stream, recordings, snapshots) reuses that same H264 stream without re-encoding β that's why it stays fluid and light.
Zero re-encoding is the choice that makes this project viable on a Pi Zero 2 W β and it implies deliberately ignoring part of what HomeKit negotiates. These are known tolerances, shared by the DIY ecosystem (Scrypted, homebridge-camera-ffmpegβ¦), documented here for transparency:
- Fixed resolution β HomeKit picks a resolution from the advertised list (often 640Γ360 for the grid or remote viewing) but always receives the native stream (1080p by default). iOS scales it client-side.
- Bitrate (the one negotiated parameter that IS honoured) β live sessions drive the hardware encoder toward what they negotiate (~2 Mbps remote/cellular) and it returns to the configured ceiling when they leave. On a modest uplink, also consider a
camera.bitrateof 3β4 Mbps. - H264 profile β the stream is always High profile regardless of what was negotiated; Apple decoders read it without complaint.
- HKSV recording configuration β the profile/bitrate/iFrameInterval selected by the home hub are not applied (same passthrough reason); iCloud clips weigh whatever the camera encodes.
- Ghost live audio β an AAC-ELD audio block is declared because HomeKit requires one in the negotiation, but no audio packet is ever sent (the camera module has no microphone). The speaker icon in the Home app does nothing.
- Snapshots β always served at 1280Γ720 whatever size is requested; iOS resizes.
Everything lives in one file: config.yaml. On an installed system, edit /opt/pi4cam/config.yaml directly, then restart the services (sudo systemctl restart pi4cam pi4cam-homekit). Re-running install.sh never overwrites your values β it only injects keys added by newer versions (an annotated reference is kept at /opt/pi4cam/config.yaml.dist).
The table below covers the keys most people touch. The complete, always-current reference is config.yaml itself β every key ships there with an explanatory comment (tuning knobs for night vision, motion detection, ports, telemetryβ¦).
| Key | Default | Description |
|---|---|---|
camera.source |
csi | csi (Pi camera module) or usb (UVC webcam, beta β see TROUBLESHOOTING) |
camera.device |
/dev/video0 | V4L2 device (source: usb only) |
camera.usb_format |
mjpeg | Webcam output: mjpeg / yuyv / h264 (source: usb only) |
camera.width Γ height |
1920Γ1080 | Capture / stream / recording resolution |
camera.fps |
30 | Frame rate |
camera.bitrate |
8000000 | H264 bitrate (bit/s) β ~8 Mbps for crisp 1080p30; lower to ~4 Mbps to save bandwidth |
camera.day_min_bitrate |
500000 | Lowest bitrate (bit/s) the live governor may request in daylight. The encoder's practical floor; raising it costs bandwidth/storage with no proven gain. Always capped by camera.bitrate. |
camera.night_min_bitrate |
3000000 | Same floor once IR night mode is active. The auto-levels stretch amplifies grain across the frame, which macroblocks below ~3 Mbps (field-tested, 4G included). Always capped by camera.bitrate. |
camera.rotation |
0 | 0 / 180 only β the Pi ISP cannot rotate 90Β°/270Β° (ignored with a warning) |
camera.full_fov |
true | Use the full sensor area so the lens shows its full angle. Most sensors (IMX219, OV5647β¦) center-crop in native 1080p mode, narrowing the view; this forces a full-FOV (binned) mode and scales to the output size. Set false for the sharper but narrower native crop. |
camera.sharpness |
1.0 | ISP edge sharpening (0.0β16.0). Try 1.5β2.0 to compensate for lens softness. |
camera.contrast |
1.0 | ISP contrast (0.0β32.0). |
camera.saturation |
1.0 | ISP colour saturation (0.0β32.0). Try 1.2β1.5 for richer colours. |
camera.day_gamma |
1.0 | (beta) Automatic brightening for dim colour scenes β a gamma curve on the frame's luma (lifts shadows, preserves highlights), kept in colour. Engaged only when the scene is dark (AEC Lux estimate below threshold), with hysteresis; in daylight the trigger stays off and the image is untouched β a sunny room is never over-exposed. At that light the sensor is already pinned, so it's a digital lift (brighter but noisier; ISP denoise cleans it). Night/IR uses its own path (ir_grayscale). Higher = brighter shadows; 2.5 is field-tested for a dim room at dusk, 1.0 = off. |
camera.ir_grayscale |
false | (beta) Auto-switch the stream and snapshot to grayscale under IR night vision, removing the 850 nm pink cast. IR is detected from the chroma statistics of the detection stream (with hysteresis), and the effect neutralises the frame's colour planes before encoding β day/night transitions are measured on real colour data. |
camera.snapshot_path |
/dev/shm/pi4cam-snapshot.jpg | Where the JPEG snapshot is written β a tmpfs (RAM) path, to keep the 24/7 rewrites off the SD card. |
homekit.camera_name |
Pi Camera | Name shown in the Home app |
homekit.motion_timeout |
10 | Seconds the motion sensor stays active |
detection.min_motion_area |
1500 | Motion sensitivity, in absolute pixels on the low-res detection frame (smaller = more sensitive). 1500 is tuned for humans at 320Γ240; reduce to ~300β600 to also catch cats/dogs. Recalibrate if you change lores_width/lores_height. |
detection.cooldown |
30 | Quiet time between two motion episodes. A continuous movement keeps the sensor β and the HKSV clip β active for its whole duration. |
The pairing secrets (PIN, setup ID, accessory MAC) are generated once by the installer into /opt/pi4cam/homekit/pairing.json and survive re-installs β updating the code never requires re-pairing.
For a full symptom-by-symptom guide (thermal/throttling, memory & swap, stream latency, motion tuning, pairing backupβ¦), see TROUBLESHOOTING.md.
journalctl -u pi4cam -f # camera pipeline + motion detection
journalctl -u pi4cam-homekit -f # HomeKit app (pairing QR, streams, HKSV)
journalctl -u mediamtx -f # RTSP server- Camera not found when pairing β both the iPhone and the Pi must be on the same network; check that
avahi-daemonis running (mDNS). - "Recording Options" missing in the Home app β the accessory's capabilities are cached at pairing time. Remove the camera from the Home app and pair it again.
- Snapshot looks frozen β the Python pipeline refreshes
/dev/shm/pi4cam-snapshot.jpgevery 2 s. If it stops updating, checkjournalctl -u pi4cam(the frame watchdog restarts the service automatically on a libcamera timeout). - Check service health β open
http://<pi>.local:8080: the status dashboard shows each service's status, temperature & throttle state, memory/swap and the motion count. - Check the raw stream β the RTSP feed is bound to localhost for privacy, so probe it from the Pi itself:
ffprobe rtsp://127.0.0.1:8554/camerashould showh264, 1920x1080.
sudo bash uninstall.shRemoves the services, /opt/pi4cam, mediamtx, Node.js and the nodesource repo. System packages (picamera2, opencv, ffmpeg) are left in place.
- HAP-NodeJS β the HomeKit Accessory Protocol implementation (including HKSV)
- mediamtx β RTSP server
- picamera2 / libcamera β camera capture & hardware H264
- Inspired by pi0-Camera-HomeKit