Compare commits

..

2 Commits

Author SHA1 Message Date
alexbelgium
3fdaaeb481 style(birdnet-pi-zach): blank lines around the new CHANGELOG entry
Codacy flagged 3 new issues on this PR while the byte-identical 99-run.sh change
passed on #2972, which narrowed it to the CHANGELOG entry I wrote by hand.

The entry had no blank line after its heading and none before the heading below
it — MD022 (headings surrounded by blank lines) and MD032 (lists surrounded by
blank lines). Neither is disabled in .markdownlint.yaml; only MD001, MD002,
MD012, MD013, MD025, MD033 and MD041 are. The birdnet-pi entry for the same fix
has the blank line, which is why it passed.

The rest of this file has the same missing blank lines throughout, but those are
pre-existing lines that Codacy does not count and this change does not touch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 08:00:39 +02:00
alexbelgium
38423f4bff fix(birdnet-pi-zach): make ALSA_CARD actually select the microphone
Port of the birdnet-pi fix in #2972. birdnet-pi-zach is a sibling wrapper around
the zach7036 BirdNET-Pi fork, and carried the identical defect: 99-run.sh was
byte-identical to birdnet-pi's before the fix, and 01-structure.sh still is, so
this is an exact port rather than an adaptation — verified by diffing the result
against the fixed file (identical).

ALSA_CARD was copied straight into REC_CARD, but BirdNET-Pi passes REC_CARD to
`arecord -D` and `ffmpeg -f alsa -i`, which want an ALSA PCM name. A card index
like `1` became "Unknown PCM 1" and nothing recorded. It is now converted to
plughw:CARD=<value>,DEV=0, with values that already are PCM names passed through.

The sed also clobbered the symlink: 01-structure.sh links
$HOME/BirdNET-Pi/birdnet.conf -> /config/birdnet.conf (confirmed present in this
add-on, same CONFIG_FILES loop), and `sed -i` replaces a symlink with a regular
file, detaching it from the file the WebUI writes to. Now edits
/config/birdnet.conf with --follow-symlinks.

version 2026.07.10.2 -> 2026.08.15, matching the new CHANGELOG heading. This
add-on's updater.json has an empty upstream_version and is paused, so there is no
upstream prefix to derive a patch counter from; the family's date convention
governs instead. Without the bump Supervisor would never offer the rebuild and
the fix would ship inert.

Shellcheck: clean apart from two pre-existing SC2015 warnings in the unrelated
AUDIO_GID block, which are present on master and left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 07:57:39 +02:00
11 changed files with 19 additions and 122 deletions

View File

@@ -11,21 +11,10 @@ Read:
diff, verification, risk). This is your spec.
- `/tmp/ai-exec/issue.json` — the issue it fixes (for `Closes #<n>` and context).
## Hard limits (identical to the fix sweep; only limit 1 is machine-enforced)
## Hard limits (identical to the fix sweep — a workflow step enforces them)
1. **Never modify `.github/` or `.templates/`.** Repo-wide infrastructure.
2. **`config.yaml` is yours to edit, except the upstream part of `version`;
never edit `updater.json`** — `addons_updater` owns both. (There is no
`upstream:` key in `config.yaml`.) You must still
bump the local patch counter, or Supervisor never offers the rebuild and the
fix ships inert. Read `updater.json` to find the boundary — you cannot tell
it from `version` alone, since upstream versions here run to four or five
components. With `U` = `upstream_version`: if `version` equals `U`, **append**
`.1` (sonarr `4.0.19.3001` -> `4.0.19.3001.1`); if it is `U` + `.` + digits,
**increment** those digits (radarr `6.3.0.10514.1` -> `6.3.0.10514.2`);
anything else — no `updater.json`, drifted version, LSIO tag, date, nightly —
leave `version` alone and say so in the pull request body. A dot, never a
hyphen.
2. **Never touch the `version` or `upstream` fields in `config.yaml`.**
3. **One add-on, one branch:** `ai-fix/<addon>-<issue-number>`.
4. **Never merge, never close the issue, never enable auto-merge.** Open the
pull request **ready for review** — CI (`onpr_check-pr.yaml`) validates it,

View File

