Automation

Auto-Pulling BlackVue Dashcam Footage With Home Assistant and Docker

Independent technologist · 200+ HA devices · GriswoldLabs
9 min read

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 list
  • GET 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:

  1. 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.
  2. 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:

  1. for: 10 minutes prevents 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 the person.charles: home condition fails. No alert.
  2. person.charles: home is 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/_ER suffix, 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:

  1. 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.
  2. Stand up acolomba/blackvuesync for one camera and verify files appear in your mount. Don’t write a single line of HA YAML until this works manually.
  3. 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.
  4. 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.

Tags: #home-assistant #blackvue #docker #dashcam #nas
Share: X / Twitter Facebook

Related Articles