Modern bird detection dashboard and engine for Raspberry Pi 5. Standalone dual-model architecture with BirdNET V2.4 + Perch V2. Community network with live station map. Customizable station name and branding.
Highlights — scroll horizontally to peek at the main pages. Full per-section galleries are folded below.
Indicators — Weather · Statistics · Models · Analyses · Biodiversity · Phenology · Seasons · Compare
Full architecture documentation → — deep technical reference covering audio pipeline, database schema, performance, and more.
Raspberry Pi 5 + SSD
├── USB Audio Interface
│ ↓
├── BirdEngine (Python)
│ ├── Recording service (arecord → WAV 45s)
│ ├── Audio pipeline: Adaptive Gain → Highpass → Lowpass
│ │ → Noise Profile Subtraction → RMS Normalize
│ ├── BirdNET V2.4 (~1.5s/file, primary)
│ ├── Perch V2 (~0.7s/file on Pi 5, secondary)
│ ├── MP3 extraction + spectrograms
│ └── BirdWeather upload
│
├── Birdash (Node.js)
│ ├── Dashboard API (port 7474)
│ ├── Live spectrogram (PCM + MP3 stream)
│ ├── Push notifications via Apprise (100+ services)
│ ├── Detection review + auto-flagging
│ ├── Telemetry (opt-in Supabase)
│ └── In-app bug reporting (GitHub Issues)
│
├── Caddy (reverse proxy :80)
├── ttyd (web terminal)
└── SQLite (1M+ detections)
Dual-model inference — BirdNET V2.4 (~1.5s/file) + Perch V2 (~0.7s/file on Pi 5) in parallel. Model variant auto-selected per Pi: FP32 on Pi 5, FP16 on Pi 4, INT8 on Pi 3
Dual-model cross-confirmation — Perch detections below a standalone threshold (default 0.85) must be echoed by BirdNET (raw score ≥ 0.15) on an overlapping chunk. Kills the bulk of Perch false positives on low-frequency noise (wind, vehicles → geese/herons/ravens) without losing Perch's edge on species BirdNET misses. All three thresholds adjustable in Settings → Detection with (i) tooltips
Noisy-species throttle — opt-in per-species cooldown (default 120 s) keeps dominant feeder species (sparrows, blackbirds…) from flooding the DB while letting high-confidence calls (≥ bypass threshold, default 0.95) always pass. State is in-memory on the engine, hot-reloaded from
birdnet.conf. Companion scriptscripts/cleanup_throttle.pyapplies the same rule retroactively to historical rows with--dry-run/--apply, DB backup, and audio quarantine — 60-70 % typical purge on noisy stationsLocal recording — any USB audio interface via ALSA with configurable gain
Adaptive noise normalization — automatic software gain based on ambient noise, with clip guard, activity hold, and observer mode
Audio filters — configurable highpass + lowpass (bandpass), spectral noise reduction (stationary gating), RMS normalization
BirdWeather — automatic upload of soundscapes + detections
Smart push notifications — via Apprise (ntfy, Telegram, Discord, Slack, email, 100+ services) with species photo attached, station name prefix (
[Heinsch] Merle noir). 5 configurable rules: rare species, first-of-season, new species, first-of-day, favoritesMQTT publisher — opt-in, publishes each detection to any MQTT broker (Mosquitto, EMQX, HiveMQ…) on
<prefix>/<station>/detection, with a retainedlast_speciestopic and LWT online/offline status. Optional Home Assistant auto-discovery createsLast species+Last confidencesensor entities automatically. Configurable QoS, retain, TLS, username/password, minimum confidence — one-click Test from SettingsPrometheus
/metricsendpoint — scrapehttp://your-pi.local/birds/metricsfrom Prometheus / Grafana / VictoriaMetrics. Custom gauges (detections total/today/last-hour, distinct species, last-detection age, DB size), system gauges (CPU temp, usage, RAM, disk, fan RPM, uptime), feature toggles, and standard Node.js process metrics. Refreshed lazily on each scrapeLive sound-level monitor (Leq / peak) — RMS and peak in dBFS computed per recording, exposed on
/metrics(birdash_sound_leq_dbfs,_peak_dbfs,_leq_1h_avg_dbfs) and displayed as a live card in Settings → Audio with a 60-point sparkline. Useful for spotting wind, traffic, a dead mic, or silent overnight hours. Uncalibrated (trend-tracking, not SPL). Optional Apprise alerts when the average Leq drops below-90 dBFSfor 15 min (silent mic) or stays above-5 dBFS(clipping)Auth & access control — opt-in cookie sessions (single user, bcrypt). Three modes:
off(LAN-trust, default),protected(login for everything), andpublic-read(everyone can browse detections, species and stats — login required only to change settings or access sensitive data). HMAC-signed cookies, no DB sessions to manage. Bearer token (BIRDASH_API_TOKEN) still works in parallel for cron/automation. Login attempts rate-limited 5/min/IP. See Expose on the internet belowRange filter — geographic — BirdNET MData filter (already active, configurable via
SF_THRESH) now shows the live list of species expected at your location for the current week (Settings → Detection). Plus opt-in eBird filter for Perch that drops Perch detections absent from the local eBird "recently observed" map — Perch has no built-in geographic model and otherwise reports tropical species in temperate zonesPre-analysis filters (YAMNet) — opt-in privacy filter (drops detections + optionally deletes the WAV when human voice is detected, RGPD-friendly default) and dog bark filter (drops detections + cooldown window when bark / howl / growl is detected — stops the cascade of false positives dogs trigger). Powered by Google's YAMNet (AudioSet, 521 audio classes, 4 MB TFLite, bundled). One model, two filters, ~30 ms added latency per recording on Pi 5
Weekly editorial digest — Monday 8am, 5 curated lines via Apprise: numbers + delta vs N-1, highlight (rare > first-of-year > notable), best moment, phenology shift, top 3 species. Opt-in, optional tag routing
Async post-processing — MP3 extraction, spectrogram generation, DB sync don't block inference
Detection Refinement Module — heuristic bbox computed for every detection (time-frequency localization visualized as an amber dashed rectangle on every spectrogram in the dashboard) + optional stability-check worker (
birdengine-stability.service, opt-in) that re-runs Perch on a 5 s window recentered on the energy peak; detections whose confidence collapses on recentering are taggedunstableand surface in the flagged list. Seedocs/refinement/SPEC-v2.md
Home
Overview (landing page) — 6 KPIs (incl. first-detection time), "What's New" alerts, weather context, hourly activity. Featured-detection card with two tabs: Last detection (station-alive signal) and Best of today (highest-confidence pick of the day)
Today — species list with sort (count / first heard / max conf / new first) and separated count/confidence pills. Per-species interpretive summary (single deterministic status: needs review / single weak / isolated burst / repeated high-confidence / mostly dawn active / present all day). Spectrogram with expected frequency band overlay (~95 species, toggleable). Audio player with gain/HP/LP filters. Direct deep-link to Review with species + date pre-filtered
Species name translation — bird names displayed in the user's chosen language across all pages
Live
Bird Flow — animated pipeline showing live audio levels (SSE), dual-model inference with per-model species + confidence, detection flow with animated connectors, today's KPIs, key events feed
Live spectrogram — real-time audio from mic with bird name overlay
Live log — real-time streaming dashboard (SSE) with color-coded categories, KPIs, pause/resume
Live Board — full-screen kiosk display for a dedicated screen: large species photo, KPIs, today's species list, weather, auto-refresh 30s, discreet back button
History
Calendar — monthly grid with per-day species count, detection count, activity heatmap, new species badges (★) and rare species badges (◆). Click a cell with badges to see a popover with species photos and names. Click any day to open the detail view
Timeline — full-page interactive timeline with drag-to-zoom, unified bird density slider (0-100%), SVG sunrise/sunset/moon icons, type filter badges with blink highlight, confidence-mapped vertical layout
Detections — full filterable table with favorites, new species filter, per-detection delete, CSV/eBird export
Review — auto-flagged detections with spectro modal, bulk confirm/reject/delete with preview, purge rejected
Species
Species cards with photos (iNaturalist + Wikipedia), IUCN status, favorites (SQLite-backed), personal notes (per-species and per-detection), phenology calendar (12-month dot map), year-over-year monthly comparison, chart PNG export, Web Share API
Favorites — dedicated page with KPIs, search, sort; heart toggle on all species lists
Rarities — full-width clickable KPI cards, filterable table (seen once / new this year), detailed species list with photos and confidence badges
Recordings — unified audio library with two tabs: "Library" (all recordings, sortable/filterable) and "Best" (top recordings grouped by species)
Indicators
Weather — Open-Meteo correlation analysis (Pearson r), tomorrow's forecast, plus full ornithological analytics: 4 leaderboards (cold tolerance · storm singers · heavy rain · strong wind), species × temperature heatmap (top 30, -15…+35 °C bins), and a live custom-search card with 6 filterable dimensions (temp, precip, wind, hour-of-day, date range, conditions) — answers "which species are still active when it's freezing?" in one click. URL-shareable filters and CSV export
Per-detection weather context — every detection is tagged with hourly weather snapshot (temp, humidity, wind, precip, cloud, pressure, WMO code) via background
weather-watcherpolling Open-Meteo. Compact weather chips appear ontoday,overview,recordings,rarities,review,favorites, and inside the spectrogram modal. Per-species "Weather profile" panel onspecies.htmlshows the species' typical conditions (avg temp, range, % during precip) plus distribution histograms. Backfilled to the oldest detection in the DB via Open-Meteo's free archive API (no key required)Statistics — rankings, records, distributions, annual evolution; integrated Models tab for dual-model comparison (daily chart, exclusive species, overlap analysis)
Advanced analyses (polar charts, heatmaps, time series, narrative)
Biodiversity — Shannon index, adaptive richness chart, taxonomy heatmap
Phenology calendar — observed annual cycle per species (presence/abundance/hourly modes), inferred phases (active period, peak abundance, dawn chorus, migrant detection), 53-week ribbon visualization, species suggestions on empty state
Seasons — seasonal ornithological report (spring/summer/autumn/winter). Migratory arrivals with date comparison vs previous year (earlier/later), departures, season-exclusive species, multi-year evolution chart, best days, top species with year-over-year delta
Compare — side-by-side disambiguation of 2 species. Identity cards, deterministic verdict (not enough data / possible model confusion / strong seasonal separation / distinct profiles), 24h activity overlay, weekly phenology overlay, confidence histogram, reliability badge. Hardcoded "often confused" pairs (Chiffchaff/Willow Warbler, Marsh/Willow Tit…)
Navigation
- 6 intent-based sections: Home, Live, History, Species, Indicators, Station
- Mobile bottom nav (4 quick links + hamburger drawer with all 20 pages)
- Global species+date search, notification bell, review badge counter
- Keyboard shortcuts on 5 pages, swipe gestures on species photos
- Skeleton loading states for data-heavy pages
- Cross-navigation between settings and system pages
7-step hardware-aware modal — auto-triggered on fresh install (no setup flag, lat/lon=0). Detects Pi model, RAM, sound cards, disks, internet via
/api/setup/hardware-profileand proposes adapted defaults. Steps: Welcome (with detected hardware preview) → Location → Audio source (USB-recommended badge) → Model (Single/Dual based on hardware) → Pre-filters (privacy + dog) → Integrations (BirdWeather, Apprise) → Recap. Applies config to disk without restarting any service — ongoing detections never interrupted, user restarts the engine when ready. Re-runnable any time from Settings → Station → "Launch wizard". Auto-skipped after first successful completion viaconfig/setup-completed.json. Available in 4 languages.
Auto-flagging — nocturnal birds by day, out-of-season migrants, low confidence isolates, non-European species
Bulk actions — confirm/reject/delete by rule, per-selection, or purge all rejected
Full spectrogram modal with gain/highpass/lowpass filters and loop selection for manual verification
Permanent deletion — preview modal listing what will be deleted (DB + audio files), with result report
Auto-detection of USB audio devices with one-click selection
Adaptive gain — noise floor estimation, clip guard, activity hold, observer/apply modes
Bandpass + denoise — highpass (50-300 Hz), lowpass (4-15 kHz), spectral gating (noisereduce), all toggleable per profile
Ambient noise profile — record 5s of background noise (highway, HVAC), used for targeted spectral subtraction via noisereduce
y_noise— more effective than auto-denoise for constant noise sourcesFilter preview — before/after spectrograms from live mic to visualize the effect of each filter including the noise profile
Audio pipeline — Mic → Adaptive Gain → Highpass → Lowpass → Noise Profile (or auto denoise) → RMS Normalize → BirdNET + Perch — visual pipeline diagram in Settings
6 environment profiles (garden, forest, roadside, urban, night, test)
Inter-channel calibration wizard for dual EM272 microphones
Real-time VU meters via SSE
BirdStation Network — opt-in community of stations sharing daily detection summaries via Supabase
Live station map — all registered stations on an interactive dark-themed map
In-app bug reporting — submit issues directly to GitHub from the dashboard header, with optional log attachment (last hour of service logs included in the issue)
Full settings UI — models (one-click BirdNET download with license acceptance), analysis parameters, notifications, audio, backup
Interactive GPS map — Leaflet/OpenStreetMap widget in station settings with click-to-set, drag marker, and geolocation button
System health — CPU, RAM, disk, temperature, services
Web terminal — full bash in browser, supports Claude Code
Backup — NFS/SMB/SFTP/S3/GDrive/WebDAV with scheduling
12 themes — 7 dark (Forest, Night, Ocean, Dusk, Solar Dark, Nord, High Contrast AAA), 4 light (Paper, Sepia, Solar Light, Colonial), plus an Auto mode that follows the OS
prefers-color-scheme. Mini page previews in the picker, smooth cross-fade between themes, fully token-driven (design system documented indocs/THEMES.md)Photo management — ban/replace photos, set preferred photo per species
Customizable branding — configurable station name and header brand via settings
4 UI languages (FR/EN/NL/DE) + 36 languages for species names
Locale-aware units & formats — auto-detected from browser locale (°C/°F, km/h/mph, 12h/24h, DMY/MDY/ISO dates, Mon/Sun week start), overridable in Settings → Station
We publish 3 optimized Perch V2 TFLite models for edge deployment, converted from the official Google SavedModel:
ernensbjorn/perch-v2-int8-tflite on HuggingFace
| Model | Size | Latency (Pi 5) | Top-1 | Top-5 | Best for |
|---|---|---|---|---|---|
perch_v2_original.tflite |
409 MB | 435 ms | baseline | baseline | Pi 5 (default) |
perch_v2_fp16.tflite |
205 MB | 384 ms | 100% | 99% | Pi 4 (default) |
perch_v2_dynint8.tflite |
105 MB | 299 ms | 93% | 90% | Pi 3 (default) |
Benchmarked on Raspberry Pi 5 (8 GB, Cortex-A76 @ 2.4 GHz), 20 real bird recordings from 20 species, 5 runs each, 4 threads. The installer auto-selects the optimal variant for your Pi model.
| Component | Recommended |
|---|---|
| SBC | Raspberry Pi 5 (8GB) recommended — also works on Pi 4 (4GB+) and Pi 3 (1GB, INT8 models only) |
| Storage | NVMe SSD (500GB+) |
| Audio | Any USB audio interface (e.g., RODE AI-Micro, Focusrite Scarlett, Behringer UMC, UGreen 30724) + microphone |
| Network | Ethernet or WiFi |
Low-RAM Pis (≤ 4 GB): BirdNET + Perch + Node + arecord can pressure memory and trigger OOM kills under load. The installer auto-runs
scripts/configure_zram.shwhich tunes zstd-compressed zram swap (50 % of RAM on ≤2 GB hosts, 25 % on 3-4 GB hosts) viasystemd-zram-generator(modern) orzram-tools(legacy). Re-run any time, orbash scripts/configure_zram.sh --statusto inspect. Skipped silently on ≥6 GB hosts where modern RPi OS defaults already suffice.
By default birdash trusts the LAN — anyone on 192.168.x.x can change settings. To safely show your station to friends or embed it in a public site:
-
Enable auth. In Settings → Station → Security, pick a mode:
Public read-only⭐ — visitors can browse detections, species, stats, audio. Login required to change settings, edit detections, or see logs. Recommended for public sharing.Protected— login required for everything.
Set a username + password (8 chars min, bcrypt-hashed in
birdnet.conf). Login attempts are rate-limited 5/min/IP. -
Reverse-tunnel with Cloudflare (no port-forwarding, free TLS, hides your home IP):
# On your Pi curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb -o cloudflared.deb sudo dpkg -i cloudflared.deb # Login + create a tunnel cloudflared tunnel login cloudflared tunnel create birdash cloudflared tunnel route dns birdash birds.example.com # /etc/cloudflared/config.yml tunnel: <UUID-from-create> credentials-file: /root/.cloudflared/<UUID>.json ingress: - hostname: birds.example.com service: http://localhost:80 - service: http_status:404 sudo cloudflared service install sudo systemctl restart cloudflared
Your station is now reachable at
https://birds.example.com/birds/with end-to-end TLS, no open ports on your router, and Cloudflare in front of any abuse. -
Don't forget the Bearer token. If you also use
BIRDASH_API_TOKENfor cron/automation, that still works in parallel — set theAuthorization: Bearer <token>header instead of logging in.
Alternatives: Tailscale Funnel, ngrok, plain port-forward + Caddy with Let's Encrypt — birdash doesn't care which transport you use as long as something terminates TLS in front of it.
- Raspberry Pi 3/4/5 with Raspberry Pi OS 64-bit (Bookworm/Trixie) — Pi 5 recommended for dual-model
- Internet connection (for initial setup and model download)
- USB audio interface + microphone(s)
- Lavalier (clip-on) microphones with TRRS plug need a TRRS→TRS adapter for standard USB sound cards
- The installer auto-configures ALSA with a software gain boost for low-sensitivity USB mics
All other dependencies are installed automatically by the installer.
curl -sSL https://raw.githubusercontent.com/ernens/birdash/main/bootstrap.sh | bashThat's it. The bootstrap installs git if needed, clones the repo into ~/birdash, runs install.sh non-interactively, downloads the BirdNET V2.4 model, enables dual-model detection (BirdNET + Perch), and starts all services. When it finishes, open the dashboard URL printed at the end and tweak GPS/audio from Settings.
BirdNET V2.4 is under CC-BY-NC-SA 4.0 (non-commercial use — see the BirdNET-Analyzer repo). To skip the BirdNET download and use Perch-only:
curl -sSL https://raw.githubusercontent.com/ernens/birdash/main/bootstrap.sh | BIRDASH_SKIP_BIRDNET=1 bash# 1. Clone and install
cd ~
git clone https://github.com/ernens/birdash.git
cd birdash
chmod +x install.sh
./install.sh # interactive
# or: ./install.sh --yes # non-interactive
# 2. Start all services
sudo systemctl enable --now birdengine-recording birdengine birdash caddy ttyd
# 3. Open the dashboard and configure
# → Settings → Station: set GPS coordinates via interactive map
# → Settings → Detection: download BirdNET V2.4 (one-click)
# → Settings → Audio: select your USB audio deviceThe installer handles everything: system packages, Caddy, ttyd, databases, Perch V2 models (auto-downloaded from HuggingFace, variant adapted to your Pi model), services, and cron jobs. BirdNET V2.4 is installed via the dashboard (CC-NC-SA license acceptance required).
Your dashboard will be available at http://yourpi.local/birds/
When a new version is available, a red banner appears at the top of every page with the real semver version (e.g. v1.7.0 → v1.7.3). Click View to see categorized release notes, then:
- Install now — applies the update, restarts services with health-check, auto-reloads the page
- Later (24h) — snoozes the banner for 24 hours
- Skip these updates — hides until a newer version is published
On failure:
- Roll back — reverts to the previous version (appears when
previousCommitis known) - Force update — forces the update even with diverged history or dirty files
- Show log — expandable log viewer for debugging
On success: a confirmation message is shown, and the page auto-reloads after 2 seconds.
ssh user@yourpi.local 'bash ~/birdash/scripts/update.sh'for h in mickey donald papier; do
ssh "$h.local" 'bash ~/birdash/scripts/update.sh'
doneBirdStation follows Semantic Versioning: MAJOR.MINOR.PATCH
| Bump | When | Example |
|---|---|---|
| PATCH | Bug fix, polish, CSS tweak | 1.7.0 → 1.7.1 |
| MINOR | New feature, screen refonte | 1.7.3 → 1.8.0 |
| MAJOR | Breaking change, DB migration | 1.8.0 → 2.0.0 |
Version source of truth: package.json. Bump before pushing:
bash scripts/bump.sh patch # bug fix
bash scripts/bump.sh minor # new feature
bash scripts/bump.sh major # breaking change
bash scripts/bump.sh # auto from last commit (feat:→minor, else→patch)/api/update-statuscompares localgit rev-parse HEADagainstgit ls-remote origin main(1-minute cache)latestVersionis fetched from the remotepackage.jsonvia GitHub Contents API (real version, no guessing)update.shruns: git fetch → merge → npm/pip install (fatal on failure) → migrations → health-check restartrollback.shreverts to a known commit:git reset --hard→ npm install → health-check restart- All update output is logged to
config/update.log - Stale "running" states auto-expire after 10 minutes
- Snooze state stored server-side in
config/update-state.json(persistent across browsers) BIRDASH_SKIP_BIRDNET=1skips BirdNET download during install if desired
| Step | Action |
|---|---|
| 1 | System packages (Node.js, Python, ffmpeg, alsa, sqlite3, Caddy, ttyd) |
| 2 | Node.js dependencies |
| 3 | Python venv + ML dependencies (ai-edge-litert, numpy, soundfile, resampy, scipy, noisereduce) |
| 4 | Directory structure (audio, models, BirdSongs) |
| 5 | Database bootstrap (birds.db + birdash.db with full schema) |
| 6 | GeoIP auto-detection (latitude, longitude, language from ipapi.co) |
| 7 | Configuration files (birdnet.conf with Pi-aware defaults, engine config, ALSA dsnoop for shared mic access) |
| 8 | Model download — Perch V2 from HuggingFace (auto: FP32 on Pi 5, FP16 on Pi 4, INT8 on Pi 3) |
| 9 | Systemd services with KillMode=process (engine, recording, dashboard, terminal) |
| 10 | Caddy reverse proxy |
| 11 | BirdNET V2.4 download via birdnetlib (CC-BY-NC-SA 4.0) + l18n species labels (38 languages) |
| 12 | Enable dual-model (BirdNET primary + Perch secondary), start all services |
Note: BirdNET V2.4 download can be skipped with
BIRDASH_SKIP_BIRDNET=1and installed later from the dashboard: Settings → Detection → Download BirdNET V2.4.
# Node.js backend tests (160 tests including cross-page coherence)
npm test
# Python engine tests (13 tests)
cd engine && ../engine/venv/bin/python -m unittest test_engine -v
# Smoke test — loads every page in a headless browser, captures pageerror
# + console.error + 5xx, exits non-zero on any failure. Catches the
# silent regressions screenshot-only runs miss (broken JS, missing icons,
# pages that fail to mount).
npm run smoke # local
npm run smoke http://biloute.local # against any Pi station
# Refresh README screenshots (Paper theme, EN, 1440x900)
npm run screenshotsbirdash/
├── server/
│ ├── server.js # HTTP server, middleware, route delegations (271 lines)
│ ├── lib/
│ │ ├── adaptive-gain.js # Adaptive software gain algorithm
│ │ ├── alerts.js # System alert monitoring (temp, disk, RAM)
│ │ ├── alert-i18n.js # Alert message translations (4 langs)
│ │ ├── config.js # BirdNET config, settings validators, exec helper
│ │ ├── db.js # Database bootstrap, tables, taxonomy
│ │ ├── notification-watcher.js # Push notifications (polls DB, sends via Apprise)
│ │ ├── result-cache.js # Proactive result cache for heavy queries
│ │ ├── safe-config.js # Atomic JSON config read/write with mutex
│ │ ├── telemetry.js # Telemetry: anonymous pings + community network
│ │ ├── weather-watcher.js # Hourly Open-Meteo poll + archive backfill
│ │ └── whats-new-worker.js # Worker thread for heavy computation
│ └── routes/
│ ├── audio.js # Audio routes — thin dispatcher (45 lines)
│ ├── audio/ # Audio sub-modules (split from audio.js)
│ │ ├── _helpers.js # jsonConfigGet/Post, paths, whitelists
│ │ ├── streaming.js # /api/audio-info, audio-stream, live-stream, live-pcm
│ │ ├── devices.js # /api/audio/devices, test, config, boost
│ │ ├── profiles.js # /api/audio/profiles CRUD + activate
│ │ ├── calibration.js # Inter-channel gain wizard
│ │ ├── monitoring.js # SSE VU meter + filter preview
│ │ ├── adaptive_gain.js # Adaptive-gain endpoints + bg collector
│ │ └── noise_profile.js # Ambient-noise capture
│ ├── backup.js # Backup config, scheduling, export
│ ├── bug-report.js # In-app bug reporting (GitHub Issues)
│ ├── comparison.js # Seasonal report (spring/summer/autumn/winter)
│ ├── data.js # Favorites, notes, photo-pref, query
│ ├── detections.js # Detections, validations, flagging
│ ├── external.js # BirdWeather, eBird, Open-Meteo, weather analytics
│ ├── photos.js # Photo resolution/caching, species names
│ ├── settings.js # Settings, apprise, alerts, logs SSE
│ ├── setup.js # Setup wizard backend (status, hardware-profile, complete)
│ ├── system.js # Services, health, hardware, models
│ ├── telemetry.js # Telemetry routes (register, anonymous pings toggle)
│ ├── timeline.js # Timeline with SunCalc astronomy
│ ├── updates.js # Update system: status, apply, rollback, force, log
│ └── whats-new.js # Daily overview cards
├── public/ # Static frontend (Vue 3 (vendored))
│ ├── index.html # Redirect to overview.html
│ ├── overview.html # Landing page — KPIs, bird of the day, weather
│ ├── dashboard.html # Bird Flow — live pipeline visualization
│ ├── today.html # Today's detections with audio filters
│ ├── calendar.html # Monthly calendar with new/rare species badges + popover
│ ├── timeline.html # Full-page timeline with drag-to-zoom
│ ├── detections.html # Filterable detection table
│ ├── review.html # Detection review + bulk actions
│ ├── species.html # Species cards + favorites + notes
│ ├── gallery.html # Redirect → recordings.html
│ ├── favorites.html # Favorites with stats + management
│ ├── weather.html # Weather/activity correlation
│ ├── stats.html # Statistics + integrated Models tab
│ ├── analyses.html # Per-species deep analysis
│ ├── biodiversity.html # Shannon index, adaptive richness chart
│ ├── phenology.html # Observed phenology calendar (per species)
│ ├── comparison.html # Seasonal report (migratory arrivals, departures, evolution)
│ ├── compare.html # Compare 2 species (disambiguation: identity, verdict, 24h, phenology, confidence)
│ ├── spectrogram.html # Live spectrogram + clip playback
│ ├── settings.html # Full settings (9 tabs)
│ ├── system.html # System health + terminal
│ ├── liveboard.html # Live Board — kiosk display mode
│ ├── log.html # Live log dashboard (SSE)
│ ├── recordings.html # Audio library with photos
│ ├── rarities.html # Rare species tracker
│ ├── recent.html # Redirect to calendar.html
│ ├── models.html # Redirect to stats.html?tab=models
│ ├── js/
│ │ ├── bird-config.js # Navigation, API config
│ │ ├── bird-queries.js # Shared SQL query library (38 queries)
│ │ ├── bird-icons.js # Lucide icon set (98 SVG icons)
│ │ ├── bird-shared.js # Utilities, DSP, favorites, notes API
│ │ ├── bird-vue-core.js # Vue composables, i18n (4 langs), shell
│ │ ├── bird-spectro-modal.js # SpectroModal component (extracted)
│ │ ├── bird-weather-chip.js # <weather-chip> component + global cache
│ │ ├── bird-setup-wizard.js # <setup-wizard> 7-step onboarding modal
│ │ ├── bird-timeline.js # Timeline rendering (sky, stars, markers)
│ │ ├── vue.global.prod.min.js # Vue 3 (vendored)
│ │ ├── chart.umd.min.js # Chart.js (vendored)
│ │ └── echarts.min.js # ECharts (vendored)
│ ├── i18n/ # Translation files (fr/en/de/nl.json)
│ ├── css/ # Styles + 12 themes (see docs/THEMES.md)
│ ├── settings/ # Lazy-loaded settings tab fragments
│ └── sw.js # Service Worker (offline cache)
├── engine/ # BirdEngine (Python detection engine)
│ ├── engine.py # BirdEngine orchestrator + main (~850 lines)
│ ├── audio.py # I/O · sound-level · adaptive gain · filters · split_signal
│ ├── models.py # TFLite wrappers (BirdNET v1/v2.4, Perch v2) + factory
│ ├── clips.py # MP3 extraction + plasma-colormap spectrogram PNG
│ ├── birdweather.py # Soundscape + per-detection upload
│ ├── db.py # SQLite bootstrap + write_detection
│ ├── watcher.py # WavHandler (one-behind rotation trick)
│ ├── yamnet_filter.py # Privacy + dog YAMNet pre-filter (opt-in)
│ ├── config.toml # Engine configuration
│ ├── record.sh # Audio capture (arecord)
│ ├── purge_audio.sh # Disk space management
│ ├── convert_from_saved_model.py # Perch V2 optimization script
│ ├── birdengine.service # systemd: detection engine
│ ├── birdengine-recording.service # systemd: audio capture
│ ├── ttyd.service # systemd: web terminal
│ └── models/ # TFLite models (not in git)
├── config/
│ ├── birdash.service # systemd: dashboard
│ ├── audio_config.json # Audio device config
│ ├── audio_profiles.json # 6 environment profiles
│ ├── detection_rules.json # Auto-flagging rules
│ └── birdash-local.example.js # Local config template
├── scripts/
│ ├── update.sh # Update: git pull, deps, migrations, health-check restart
│ ├── rollback.sh # Rollback: git reset --hard, deps, restart
│ ├── bump.sh # Semver bump (patch/minor/major/auto)
│ ├── backup.sh # Incremental backup (rsync)
│ ├── bench-sqlite.mjs # SQLite query bench (--baseline / --tuned, 9 representative queries)
│ ├── cleanup_throttle.py # Retroactive noisy-species purge (DB + audio quarantine, --dry-run by default)
│ ├── configure_zram.sh # Auto-tune zram swap (zstd, % of RAM); skips on ≥6 GB hosts
│ └── smoke.mjs # Headless smoke test of all pages
├── tests/
│ └── server.test.js # Backend tests
├── README.md # English
├── README.fr.md # Francais
├── README.nl.md # Nederlands
└── README.de.md # Deutsch
| Variable | Default | Description |
|---|---|---|
BIRDASH_PORT |
7474 |
API server port |
BIRDASH_DB |
~/birdash/data/birds.db |
SQLite database path |
EBIRD_API_KEY |
— | eBird API key (optional) |
BW_STATION_ID |
— | BirdWeather station ID (optional) |
- Rate limiting: 300 req/min per IP
- Strict SQL validation (read-only, parameterized)
- Centralized SQL query library (
bird-queries.js) — 38 parameterized queries with automatic confidence filtering - Lucide icon system (
bird-icons.js+<bird-icon>component) — 100+ modern SVG icons, theme-aware form controls (accent-color) - Security headers (CSP, X-Frame-Options, Referrer-Policy)
- CORS restricted to localhost
- Worker threads for heavy computation (event-loop non-blocking)
- Auto-download of species translation labels when missing (BirdNET GitHub fallback)
BirdStation has two independent telemetry layers:
Anonymous usage pings (opt-out) — enabled by default, disableable in Settings → Station:
- Sends a monthly ping with:
version,Pi model,OS,country - No GPS, UUID, station name, IP, or any personal data
- Install and update events also recorded (same anonymous data)
- Helps us track adoption and which platforms to prioritize
- Disable anytime in Settings → Station → "Anonymous usage statistics"
Community network (opt-in) — disabled by default:
- Registers your station on the live map with GPS + station name
- Sends daily detection summaries (top species, rare species)
- Enable in Settings → Station → "Join the network"
Both layers use Supabase with a public anon key (write-only RLS). No data is collected until the service starts, and anonymous pings can be fully disabled.
- Live Station Map — see all registered BirdStation installations worldwide
- Report a bug — or use the in-app bug report button (red bug icon in header)
- Discussions — questions, ideas, show your setup
