@@ -13,52 +13,14 @@ about confidence matters more than the number of pull requests you open.
## Hard limits
These are not guidelines. Limit 1 is machine-enforced — a workflow step checks
every pull request you open and blocks and flags anything that violates it. The
rest are on you: nothing checks them, so breaking one ships silently.
These are not guidelines. A workflow step enforces them after you finish, and
anything that violates them gets blocked and flagged.
1. **Never modify `.github/` or `.templates/`.** Those are inherited by every
add-on in the repo. A change there is a 100-add-on incident, not a fix.
2. **`config.yaml` is yours to edit, with one carve-out.** Never change the
*upstream part* of `version` — the portion that tracks the upstream release
— and never edit `updater.json` at all. The `addons_updater` job owns both,
and editing them causes merge conflicts you will not be around to resolve.
(There is no `upstream:` key in `config.yaml`; upstream tracking lives in
`updater.json` as `upstream_repo` / `upstream_version`.)
The **local patch counter** is a different thing and you must bump it. When
you change any file in an add-on, `version` must change too — otherwise
Supervisor never offers the rebuild, the add-on keeps running the old image,
and your fix ships inert: merged, doing nothing, with the issue looking
closed. That is worse than not fixing it at all.
**You cannot tell the counter from `version` alone — read `updater.json`.**
Upstream versions in this repo have anywhere from one to five components, so
a trailing `.1234` is just as likely to belong to upstream as to be a local
counter. `updater.json`'s `upstream_version` is the authority. Let `U` be
that value, and compare:
| `version` vs `U` | what to do | example |
|---|---|---|
| identical | **append** `.1` | sonarr `4.0.19.3001` -> `4.0.19.3001.1` |
| `U` + `.` + digits | **increment** those digits | radarr `6.3.0.10514.1` -> `6.3.0.10514.2` |
| anything else | **leave it alone** | plex, readarr, joal |
Getting this backwards corrupts data you do not own: sonarr's `4.0.19.3001`
*is* the upstream version, so "increment the last component" would produce
`4.0.19.3002` and burn the identifier of a future real release. 82 of the
add-ons in this repo are in that first row — appending is the common case,
incrementing the rare one.
Use a dot, never a hyphen: `X.Y.Z-N` parses as a semver pre-release, which
Supervisor treats as *older* than `X.Y.Z` and will not offer.
The third row is not a failure — it is the safe answer whenever the add-on
has no `updater.json`, its `version` has drifted from `upstream_version`, or
the format is exotic (LSIO tag `1.43.1.10611-1e34174b1-ls301`, a date, a
nightly). Do not guess a counter onto those. Leave `version` untouched and
say so — in the pull request body, or in the plan if this issue is going out
as Outcome B — so a human can decide.
2. **Never touch the `version` or `upstream` fields in `config.yaml`.** The
`addons_updater` job owns those. Editing them causes merge conflicts you
will not be around to resolve.
3. **One add-on per branch, one branch per pull request.** Branch name
`ai-fix/<addon>-<issue-number>`.
4. **Never merge, never close an issue, never enable auto-merge.** Opening a

View File

@@ -23,10 +23,7 @@ Consider only comments authored by `coderabbitai[bot]`. Ignore its collapsed
## Hard limits (a workflow step enforces the first)
1. **Never modify `.github/` or `.templates/`.** Repo-wide infrastructure.
2. **Leave `version` in `config.yaml` alone, and never edit `updater.json`.** The rest of
`config.yaml` is fair game. Unlike the fix sweep, you are amending a pull
request that has *already* bumped the local patch counter — one bump covers
the whole PR, so incrementing it again here would just churn the diff.
2. **Never touch the `version` or `upstream` fields in `config.yaml`.**
3. **Stay within this PR's scope and branch.** Do not open a new PR, do not
touch other add-ons, do not merge, do not mark ready/draft.

View File

@@ -161,17 +161,7 @@ the sweep), `ai:plan-pending` (plan posted, awaiting `ai:approved`), `ai:fixed`,
out of the automated tiers but not the manual ones. **Kill switch:** set the
repo variable `AI_DISABLED=true` to pause every AI workflow with no file edits.
AI fixes must never touch `.github/` or `.templates/` (enforced by
`ai_guard_paths.sh`). They may edit `config.yaml` freely except the upstream
part of `version`, and must never edit `updater.json` — `addons_updater` owns
both. (There is no `upstream:` key in `config.yaml`; upstream tracking lives in
`updater.json`.) They must still bump the local patch counter so Supervisor
offers the rebuild —
without it the fix ships inert. The counter boundary comes from
`updater.json`'s `upstream_version`, never from the shape of `version`: append
`.1` when the two are equal (the common case — upstream versions here run to
four or five components), increment the trailing digits only when `version` is
`upstream_version` + `.N`, and otherwise leave `version` alone. This rule is
prompt-only, not machine-enforced.
`ai_guard_paths.sh`) or the `version`/`upstream` fields in `config.yaml`.
## Linting Rules

