BirdLense Hub configuration
Config file: app/app_config/user_config.yaml
Defaults: app/app_config/default_config.yaml. User config is merged on top.
Precedence: environment variables > user_config.yaml > default_config.yaml. Example: GO2RTC_URL in env overrides video.go2rtc_url in YAML.
UI: Most options are editable in the web app (Settings → gear). YAML remains for advanced cases and env-based overrides.
Related: ACCESS_CONTROL (password tiers), API (HTTP surface), GLOSSARY (terms).
How keys are named
- Tables use dotted paths that mirror YAML nesting, e.g.
video.go2rtc_url→video:→go2rtc_url:inuser_config.yaml. - Boolean defaults like “empty password = open hub” are documented in ACCESS_CONTROL.
Environment variables
| Variable | Description |
|---|---|
DATA_DIR |
Data directory (/app/data in Docker) |
REDIS_URL |
Docker: set by default in docker-compose.yml (redis://redis:6379/0, service birdlense-redis). Override in app/.env for an external Redis. Local run without Compose: unset → in-process memory cache. |
DATABASE_URL |
Optional. SQLAlchemy URI. Default: SQLite under DATA_DIR. For high write load use PostgreSQL, e.g. postgresql+psycopg://user:pass@host:5432/dbname. |
SQLALCHEMY_POOL_SIZE |
PostgreSQL pool size (default 5) |
SQLALCHEMY_MAX_OVERFLOW |
PostgreSQL pool overflow (default 15) |
FLASK_SECRET_KEY |
Flask session key (settings protection) |
PROCESSOR_SECRET |
Processor API protection (X-Processor-Token) |
MCP_TOKEN |
MCP token (overrides mcp.token) |
BIRDLENSE_PORT |
Nginx port (default 8085) |
CORS_DEFAULT_ORIGINS |
Baseline CORS origins (comma-separated) for non-localhost defaults |
CORS_ORIGINS |
Extra CORS origins (comma-separated) |
OPENWEATHER_API_KEY |
OpenWeather key |
MQTT_BROKER, MQTT_PASSWORD |
MQTT if not in config |
HA_TOKEN |
Home Assistant token |
GO2RTC_URL |
Go2RTC URL if not in config |
BIRDLENSE_STARTUP_BACKFILL_SPECIES_TAXA |
1 — run species→taxon backfill on app startup; default off; otherwise use POST /api/ui/system/species-registry/backfill |
BIRDLENSE_STARTUP_CLEANUP_LEGACY_IMPORT |
1 — remove legacy disk-import placeholder detections on startup; default off; recording scan still cleans |
BIRDLENSE_STARTUP_REPAIR_SPECIES_METADATA |
1 — background metadata/image repair on startup; default off |
BIRDLENSE_NOTIFY_APP_STARTUP |
0 — skip Telegram “App is UP!” on startup; default on |
See app/.env.example. Secrets are generated by make setup (via make start / make pull).
General
| Key | Description |
|---|---|
settings_password |
Admin password: settings, feeder dispense, system tools, processor restart. Empty = no lock (home lab default) |
require_auth_for_video_stream |
false (default): guests can play recordings (/api/ui/videos/:id/stream), consistent with ACCESS_CONTROL. true: stream requires Contributor/Admin (legacy lock-down). |
contributor_password |
Optional Contributor password: species fixes, Unknowns, iNaturalist, dataset export, reports — not settings/feeder/system. Empty = single-tier mode (see ACCESS_CONTROL) |
enable_notifications |
Enable notifications (global) |
notification_excluded_species |
Species excluded from notifications |
birdnet_url |
Link to your BirdNET installation (BirdNET-Pi/Go). Empty = link/icon hidden. |
heimdall_url |
Heimdall base URL for Hub → Heimdall health probe only (System page). You may use http://heimdall.local if that hostname resolves from the Hub host/container (Docker: shared network, extra_hosts, or LAN DNS). This does not configure Heimdall to read Hub; see below. |
donate_url |
Support link. When non-empty, only the heart icon in the top app bar is shown. Empty = hidden. |
Platforms: RU — Boosty, DonationAlerts, DONAT24, YooMoney. Elsewhere — Ko-fi, GitHub Sponsors, Patreon. Settings → General → paste page URL.
Heimdall vs Hub metrics (direction)
- Heimdall → Hub: To show BirdLense status in Heimdall, add an external link or monitoring tile to your Hub, for example:
- Prometheus text:
http://<hub-host>:<port>/metricsor/api/metrics - JSON snapshot (same counters as Prometheus):
http://<hub-host>:<port>/api/metrics/summary - Hub → Heimdall: The
heimdall_urlfield is only used so the Hub can probe your Heimdall instance from the server (latency, HTTP status, title). It is not a substitute for exporting Hub metrics.
The System page also lists these endpoints under Notification observability (authenticated UI).
Processor
| Key | Description |
|---|---|
tracker |
Tracker config (bytetrack.yaml) |
max_record_seconds |
Max recording length (seconds) |
max_inactive_seconds |
Max gap without detections |
post_record_seconds |
Post-roll: added to the no-detection gap before stopping the clip. Effective gap = max_inactive_seconds + post_record_seconds. See #157. |
min_confidence_binary |
Detector threshold: bird vs non-bird. Default 0.15 |
min_track_duration |
Min track duration (s). Default 3 — fewer false triggers |
min_confidence_to_process |
Classifier threshold: species. Default 0.30. Lower = more detections, higher = stricter |
species_confidence_overrides |
Per-species thresholds: {"Rare Bird": 0.05} |
ebird_regional_top_auto_confidence |
If true (default), merge lower thresholds for species in the regional eBird top (needs secrets.ebird_api_key, ebird.*). Manual species_confidence_overrides keys always win. See #128. |
ebird_regional_top_confidence_delta |
Subtracted from min_confidence_to_process for each auto top species (default 0.05). |
ebird_regional_top_confidence_floor |
Minimum auto threshold (default 0.05). |
birdnet_mqtt_auto_confidence |
If true, lower classifier thresholds for species seen in recent BirdNET MQTT messages (similar to eBird top). Default false. Manual species_confidence_overrides win. See #129. |
birdnet_mqtt_bias_window_seconds |
Look-back window from recording start for BirdNET species (seconds, default 120). |
birdnet_mqtt_bias_delta |
Subtracted from min_confidence_to_process for auto BirdNET species (default 0.05). |
birdnet_mqtt_bias_floor |
Minimum auto threshold for BirdNET bias (default 0.05). |
multi_camera_groups |
List of Frigate camera-id groups at one location, e.g. [["BirdBox","Forest"]]. See #153. |
multi_camera_confidence_boost |
When Frigate reports the same species from two or more cameras in one group, add this to merged confidence (default 0.05, capped at 1.0). |
spectrogram_px_per_sec |
Spectrogram pixels per second (only when BirdNET event in recording window) |
regional_species |
Local species for BirdNET (empty = YOLO all classes) |
single_stage_coco_animals_only_auto |
Default true: if single_stage loads a model with exactly 80 classes (typical COCO, e.g. yolov8n.pt), detect only animal classes (bird, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe) — excludes person and inanimate COCO objects. Set false for a custom 80-class detector. Legacy: single_stage_coco_bird_only_auto is read if this key is unset. |
included_bird_families |
Family filter list (Perching Birds, Squirrel, …) |
save_images |
Save detection frames |
detection_strategy |
single_stage or two_stage |
models.single_stage |
Single-stage model path (NCNN) |
models.binary |
Binary detector path (.pt) |
models.classifier |
Classifier path (.pt) |
Video
| Key | Description |
|---|---|
source |
go2rtc (file — CLI only) |
go2rtc_url |
Go2RTC URL (http://YOUR_HOST:1984) |
cameras |
List: {id, stream_name, name} |
pre_record_seconds |
Pre-roll before trigger |
auto_reconnect |
Auto-reconnect to stream |
video_width, video_height |
Resolution |
Motion
| Key | Description |
|---|---|
source |
opencv | frigate | mqtt | esphome |
frigate_camera_filter |
Frigate cameras (from cameras) or empty = all |
frigate_label_filter |
Frigate labels to allow (bird, Bird) |
frigate_label_exclude |
Labels to ignore (cat, dog — mouse as cat) |
mqtt_topic |
MQTT binary sensor topic (Tasmota PIR) |
esphome_url |
ESPHome URL |
esphome_sensor_id |
binary_sensor id in ESPHome |
MQTT
One connection — Frigate and BirdNET topics. Triggers: Frigate, BirdNET (when MQTT), ESPHome, MQTT binary, OpenCV. YOLO runs after trigger.
| Key | Description |
|---|---|
broker |
Broker address |
port |
Port (1883) |
frigate_topic |
Frigate events topic |
birdnet_topic |
BirdNET topic |
publish_topic |
BirdLense detection publish topic |
reconnect_min_delay |
Minimum MQTT reconnect/backoff delay (seconds) |
reconnect_max_delay |
Maximum MQTT reconnect/backoff delay (seconds) |
ha_discovery |
Home Assistant MQTT discovery — Last Species, Bird at Feeder, etc. Default true. |
Topics: frigate/events (Frigate), birdnet (BirdNET), birdlense/detections (publish), birdlense/sensor/last_species/state (HA), birdlense/binary_sensor/bird_detected/state (HA). Feeder relay: homeassistant/switch/bird_feeder/command.
BirdNET: CommonName, Confidence, BeginTime (merge), ScientificName, BirdImage.URL. Frigate: after — camera, label, sub_label (species from Bird Classification), frame_time. sub_label wins over label.
Missed-event note: during outages, MQTT events can be missed and are usually not replayed later (Frigate events are typically a live stream, not backlog replay). Use Frigate recording/clip retention as the source of historical truth.
Feed
| Key | Description |
|---|---|
source |
mqtt | esphome |
duration_seconds |
Relay on duration |
mqtt_topic |
MQTT relay topic (Tasmota) |
esphome_url |
ESPHome URL |
esphome_switch_id |
Switch/button id |
esphome_type |
switch | button |
Last dispense: Hub writes data/feed_last_dispense.json on successful dispense. Overview feeder card shows last dispense time.
Weather
| Key | Description |
|---|---|
source |
openweather | homeassistant |
ha_url |
Home Assistant URL |
ha_entity_id |
Weather entity (weather.home) |
Detection (merge YOLO + Frigate + BirdNET)
Sources: YOLO (video, EU ~491 species), Frigate (sub_label from Bird Classification), BirdNET (audio). UI shows source per species. One result per species (max confidence).
Canonical names: Common name (Eurasian Jay), not scientific. species_mapping maps variants. species_canonical_mapping.txt for “Merge duplicates” (System → Recordings). Format: variant|canonical.
Catalog quality: app/web/seed/species_suspect_blocklist.txt lists terms used to hide non-bird / object rows from filtered species pickers (GET /api/ui/species?exclude_suspects=1 when requested). Full report (suspects, duplicate-name merge candidates): System → “Species catalog data quality” or GET /api/ui/system/species-registry/data-quality (settings password). New ingest matching the blocklist does not create a junk species row — it is routed to “Unknown”.
Classifier dataset alignment (EU ~491 / US NABirds ~400): in user_config.yaml, species.catalog_allowlist_file points to a text file of class display names (one per line, same as merged_cls / YOLO-normalized). Generate from your best.pt with scripts/datasets/dump_classifier_allowlist.py (e.g. write models/classification/weights/class_names.txt under app/processor). Set species.catalog_strict_ingest: true to block new species outside that list (detections go to “Unknown”). Bulk cleanup of existing junk and duplicate names: POST /api/ui/system/species-catalog/reconcile (always try {"dry_run": true} first). Compare classifier vs DB vs data/dataset folders: System → “Classifier vs catalog vs dataset”.
| Key | Description |
|---|---|
merge_window_seconds |
MQTT merge window (8 s) |
dedup_window_seconds |
Gap > N s = new visit (60 s) |
one_per_species |
One result per species (true) |
source_priority |
On conflict: ["yolo", "frigate", "birdnet"] |
cross_source_confidence_bonus |
When MQTT (Frigate/BirdNET) first merges into an existing YOLO detection, add this to confidence once (cap 1.0). Default 0.02 — small lift without retraining. Set 0 to disable. |
min_confidence_to_store |
Min confidence (0.05) |
species_mapping |
Species name mapping |
EU model: best.pt. US: best_US.pt. Training: TRAINING.
Retention
| Key | Description |
|---|---|
days |
Delete recordings older than N days |
max_gb |
Max size in GB (optional) |
Gallery (public)
Opt-in: when enabled=true and upload_url is set, Hub POSTs best frames. Multipart: image, species, confidence, timestamp, detection_id, video_id, latitude, longitude. Filters: min_confidence (0.5), only_manually_corrected. Test: docker compose -f docker/gallery-test/docker-compose.yml up -d → http://localhost:8086/api/upload.
| Key | Description |
|---|---|
enabled |
Enable uploads |
upload_url |
Receiver API URL |
min_confidence |
Only detections ≥ threshold |
only_manually_corrected |
Only human-verified |
Troubleshooting
- Visits in UI but nothing in gallery: Timeline/Overview can include audio (BirdNET) visits. Gallery uploads only
VideoSpecieswithsource=video, non-nullframes(track bboxes from the processor), andconfidence >= gallery.min_confidence. Pure audio or video rows withoutframesare not uploaded. min_confidence: default 0.5. If your model outputs e.g. 0.35, lower it inuser_config.yaml(e.g.gallery.min_confidence: 0.35).only_manually_corrected: true: only manually corrected detections are uploaded; everything else is skipped until you correct species.upload_url: full POST receiver URL (multipart). Server should return 200, 201, or 204.upload_urlfrom Docker: hostname must be reachable from the web container (e.g.http://gallery-test:5000/api/upload).http://127.0.0.1:…on the host often points at the container itself, not your PC.- Logs:
Gallery upload:(success),Gallery upload failed(HTTP status),Gallery: video N — нет строк для загрузки(no rows matched filters),Gallery upload thread failed(thread exception). - Image quality: Uploads use the same frame extraction as before, plus JPEG normalization (minimum edge, max width/height) for picky receivers. If a bbox crop cannot be produced, Hub falls back to a full frame at the clip midpoint (similar to Telegram preview fallbacks).
Integrations (scales)
| Key | Description |
|---|---|
integrations.scales.enabled |
Feeder / smart-scale weight path (default false). When enabled, the processor subscribes to MQTT (or reads Home Assistant) and persists the latest weight for the web UI. |
integrations.scales.source |
mqtt (default) or homeassistant. |
integrations.scales.mqtt_topic |
MQTT topic carrying a numeric payload or JSON with weight (processor persists state under DATA_DIR; in Docker the default data tree is app/data). |
integrations.scales.homeassistant_entity_id |
Entity id (e.g. sensor.smart_scale_weight) when source is homeassistant. |
integrations.scales.unit |
kg or g for display and stored values. |
Future work: trigger on sharp weight change (threshold / debounce) — tracked in #167.
Notifications (Telegram)
| Key | Description |
|---|---|
general.enable_notifications |
Enable notifications |
notifications.telegram_bot_token |
Bot token (@BotFather → /newbot) |
notifications.telegram_chat_id |
Chat or channel id (e.g. -1001234567890) |
notifications.base_url |
Hub URL for video/Live links. If empty, relative links cannot be turned into a full URL and Telegram link previews become less useful |
notifications.telegram_proxy_type |
none — no proxy; socks_http — URL below (typical); mtproto — server/port/secret like the Telegram app + api_id/api_hash |
notifications.telegram_proxy_url |
With socks_http: proxy for Bot API (socks5h://…, http://…). Empty = direct. Web image includes requests[socks]. |
notifications.telegram_mtproto_host / telegram_mtproto_port / telegram_mtproto_secret |
Only for mtproto; secret is hex from the Telegram app |
notifications.telegram_api_id / telegram_api_hash |
Only for mtproto; from https://my.telegram.org → API development tools (or env TELEGRAM_API_ID / TELEGRAM_API_HASH) |
notifications.telegram_api_base |
Empty = https://api.telegram.org; or your HTTPS reverse proxy base |
notifications.telegram_timeout |
Max timeout seconds for Telegram requests (text uses half) |
notifications.telegram_retries |
Retry count on timeout/connection errors |
notifications.compress_photo_over_kb |
Compress JPEG when larger than N KB (0 = off) |
notifications.telegram_max_side_px |
Max image side in px before send (0 = no resize) |
notifications.message_thread_id |
Forum topic id |
notifications.disable_notification |
Silent messages |
notifications.protect_content |
Disallow forward/save |
notifications.link_preview_large |
Large link previews (Bot API 9.4). This complements photo delivery; it does not replace sendPhoto |
notifications.use_custom_emoji |
icon_custom_emoji_id on buttons (bot owner needs Premium) |
notifications.custom_emoji_id_bird |
Custom emoji id for birds (@Stickers) |
notifications.custom_emoji_id_chipmunk |
Chipmunk/squirrel |
notifications.custom_emoji_id_open_live |
Open Live button |
notifications.paid_media_view_star_count |
Stars for photo view (0=free, 1–25000). sendPaidMedia |
notifications.paid_media_forward_star_count |
Free view: 0=allow forward, >0=block. Paid: forward allowed. |
general.notification_excluded_species |
Excluded species |
processor.save_images |
If true — save detection frames to disk for debugging. It does not control Telegram photo delivery |
processor.save_dataset_crops |
If true — save best_frame to data/dataset/train/<Species>/ |
processor.dataset_min_confidence |
Min confidence (0.0–1.0) for dataset crop. Default 0.5 |
How BirdLense sends Telegram notifications: first it tries to send an actual photo (sendPhoto / MTProto media) from best_frame; if that is unavailable, it falls back to a bbox crop from the video, then to a full frame. If Telegram rejects the media or the preview is broken, BirdLense falls back to a text message with link/button and records the fallback reason in observability (System → Observability).
Telegram Bot API 9.4/9.5: styled buttons, <tg-time format="r">, large previews (link_preview_large).
If my.telegram.org shows ERROR (cannot create app / get keys)
https://my.telegram.org is run by Telegram; BirdLense cannot fix it. It often fails from some networks (VPN on/off, captcha, rate limits).
Without api_id / api_hash: do not use MTProto proxy type in Hub. Choose SOCKS5 / HTTP and set a proxy URL so your server can reach https://api.telegram.org over HTTPS (e.g. your own socks5h://…), or no proxy if Bot API is already reachable. No api_id/api_hash needed — bot token and chat_id are enough.
MTProto mode is only for traffic via an MTProto proxy (like the Telegram app). It uses Telethon and requires api_id+api_hash from my.telegram.org. If the site keeps failing, use SOCKS/HTTP (or direct) until you can obtain keys (other network, VPN, device, or help from someone who can open the site).
A practical public SOCKS5 source for quick testing: ProxyGenerator.
Proxy check example (expect 404/401 from Telegram API — this is normal and means the path to Telegram works):
curl --proxy socks5h://IP:PORT --max-time 12 -s -o /dev/null -w "%{http_code}" https://api.telegram.org/botINVALID/getMe
⚠️ Public proxies are unstable and unsafe for long-term production use; prefer your own SOCKS5/HTTPS proxy.
Auto-select best proxy on production server (one-shot):
make refresh-telegram-proxy
Scheduled auto-rotation (easy setup):
- Install server cron (default every 6 hours): make proxy-rotation-install
- Check status and recent logs: make proxy-rotation-status
- Remove schedule: make proxy-rotation-remove
The script scripts/refresh-telegram-proxy.sh tests proxies from the Hub host, picks the fastest working one, updates notifications.telegram_proxy_type=socks_http and notifications.telegram_proxy_url, makes a user_config.yaml backup, and restarts containers only when the selected proxy changes.
Important: after updating repository scripts, run
make deployonce, then install schedule.
Custom emoji on buttons (Premium)
use_custom_emoji and id fields control button emoji:
| Mode | Behavior |
|---|---|
| Off (default) | Unicode (🐦, 🐿️, 📺) — visible to everyone |
| On | icon_custom_emoji_id — Telegram Premium required for bot owner |
When on, configure:
custom_emoji_id_bird— bird notificationscustom_emoji_id_chipmunk— squirrel/mousecustom_emoji_id_open_live— Open Live
If id missing — Unicode fallback.
How to get custom emoji id:
- Send the emoji to @RawDataBot — reply contains
custom_emoji_id. - Or @Stickers for pack ids.
- Paste numeric id (example:
5368324170671202286) into settings.
Web Push
Browser push (addition or alternative to Telegram). Requires HTTPS (or localhost).
| Key | Description |
|---|---|
web_push.enabled |
Auto-enabled on first UI subscription |
web_push.vapid_public_key |
Public VAPID (auto-generated) |
web_push.vapid_private_key |
Private VAPID (secret, masked in API) |
Setup: Settings → Notifications → Enable Web Push. Browser prompts; subscription stored server-side. Push to all subscribers on species detection.
Requirements: HTTPS (or localhost), general.enable_notifications, notifications.base_url for link in push. UI subscription now requires the same access as Settings (settings_check_access()), so a random LAN client cannot silently enable web_push.enabled.
UI
| Key | Description | Where |
|---|---|---|
unknown_confidence_threshold |
Threshold (0–1) for “Unknowns” list. Default 0.5 | Settings → Advanced |
Webhook
| Key | Description |
|---|---|
url |
POST URL per detection. JSON: species, confidence, time, source. IFTTT, Zapier, scripts |
Security limits: only http/https URLs are allowed. Private / loopback / link-local targets (127.0.0.1, 192.168.x.x, 10.x.x.x, localhost, etc.) are blocked so the webhook cannot be abused as an SSRF bridge into your internal network.
Trusted proxy: if Gunicorn is behind a trusted reverse proxy and you want rate limiting to honor X-Real-IP / X-Forwarded-For, set TRUSTED_PROXY=1. Otherwise BirdLense uses only remote_addr.
eBird
| Key | Description |
|---|---|
ebird.country |
Country code (2 letters: US, RU, …) |
ebird.state |
Region (1–3 chars: NY, CA, MOS for Moscow Oblast) |
ebird.location_name |
Location name for checklist |
ebird.protocol |
Stationary | Traveling | Incidental | Historical |
ebird.species_mapping |
eBird ↔ BirdLense for “Compare to region”. Example: Gray-headed Woodpecker: Grey-headed Woodpecker |
secrets.ebird_api_key |
eBird API for Overview “Compare to region”. Get key: ebird.org/api/keygen |
Settings → Advanced. Timeline “Export for eBird” does not need API key. Key is for “Compare to region”.
Semi-automatic mapping hints: Settings → Advanced, button next to ebird.species_mapping loads the regional eBird top and suggests lines (case / fuzzy); GET /api/ui/settings/ebird-species-mapping-suggestions (same access as settings). See #136.
The species filter Regional uses the same regional species list (eBird top in your ebird.* region) plus any species with a BirdNET MQTT detection (detection_provider = birdnet_mqtt in stored detections). See issue #132.
Example Russia, Moscow Oblast: ebird.country=RU, ebird.state=MOS (or MO). API region: RU-MOS.
MCP
| Key | Description |
|---|---|
enabled |
Enable MCP server |
token |
Access token (or MCP_TOKEN in env) |
Prometheus / Grafana
GET /metrics and GET /api/metrics — Prometheus format.
Prometheus — prometheus.yml:
scrape_configs:
- job_name: 'birdlense'
metrics_path: '/api/metrics'
static_configs:
- targets: ['birdlense:8085'] # or YOUR_HOST:port
scrape_interval: 15s
Metrics: CPU, memory, disk, GPU (if present), birdlense_detections_total, birdlense_species_count, birdlense_videos_total.
Optional (hub exposed beyond a trusted LAN): set BIRDLENSE_METRICS_TOKEN to a non-empty secret — then GET /metrics, GET /api/metrics, and GET /api/metrics/summary return 401 unless the request includes Authorization: Bearer <same token>. Configure your Prometheus scrape job with authorization / bearer credentials per Prometheus docs.
Grafana — Prometheus datasource, dashboard from metrics.
System page metrics history
Separate from Prometheus: SQLite table system_resource_sample, background sampler stores CPU/RAM/disk/GPU snapshots. The UI calls GET /api/ui/system/metrics/history.
| Environment variable | Default | Range | Purpose |
|---|---|---|---|
BIRDLENSE_SYSTEM_METRICS_INTERVAL_SEC |
30 |
10–600 | Seconds between samples |
BIRDLENSE_SYSTEM_METRICS_RETENTION_HOURS |
72 |
6–720 | Drop rows older than this (hours) |
DISABLE_SYSTEM_METRICS_SAMPLER |
— | 1 / true |
Disable sampler (tests, debugging) |
Alerting (Prometheus + Alertmanager)
Ready-made examples live in the repo (tune thresholds and job labels to match your scrape config):
| File | Purpose |
|---|---|
examples/prometheus/birdlense.rules.yml |
Alerts: target down, disk/memory/CPU pressure, optional “no new detections in 24h” |
examples/prometheus/alertmanager.birdlense.example.yml |
Minimal Alertmanager route / receivers skeleton |
Prometheus — add rule_files next to scrape_configs:
rule_files:
- 'birdlense.rules.yml' # path to the copied example
Notes:
- Default rules assume scrape
job_name: birdlense(seeup{job="birdlense"}). If you rename the job, update everyjob=matcher in the rule file. BirdlenseDetectionsUnchanged24his optional and noisy when the feeder is off-season — increasefor, mute in Alertmanager, or remove the rule groupbirdlense-optional-activity.- GPU alerts are not included:
birdlense_gpu_usage_percentis only exported when the container sees a usable GPU sysfs path; “stall” is better diagnosed via System → Processor logs and/api/ui/status.
Tracked as issue #57.
Secrets
Coordinates and API keys. Settings → Advanced. Prefer env for keys: OPENWEATHER_API_KEY.
| Key | Description |
|---|---|
openweather_api_key |
OpenWeather for weather widget |
xeno_canto_api_key |
Xeno-canto for bird songs (xeno-canto.org/account) |
ebird_api_key |
eBird “Compare to region” |
latitude, longitude |
Weather and eBird |
Operational rotation (backup, restart, verification, rollback): SECRETS_ROTATION.md.
Bird food (default catalog)
The app ships a curated default list of feeder foods (US + EU-oriented names and hints). Source of truth in code: app/web/seed/seed.py → seed_bird_food(). Image paths point at data/images/food/* in the app bundle.
Existing databases: on each startup, seed() merges any catalog entries missing by name — upgrades pick up new defaults without duplicating rows. The legacy catalog row Apple pieces is removed on startup (see seed.py), including clearing video_bird_food_association links. Operators can still add custom foods via GET / POST /api/ui/birdfood (see API.md).
Tracked as issue #134.
See also
INSTALL · ARCHITECTURE · ACCESS_CONTROL · API · SCENARIOS · GLOSSARY · SECRETS_ROTATION