Bird detection and classification system for UniFi Protect with web interface.
| Home | Insights | Species Detail | Face Labeling | Species Correction |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
- Automatic bird detection using YOLOv8l
- Species classification using BioCLIP-2
- Face detection and annotation for bird portraits
- UniFi Protect integration for automatic video download
- Web interface for viewing detections and species
- Species information from iNaturalist and Wikipedia
- Visit tracking and photo galleries
- Split visit support for tracking multiple birds in a single visit
- Dashboard with charts and statistics visualization
- Manual labeling interface for reviewing face annotations
- Internationalization support (English, German)
The easiest way to run Birdhomie is with Docker. All settings have sensible defaults - you only need to configure your UniFi Protect credentials.
services:
birdhomie:
image: ghcr.io/dewey/birdhomie:latest
container_name: birdhomie
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- birdhomie-data:/app/data
environment:
# Required: UniFi Protect credentials
- UFP_ADDRESS=192.168.1.1
- UFP_USERNAME=your-username
- UFP_PASSWORD=your-password
- UFP_CAMERA_ID=your-camera-id
volumes:
birdhomie-data:Update the four required environment variables:
| Variable | Description |
|---|---|
UFP_ADDRESS |
IP address of your UniFi Protect NVR |
UFP_USERNAME |
Local Access Only user username |
UFP_PASSWORD |
Local Access Only user password |
UFP_CAMERA_ID |
Camera ID (find in Protect: Settings → Camera → Advanced) |
To create a Local Access Only user, go to your UniFi Protect web UI and create a new user with "Full Management" permissions.
docker compose up -dAccess the web interface at http://localhost:5000
If you prefer running without Docker:
- macOS 13+ or Linux
- Python 3.13+
- uv for package management
- UniFi Protect NVR with a camera configured for smart detection
- (Optional) direnv for automatic environment variable loading
# Install uv if needed
curl -LsSf https://astral.sh/uv/install.sh | sh
# Clone and install
git clone https://github.com/dewey/birdhomie
cd birdhomie
uv sync
# Configure environment
cp .envrc.example .envrc
# Edit .envrc with your UniFi Protect credentials
# If using direnv
direnv allow
# Or source manually
source .envrc
# Run in development mode
make devThe application will be available at http://127.0.0.1:5001
All configuration options have sensible defaults. Only UniFi Protect credentials are required.
| Variable | Description |
|---|---|
UFP_ADDRESS |
IP address of your UniFi Protect NVR |
UFP_USERNAME |
Local Access Only user username |
UFP_PASSWORD |
Local Access Only user password |
UFP_CAMERA_ID |
Camera ID to monitor |
| Variable | Default | Description |
|---|---|---|
UFP_DETECTION_TYPES |
motion |
Detection types to monitor |
UFP_SSL_VERIFY |
0 |
Verify SSL certificates (set to 1 to enable) |
UFP_DOWNLOAD_INTERVAL_MINUTES |
60 |
How often to check for new events |
PROCESSOR_INTERVAL_MINUTES |
5 |
How often to process files |
PROCESSOR_WORKERS |
1 |
Parallel video processing workers |
MIN_SPECIES_CONFIDENCE |
0.85 |
Minimum confidence for species ID (0.0-1.0) |
MIN_DETECTION_CONFIDENCE |
0.80 |
Minimum confidence for bird detection (0.0-1.0) |
FRAME_SKIP |
5 |
Process every Nth frame |
FILE_RETENTION_DAYS |
30 |
Days to keep input files after successful processing |
UFP_INITIAL_SYNC_DAYS |
30 |
Days to look back on initial sync |
GUNICORN_WORKERS |
2 |
Number of web server workers (production) |
PORT |
5000 |
Web server port |
NNPACK_DISABLE |
0 |
Set to 1 for CPUs without AVX2/FMA support |
BIRDHOMIE_DB_DIR |
data/ |
Directory for the SQLite database file |
| Variable | Default | Description |
|---|---|---|
FLASK_DEBUG |
0 |
Enable debug mode with hot reloading |
SECRET_KEY |
dev-secret-key |
Flask session secret (change for production) |
FACE_ANNOTATION_BATCH_SIZE |
100 |
Batch size for face annotation |
By default, everything lives under a single data/ directory:
data/
├── birdhomie.db # SQLite database
├── input/ # Downloaded videos
├── output/ # Processed results and crops
├── species_images/ # Cached species images
└── models/ # ML models
If you need to store the database on a separate volume (for example to avoid overlapping Docker mounts), set BIRDHOMIE_DB_DIR to a different path:
environment:
- BIRDHOMIE_DB_DIR=/app/db
volumes:
- birdhomie-db:/app/db # Database on fast storage
- birdhomie-files:/app/data # Large files on bulk storageWhen BIRDHOMIE_DB_DIR is not set, the database is stored in data/ alongside everything else.
Access the web interface at http://localhost:5000 (Docker) or http://127.0.0.1:5001 (manual).
Pages:
/- Dashboard with recent visits and species/species- List of all detected species/species/<id>- Species detail page with photos and visits/visits/<id>- Individual visit details with video player/visits/<id>/split- Split a visit when multiple birds are detected/files- List of all processed files/tasks- Background task status/labeling- Manual labeling interface for face annotations/labeling/stats- Labeling progress and statistics/settings- Application settings
UniFi Protect occasionally creates multiple overlapping motion detection events for the same bird activity due to a known bug. Birdhomie provides a merge feature to consolidate duplicates:
- Navigate to the duplicate file's detail page
- Select the target file from the "Merge with another file" dropdown
- Click "Merge" to mark the duplicate as ignored
What happens when you merge:
- The source file is marked with status
ignored - All visits from the source file are soft-deleted (marked with
deleted_attimestamp) - The video file remains on disk for potential re-processing if needed
- You can undo by clicking "Process this file anyway" on the ignored file's page
The application automatically downloads smart detection events from UniFi Protect at the configured interval (default: every 60 minutes).
You can trigger downloads manually from the Tasks page in the web interface or via API:
curl http://localhost:5000/tasks/trigger/unifi_downloadsource .envrc # If not using direnv
make devThis compiles translations, enables hot reload, and runs on http://127.0.0.1:5001.
To test with real production data locally, you can sync your server's data directory using rsync:
-
Configure sync settings in your
.envrc:export SYNC_REMOTE=ubuntu@your-server.example.com:/home/ubuntu/services/birdhomie/data -
Run the sync command:
make sync-data
This will download the database, models, processed files, and species images to your local ./data/ directory. The logs/ directory is automatically excluded from sync.
Prerequisites:
- SSH access to your server with public key authentication configured
- rsync installed locally (pre-installed on macOS and most Linux distributions)
| Command | Description |
|---|---|
make dev |
Run with hot reloading |
make run |
Run in normal mode |
make compile-translations |
Compile .po to .mo files |
make extract-translations |
Extract translatable strings |
make update-translations |
Update translation catalogs |
make migrate |
Run pending migrations |
make process |
Run file processor manually |
make sync-data |
Sync production data locally for testing |
make clean |
Clean generated files |
make help |
Show available commands |
Currently supported languages: English (en), German (de)
To add translations:
make extract-translations
make update-translations
# Edit src/birdhomie/translations/<lang>/LC_MESSAGES/messages.po
make compile-translationsMake sure you've configured the required UniFi Protect credentials in your .envrc or docker-compose.yml.
Change the port mapping in docker-compose.yml or stop the conflicting process.
If you're running on a CPU without AVX2/FMA support (Intel pre-Haswell/2013, AMD pre-Excavator/2015), you may experience high CPU usage during model inference. Set NNPACK_DISABLE=1 in your environment to disable NNPACK optimizations.
MIT License - see LICENSE for details.