View File

@@ -1,7 +1,7 @@
## 2026.08.15 (15-08-2026)
- Fix: `ALSA_CARD` now really selects the microphone. Its value was copied as-is into `REC_CARD`, but BirdNET-Pi hands `REC_CARD` to `arecord -D` / `ffmpeg -f alsa -i`, which expect an ALSA PCM name: a card index such as `1` gave `Unknown PCM 1` and no recording at all. It is now converted to `plughw:CARD=<value>,DEV=0`, while a value that already is a PCM name (`dsnoop:CARD=Audio,DEV=0`, `default`, `null`, `pulse`, `pipewire`, ...) is used as provided
- Fix: setting `ALSA_CARD` no longer replaces the `~/BirdNET-Pi/birdnet.conf` symlink with a detached copy of `/config/birdnet.conf` (now only `/config/birdnet.conf`, which the symlink points to, is updated)
- Fix: writing `REC_CARD` no longer detaches `birdnet.conf` from `/config`. `sed -i` replaced the `$HOME/BirdNET-Pi/birdnet.conf` symlink with a regular file, so later edits from the WebUI went to a different file than the one the add-on had written; it now edits `/config/birdnet.conf` with `--follow-symlinks`
## 2026.07.10-2 (10-07-2026)
- Minor bugs fixed

View File

@@ -101,17 +101,6 @@ Ensure you have the following installed on your system:
If rtsp feed doesn't work, perhaps you need to add "-rtsp-transport tcp" to your ffmpeg instruction, or allow udp on your network
### Selecting the microphone
By default the container records through PulseAudio (`REC_CARD=default` in `birdnet.conf`). To record directly from a USB microphone instead, find it on the host with `arecord -l`, then pass its card number (or its card id) as `ALSA_CARD`:
```yaml
environment:
- ALSA_CARD=1 # "card 1: Audio [KT USB Audio]" in the output of "arecord -l"
```
At startup this writes `REC_CARD=plughw:CARD=1,DEV=0` into your `birdnet.conf`. A value that already is a full ALSA PCM name, as listed by `arecord -L`, is used as provided - for example `ALSA_CARD=dsnoop:CARD=Audio,DEV=0`, which allows the recording and the livestream services to read the same microphone at the same time.
## Updating to the Latest Version
To check for new versions of the container and update:

View File

@@ -70,9 +70,8 @@ if [ -n "${ALSA_CARD:-}" ]; then
# "ffmpeg -f alsa -i" (scripts/livestream.sh), so it must be an ALSA PCM name.
# ALSA_CARD holds a card index (1) or a card id (Audio), which are not PCM names:
# writing them as-is gives "Unknown PCM 1" and no recording at all. Build a PCM
# name from them, unless the value already is one of ALSA's own PCM names
# (checked against "arecord -L", e.g. default, null, pulse, sysdefault, front...).
if [[ "$ALSA_CARD" == *:* ]] || arecord -L 2> /dev/null | grep -qx "$ALSA_CARD"; then
# name from them, and pass through a value that already is one (plughw:...).
if [[ "$ALSA_CARD" == *:* ]] || [[ "$ALSA_CARD" =~ ^(default|null|pulse|pipewire)$ ]]; then
REC_CARD="$ALSA_CARD"
else
REC_CARD="plughw:CARD=${ALSA_CARD},DEV=0"

View File

@@ -1,7 +1,3 @@
## 2026.08.15 (15-08-2026)
- Fix: `ALSA_CARD` now really selects the microphone. Its value was copied as-is into `REC_CARD`, but BirdNET-Pi hands `REC_CARD` to `arecord -D` / `ffmpeg -f alsa -i`, which expect an ALSA PCM name: a card index such as `1` gave `Unknown PCM 1` and no recording at all. It is now converted to `plughw:CARD=<value>,DEV=0`, while a value that already is a PCM name (`dsnoop:CARD=Audio,DEV=0`, `default`, `null`, `pulse`, `pipewire`, ...) is used as provided
- Fix: setting `ALSA_CARD` no longer replaces the `~/BirdNET-Pi/birdnet.conf` symlink with a detached copy of `/config/birdnet.conf` (now only `/config/birdnet.conf`, which the symlink points to, is updated)
## 2026.08.02 (02-08-2026)
- Fix: ingress returned "502 Bad Gateway" because Caddy never listened on :8082. `91-nginx_ingress.sh` hooked the ingress site into `update_caddyfile.sh` with a sed anchored on `sudo caddy fmt --overwrite`, but 2026.07.10-1 strips `sudo` from every BirdNET-Pi script at build time, so the anchor stopped matching. `update_caddyfile.sh` then rewrote the Caddyfile from scratch just before Caddy started, dropping the ingress site
- Fix: `caddy_ingress.sh` no longer appends a second `:8082` block when it runs twice (a duplicate site address makes Caddy refuse to start)

View File

@@ -101,17 +101,6 @@ Ensure you have the following installed on your system:
If rtsp feed doesn't work, perhaps you need to add "-rtsp-transport tcp" to your ffmpeg instruction, or allow udp on your network
### Selecting the microphone
By default the container records through PulseAudio (`REC_CARD=default` in `birdnet.conf`). To record directly from a USB microphone instead, find it on the host with `arecord -l`, then pass its card number (or its card id) as `ALSA_CARD`:
```yaml
environment:
- ALSA_CARD=1 # "card 1: Audio [KT USB Audio]" in the output of "arecord -l"
```
At startup this writes `REC_CARD=plughw:CARD=1,DEV=0` into your `birdnet.conf`. A value that already is a full ALSA PCM name, as listed by `arecord -L`, is used as provided - for example `ALSA_CARD=dsnoop:CARD=Audio,DEV=0`, which allows the recording and the livestream services to read the same microphone at the same time.
## Updating to the Latest Version
To check for new versions of the container and update:

View File

@@ -116,5 +116,5 @@ tmpfs: true
udev: true
url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-pi
usb: true
version: 2026.08.15
version: 2026.08.02
video: true

View File

@@ -66,26 +66,12 @@ fi || true
# Use ALSA CARD defined in add-on options if available
if [ -n "${ALSA_CARD:-}" ]; then
# REC_CARD is passed as-is to "arecord -D" (scripts/birdnet_recording.sh) and to
# "ffmpeg -f alsa -i" (scripts/livestream.sh), so it must be an ALSA PCM name.
# ALSA_CARD holds a card index (1) or a card id (Audio), which are not PCM names:
# writing them as-is gives "Unknown PCM 1" and no recording at all. Build a PCM
# name from them, unless the value already is one of ALSA's own PCM names
# (checked against "arecord -L", e.g. default, null, pulse, sysdefault, front...).
if [[ "$ALSA_CARD" == *:* ]] || arecord -L 2> /dev/null | grep -qx "$ALSA_CARD"; then
REC_CARD="$ALSA_CARD"
else
REC_CARD="plughw:CARD=${ALSA_CARD},DEV=0"
fi
bashio::log.warning "ALSA_CARD is defined, the birdnet.conf is adapted to use device $REC_CARD"
# --follow-symlinks : $HOME/BirdNET-Pi/birdnet.conf is a symlink to /config/birdnet.conf
# (01-structure.sh), and sed -i would replace it with a regular file, detaching it from
# the file the WebUI writes to. Only /config/birdnet.conf is updated directly, since the
# home-path symlink is writable by the pi/caddy user and could be repointed before this
# root-run script gets to it.
if [ -f /config/birdnet.conf ]; then
sed -i --follow-symlinks "/^REC_CARD/c\REC_CARD=$REC_CARD" /config/birdnet.conf
fi
bashio::log.warning "ALSA_CARD is defined, the birdnet.conf is adapt to use device $ALSA_CARD"
for file in "$HOME"/BirdNET-Pi/birdnet.conf /config/birdnet.conf; do
if [ -f "$file" ]; then
sed -i "/^REC_CARD/c\REC_CARD=$ALSA_CARD" "$file"
fi
done
fi
# Define permissions for audio