From 896adcd85807ba13f0735c2a19e1611d324bbe5c Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 19 May 2026 08:32:34 -0400 Subject: [PATCH] fix(birdnet-pipy): serve API via gunicorn + gevent worker Replace the legacy Werkzeug dev server (python -m core.api) with a single gunicorn GeventWebSocketWorker, matching upstream BirdNET-PiPy 0.7.0's production-server migration (the add-on already builds 0.7.0 source). Single worker is mandatory: live detections fan out via an in-process Socket.IO emit (no Redis message_queue), so >1 worker would silently drop Live Feed broadcasts. Heavy maintenance jobs cooperatively yield to keep the worker responsive. Bound to 127.0.0.1:5002 for single-container use. Also fixes the now-stale 'Python core.api' comment in nginx/run. Add-on packaging change only -> 0.7.0 -> 0.7.0-1 (suffix convention; base tracks upstream via the updater bot). Co-Authored-By: Claude Opus 4.7 (1M context) --- birdnet-pipy/CHANGELOG.md | 3 +++ birdnet-pipy/config.yaml | 2 +- birdnet-pipy/rootfs/etc/services.d/api/run | 14 +++++++++++++- birdnet-pipy/rootfs/etc/services.d/nginx/run | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/birdnet-pipy/CHANGELOG.md b/birdnet-pipy/CHANGELOG.md index f75d55fdf8..1767681dd0 100644 --- a/birdnet-pipy/CHANGELOG.md +++ b/birdnet-pipy/CHANGELOG.md @@ -1,4 +1,7 @@ +## 0.7.0-1 (2026-05-19) +- Serve the API via gunicorn + a gevent-websocket worker instead of the Werkzeug development server + ## 0.7.0 (2026-05-19) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) diff --git a/birdnet-pipy/config.yaml b/birdnet-pipy/config.yaml index bd4ce0a1f3..4cd64c8e2c 100644 --- a/birdnet-pipy/config.yaml +++ b/birdnet-pipy/config.yaml @@ -96,4 +96,4 @@ schema: ssl: bool? slug: birdnet-pipy url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-pipy -version: "0.7.0" +version: "0.7.0-1" diff --git a/birdnet-pipy/rootfs/etc/services.d/api/run b/birdnet-pipy/rootfs/etc/services.d/api/run index d525a36ed4..27ac1b2eaf 100644 --- a/birdnet-pipy/rootfs/etc/services.d/api/run +++ b/birdnet-pipy/rootfs/etc/services.d/api/run @@ -4,4 +4,16 @@ set -euo pipefail export PYTHONPATH=/app bashio::net.wait_for 5001 127.0.0.1 300 cd /app -exec python3 -m core.api +# Single gevent-websocket worker is mandatory: live detections fan out via an +# in-process socketio.emit (no Redis message_queue), so >1 worker would +# silently drop Live Feed broadcasts. Bind 127.0.0.1 (single container; nginx +# and core.main reach the API locally, satisfying the wait_for in nginx/run + +# main/run). Heavy admin jobs cooperatively yield to keep this worker live. +exec gunicorn wsgi:application \ + --worker-class geventwebsocket.gunicorn.workers.GeventWebSocketWorker \ + --workers 1 \ + --bind 127.0.0.1:5002 \ + --timeout 120 \ + --graceful-timeout 30 \ + --keep-alive 65 \ + --error-logfile - diff --git a/birdnet-pipy/rootfs/etc/services.d/nginx/run b/birdnet-pipy/rootfs/etc/services.d/nginx/run index 3325a35c0f..aaa786aaef 100644 --- a/birdnet-pipy/rootfs/etc/services.d/nginx/run +++ b/birdnet-pipy/rootfs/etc/services.d/nginx/run @@ -2,7 +2,7 @@ # shellcheck shell=bash set -euo pipefail -# Wait for the upstream API service (Python core.api) to bind 127.0.0.1:5002 +# Wait for the upstream API service (gunicorn / wsgi:application) to bind 127.0.0.1:5002 # before starting nginx, so nginx never serves a 502 for the API/auth/socket # paths during boot. A boot-time 502 can be cached by an upstream cache layer # (HA's own PWA service worker, a Cloudflare edge worker, etc.), leaving the