I run BlackVue dashcams in both household vehicles — a DR970X-2CH Plus II in one (front + rear) and a DR900X Plus in the other (front only). They both record continuously on a 256 GB microSD, but pulling footage off the SD card means physically taking the card out, plugging it into a laptop, and copying files. I’d done that exactly twice before I got tired of it and built a system that pulls footage automatically the moment a car drives into WiFi range of the house.
The whole thing is one Docker container, two HA ping sensors, and a 121-line package file. This post is the architecture, the YAML, and the gotchas.
The architecture
Camera WiFi (built-in) → Home Network
↓
BlackVueSync container (Docker on Unraid)
↓
/mnt/user/dashcam/
├── car/
└── truck/
Home Assistant ping sensors → binary_sensor.dashcam_*_online
↓
Notifications + sync-trigger logic
Each BlackVue camera runs a small HTTP server when it’s powered on. The endpoints aren’t documented by BlackVue but they’re well-known in the community:
GET http://<camera-ip>/blackvue_vod.cgi— file listGET http://<camera-ip>/blackvue_record/<path>— download a recording- No authentication
When the car is parked in the driveway and the camera is powered, it joins the home WiFi (because I configured it to) and serves these endpoints to anything on the LAN that asks. BlackVueSync is an open-source Docker container that polls those endpoints, downloads new recordings, and stores them locally. HA’s job is just to know whether each camera is currently reachable and to surface notifications when a sync starts or fails.
Why this beats the BlackVue cloud
BlackVue sells a cloud subscription. It’s fine. It uploads from the camera over LTE / WiFi, stores footage in their cloud, lets you view it from your phone. Two reasons I don’t use it:
- The cameras are recording 24/7 and uploading every clip would be expensive bandwidth-wise. The cars together generate 30-60 GB/day of footage. On metered cellular that’s prohibitive. The cameras have a setting to upload only event-triggered clips, but I’d rather have everything archived locally.
- The footage I actually care about is the long-form continuous recording, not just events. When something happens — a parking-lot ding, a road incident — I usually want the 10 minutes of context around it, not just the 30-second event clip BlackVue’s algorithm decides to upload.
Local sync to a NAS gets me both. Footage stays on the cameras for as long as the SD card holds it (rolling buffer), and a permanent archive lives on the NAS with whatever retention I want.
The Docker setup
acolomba/blackvuesync is the image I’m using. It’s MIT-licensed, well-maintained, and handles the boring parts: parallel downloads, retry on disconnect, disk-full safety, day-folder grouping. A minimal docker-compose.yml looks like:
services:
blackvuesync-car:
image: acolomba/blackvuesync:latest
container_name: blackvuesync-car
restart: unless-stopped
environment:
- ADDRESS=<car-camera-ip>
- KEEP=30d
- GROUPING=daily
- CRON=*/15 * * * * # every 15 min
volumes:
- /mnt/user/dashcam/car:/recordings
networks:
- default
blackvuesync-truck:
image: acolomba/blackvuesync:latest
container_name: blackvuesync-truck
restart: unless-stopped
environment:
- ADDRESS=<truck-camera-ip>
- KEEP=30d
- GROUPING=daily
- CRON=*/15 * * * *
volumes:
- /mnt/user/dashcam/truck:/recordings
Two containers, one per camera. They poll their assigned camera every 15 minutes; if the camera responds, they pull whatever’s new. If the camera doesn’t respond (because the car isn’t home), they log a connection failure and try again next cycle. There’s no harm in polling a camera that isn’t there — BlackVueSync handles the connection refused gracefully and just waits.
KEEP=30d is the local retention. Anything older than 30 days in /mnt/user/dashcam/<vehicle>/ gets auto-deleted on each sync run. I’ve experimented with longer windows; 30 days is the sweet spot between “I might want to look at last month’s drive” and “this is eating disk faster than I want.”
GROUPING=daily puts each day’s footage in its own subfolder, which makes manual browsing dramatically easier. Without it, you get one giant flat directory of YYYYMMDD_HHMMSS_NF.mp4 files and good luck finding anything.
Static IPs are not optional
The camera IP has to be stable. If it changes — say your DHCP lease rolls over — the Docker container starts pinging a black hole and you stop getting footage. I assign DHCP reservations in UniFi for both cameras based on MAC, and the reservations live alongside the rest of my smart-home reservations.
You could put the IPs in HA secrets.yaml and template them into the docker-compose.yml at deploy time, but I haven’t bothered — the IPs are stable enough that hardcoding them in docker-compose.yml is fine.
The HA side: ping sensors + sync notifications
HA isn’t doing the actual sync — that’s all BlackVueSync’s job. HA’s role is to surface visibility: tell me when a car comes home, when a camera disconnects unexpectedly, and let me toggle notifications during a road trip when I don’t care about every sync event.
Two ping integrations, added through the UI (Settings → Devices → Add Integration → Ping):
- Dashcam Car Online — pings the car camera IP every 30 seconds
- Dashcam Truck Online — pings the truck camera IP every 30 seconds
These produce binary_sensor.dashcam_car_online and binary_sensor.dashcam_truck_online. The package file uses these as the foundation:
input_boolean:
dashcam_sync_notifications:
name: Dashcam Sync Notifications
icon: mdi:dashcam
automation:
- id: dashcam_car_online
alias: "Dashcam Car Online"
description: "Notify when car dashcam connects to WiFi (car arrived home)"
triggers:
- trigger: state
entity_id: binary_sensor.dashcam_car_online
from: "off"
to: "on"
for:
seconds: 30
conditions:
- condition: state
entity_id: input_boolean.dashcam_sync_notifications
state: "on"
actions:
- action: notify.charles
data:
title: "Dashcam Sync"
message: "car dashcam connected — footage syncing to NAS"
data:
tag: dashcam-car-sync
mode: single
The for: 30 seconds debounces — without it, brief WiFi handshake flickers when the car first parks send the binary sensor through three or four state changes in a row, and you’d get redundant notifications.
The tag: dashcam-car-sync is a notification tag. On Android (and iOS with the companion app), notifications with the same tag replace each other instead of stacking. So if you arrive home, leave, arrive again twenty minutes later, you don’t get six “Dashcam Sync” notifications piling up in your tray — you get one, updated in place.
input_boolean.dashcam_sync_notifications is a manual on/off toggle. I have a card for it on my home dashboard. When I’m road-tripping and the camera is bouncing on/off WiFi at gas stations and hotels, I flip it off so my phone isn’t pinging.
The disconnect alert
The other half is detecting an unexpected disconnect — the camera was online, I’m still at home, and now the camera has gone offline. That usually means the camera or its WiFi has died and I want to know:
- id: dashcam_car_disconnect_alert
alias: "Dashcam Car Disconnect Alert"
description: "Alert if car dashcam drops off WiFi while car should be home"
triggers:
- trigger: state
entity_id: binary_sensor.dashcam_car_online
from: "on"
to: "off"
for:
minutes: 10
conditions:
- condition: state
entity_id: person.charles
state: "home"
- condition: state
entity_id: input_boolean.dashcam_sync_notifications
state: "on"
actions:
- action: notify.charles
data:
title: "Dashcam Offline"
message: "car dashcam lost WiFi connection (you're still home)"
data:
tag: dashcam-car-offline
Two key pieces:
for: 10 minutesprevents this firing when I drive away. The car leaves the driveway, the camera goes off WiFi, ten minutes later the automation would fire — but by then I’m not home, so theperson.charles: homecondition fails. No alert.person.charles: homeis the condition that says “this only fires if the car is supposed to be parked here.” Without it, every legitimate departure would generate a “dashcam offline” alert.
I’ve gotten this notification twice in a year. Once was a tripped GFCI on the garage outlet that powers the car’s hardwire kit; once was a flaky WiFi extender. Both real problems worth knowing about. Zero false positives.
File naming, in case you’re parsing it
BlackVue’s filename convention:
YYYYMMDD_HHMMSS_NF.mp4— Normal recording, Front camera..._NR.mp4— Normal, Rear (DR970X-2CH only)..._EF.mp4— Event-triggered, Front (sudden brake, impact, etc.)..._ER.mp4— Event-triggered, Rear..._PF.mp4— Parking mode, Front (motion-triggered while parked)..._PR.mp4— Parking mode, Rear
If you’re scripting against this — say, automatically pulling out only event clips for a separate “interesting events” archive — the suffix is the only signal you need.
What this doesn’t do
A few things this setup deliberately doesn’t include, in case you’re expecting them:
- Cloud uploads. I don’t push footage anywhere offsite. If I cared, I’d add a separate rclone job that watches
/mnt/user/dashcam/and pushes to B2 or similar. Haven’t bothered. - Event detection / parsing. The cameras flag their own events with the
_EF/_ERsuffix, but I don’t have anything that watches the directory for new event clips and notifies me. The notification fires when the camera comes online, not when an event is recorded. I’ve thought about a folder-watcher that fires a “new event clip arrived” notification, but in practice I check the archive when something happened, not the other way around. - OCR or video analysis. I’ve experimented with running each new clip through a license-plate OCR for “log every plate that drives past my house” — works fine, requires GPU time, and turns out to be more interesting in concept than useful in practice. Maybe a separate post.
If you’re building this from scratch
The order I’d build in:
- Get the cameras on home WiFi with static IPs. This is the part most people skip and then wonder why nothing works. Static IPs are not optional.
- Stand up
acolomba/blackvuesyncfor one camera and verify files appear in your mount. Don’t write a single line of HA YAML until this works manually. - Add ping sensors in HA and confirm they flip state when the car leaves and arrives. Again, no automations yet — just confirm the sensors are reliable.
- Then write the notification automations, with the
for:debounces from the start.
Skipping straight to “I’ll write the HA YAML and figure out the rest as I go” is the fast path to debugging an automation that’s broken because the underlying ping sensor was always wrong.
The full package is about 120 lines once you include the disconnect alerts for both vehicles. Reasonable amount of YAML for a system that ends up running unattended for months.