Local motion capture for Blink cameras — no subscription, 30s pre-roll, in-memory buffer
Project description
blinkvault
Local motion capture and livestreaming for Blink cameras — no subscription required.
blinkvault keeps a continuous live stream from your Blink camera in memory, detects motion locally using frame differencing, and saves MP4 clips that include up to 30 seconds of footage before the motion event. Everything runs on your own machine. No Blink subscription, no cloud clip storage, no always-on disk writes.
Why this is different
Every other Blink integration works the same way: poll Blink's cloud API every 30 seconds, wait for a new clip to appear, download it. This means you need a paid Blink subscription, you get ~30 second delays, and you only see footage after motion triggers — never before.
blinkvault takes a different approach:
| blinkvault | blinkbridge / HA integration | |
|---|---|---|
| Blink subscription required | No | Yes (for clip history) |
| Motion detection | Local, frame-by-frame | Cloud polling |
| Pre-roll footage | Up to 30 seconds | None |
| Latency | Real-time | 30+ second delay |
| Disk I/O at rest | None (RAM buffer) | Constant segment writes |
| Setup complexity | Single Python script | Docker / Home Assistant |
How it works
Live stream via the IMMI protocol
Blink cameras do not expose RTSP. blinkvault uses blinkpy's BlinkLiveStream class to speak Blink's proprietary IMMI protocol — a TLS-wrapped binary protocol that delivers a real MPEG-TS stream. We patch two bugs in blinkpy's implementation (partial reads in recv() and over-eager poll termination) to keep the stream stable.
In-memory rolling buffer
A dedicated ffmpeg process reads raw MPEG-TS data from the local proxy and feeds it into a Python collections.deque — a rolling ~45 MB window of timestamped byte chunks. Nothing is written to disk while the camera is idle.
Local motion detection
A second ffmpeg process decodes the stream at 2 fps and downscales to 320×180 grayscale. Python computes the mean absolute pixel difference between consecutive frames using numpy. When the difference exceeds a configurable threshold, motion is declared. No cloud, no ML model, no subscription.
Pre-roll clips
When motion fires, the in-memory buffer already contains the last 30 seconds of footage. blinkvault slices the relevant byte range, writes a single temporary .ts file, and converts it to a clean MP4 with ffmpeg. The resulting clip starts before the motion event — you see the person walking up to the door, not just the moment they arrived.
Auto-reconnect
Blink sessions time out after approximately 5 minutes. blinkvault detects stream termination and reconnects automatically, maintaining continuous monitoring.
Requirements
- Python 3.11+
- ffmpeg (must be on
$PATH) - A Blink account with a compatible camera (tested on Blink Video Doorbell)
Installation
From PyPI:
pip install blinkvault
From source:
git clone https://github.com/karl-dykema/blinkvault
cd blinkvault
python3 -m venv venv
source venv/bin/activate
pip install -e .
Requires ffmpeg on your
$PATH. macOS:brew install ffmpeg
Web interface
blinkvault
Or if running from source:
python app.py
Open http://localhost:8080 in your browser.
On first run you will be prompted for your Blink email, password, and a two-factor authentication code. Credentials are saved to creds.json (gitignored) and reused on subsequent runs.
Features
- Start / Stop the monitoring daemon
- Record Now — grab a clip on demand without waiting for motion
- Motion sensitivity — tune the frame-diff threshold (lower = more sensitive)
- Cooldown — minimum seconds between consecutive motion triggers
- Clip duration — how many seconds of post-motion footage to include
- Clip browser — collapsible list with formatted timestamps, file sizes, inline playback and download
CLI livestream
# Watch live in a player
blinkvault-stream --output - | ffplay -
# Record to file
blinkvault-stream --output recording.mp4
# Stream to UDP (e.g. for VLC or Frigate)
blinkvault-stream --output udp://127.0.0.1:1234
# Pick a specific camera by name
blinkvault-stream --camera "Front Door" --output recording.mp4
Configuration
Settings are saved to capture_config.json (gitignored) via the web UI, or you can edit the file directly:
{
"clip_duration": 30,
"camera_name": "",
"motion_threshold": 10,
"cooldown": 60
}
| Key | Default | Description |
|---|---|---|
clip_duration |
30 |
Seconds of post-motion footage per clip |
camera_name |
"" |
Camera name as shown in the Blink app. Leave blank to use the first camera found. |
motion_threshold |
10 |
Mean pixel difference to declare motion (1–50). Lower is more sensitive. |
cooldown |
60 |
Minimum seconds between motion triggers |
Privacy note
creds.json contains your Blink access token. It is gitignored and never leaves your machine. Clips and config are also gitignored.
Acknowledgements
- blinkpy by Kevin Fronczak — Python API library for Blink cameras. blinkvault is built on top of blinkpy for authentication, camera discovery, and the
BlinkLiveStreamIMMI protocol implementation. - FFmpeg — used for stream demuxing, grayscale frame extraction, motion analysis, and MP4 encoding.
- FastAPI — web framework powering the local UI.
- numpy — frame differencing for local motion detection.
License
GPL-3.0 — see LICENSE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file blinkvault-0.1.3.tar.gz.
File metadata
- Download URL: blinkvault-0.1.3.tar.gz
- Upload date:
- Size: 54.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20a5f4820a598e457b88568dec9fd310addab6faa82b657379835e0bf1d57c57
|
|
| MD5 |
b6a10e1af4463cc0468c36014ae5613b
|
|
| BLAKE2b-256 |
e4778c0a624e90b8a9faaf03d1fcc33f9f23d2cdfdc34f54a05e8d06f14cf1b0
|
Provenance
The following attestation bundles were made for blinkvault-0.1.3.tar.gz:
Publisher:
publish.yml on karl-dykema/blinkvault
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
blinkvault-0.1.3.tar.gz -
Subject digest:
20a5f4820a598e457b88568dec9fd310addab6faa82b657379835e0bf1d57c57 - Sigstore transparency entry: 1220031933
- Sigstore integration time:
-
Permalink:
karl-dykema/blinkvault@e2c6fe93b4b1a1dd810399f1f2fbf5974ba898bb -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/karl-dykema
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2c6fe93b4b1a1dd810399f1f2fbf5974ba898bb -
Trigger Event:
push
-
Statement type:
File details
Details for the file blinkvault-0.1.3-py3-none-any.whl.
File metadata
- Download URL: blinkvault-0.1.3-py3-none-any.whl
- Upload date:
- Size: 41.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8eb8bfc23596141370e2cf2e7a7b334befa0f5420ece7a480c72b537e7290b82
|
|
| MD5 |
0bad813aebeada1578967fe3714d9b60
|
|
| BLAKE2b-256 |
fae3cc98fe105c8f7a485cb641b7a20d81b5b65cac6e6e143a418dee7f11d6ee
|
Provenance
The following attestation bundles were made for blinkvault-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on karl-dykema/blinkvault
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
blinkvault-0.1.3-py3-none-any.whl -
Subject digest:
8eb8bfc23596141370e2cf2e7a7b334befa0f5420ece7a480c72b537e7290b82 - Sigstore transparency entry: 1220031934
- Sigstore integration time:
-
Permalink:
karl-dykema/blinkvault@e2c6fe93b4b1a1dd810399f1f2fbf5974ba898bb -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/karl-dykema
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2c6fe93b4b1a1dd810399f1f2fbf5974ba898bb -
Trigger Event:
push
-
Statement type: