Compare commits

...

135 Commits

Author SHA1 Message Date
Alexandre
0030556525 Expose Claude Desktop headroom dashboard 2026-07-08 14:31:22 +02:00
Alexandre
905ce7bc0f Merge pull request #2830 from ToledoEM/fix/manyfold-assume-ssl-redirect
Fix/manyfold assume ssl redirect
2026-07-08 13:57:20 +02:00
Alexandre
19fe564e17 Add conditional export for ANTHROPIC_BASE_URL 2026-07-08 13:32:01 +02:00
Alexandre
6248cb3c21 Reorder gain report checks for better logic 2026-07-08 13:30:06 +02:00
ToledoEM
eff8577e37 Use latest tag for manyfold base image 2026-07-08 12:26:06 +01:00
github-actions
1bd0c6f054 GitHub bot: changelog [nobuild] 2026-07-08 11:22:49 +00:00
Alexandre
976e9bcc03 nobuild
Removed 3000/tcp port configuration from YAML.
2026-07-08 13:14:40 +02:00
Alexandre
1dab8b9344 Update config.yaml 2026-07-08 13:14:25 +02:00
ToledoEM
7667605e6b Fix manyfold HTTPS redirect loop 2026-07-08 12:11:09 +01:00
GitHub Actions
03e470a83a Revert "Update config.yaml"
This reverts commit b922673192.
2026-07-08 10:43:39 +00:00
Alexandre
b922673192 Update config.yaml 2026-07-08 12:41:24 +02:00
github-actions
ba37ee203e GitHub bot: changelog [nobuild] 2026-07-08 08:54:47 +00:00
Alexandre
55035b4ee8 Update config.yaml 2026-07-08 10:48:28 +02:00
Alexandre
fffec89eaf Update run 2026-07-08 07:33:47 +02:00
Alexandre
0073339f8c Merge pull request #2829 from alexbelgium/claude_desktop/hourly-gains-and-headroom-backend
Claude Desktop: hourly rtk + headroom gains report + headroom proxy backend
2026-07-07 22:10:45 +02:00
alexbelgium
e1a0f01fb9 claude_desktop: fix gains report headroom gating + data_location
- Gate the headroom section on install_headroom (bashio::config.true), same
  as svc-headroom, instead of only checking whether the binary is on PATH
  (it's pip-installed unconditionally at build time, so it's always
  present). have_headroom is now just a secondary availability guard.
  Fixes noisy/stale headroom output when the option is disabled.
- Switch the shebang to with-contenv bashio so HOME comes from the s6
  envdir instead of a hardcoded /data/data. 20-folders.sh only rewrites
  /data/data references under /defaults, /etc/cont-init.d,
  /etc/services.d and /etc/s6-overlay/s6-rc.d — not /usr/local/bin — so
  a custom data_location previously left this script reading/writing the
  wrong home directory.

Verified live: real cron firing confirms with-contenv correctly resolves
HOME and bashio::config from the s6 envdir when invoked by cron; isolated
gating-logic test covers all four enabled/binary-present combinations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:09:11 +02:00
alexbelgium
66b91278b9 claude_desktop: hourly rtk+headroom gains report + headroom proxy backend
- Add claude-gains-report.sh + /defaults/crontabs/root: an hourly rtk gain +
  headroom savings snapshot to the add-on log (heartbeat + accumulated gains).
- Add svc-headroom longrun: run the headroom proxy as a local MCP backend
  (127.0.0.1:8787, no client routing) so headroom_compress/headroom_retrieve
  actually store/retrieve content and record savings. Backend only, so the
  Claude Desktop app's traffic is untouched (headroom #869).
- Nudge headroom tool usage via a managed, idempotent CLAUDE.md block.
- Bump version 1.6 -> 1.7 and update CHANGELOG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 21:49:58 +02:00
github-actions
a70bf0f0f4 GitHub bot : README updated 2026-07-07 17:38:51 +00:00
Alexandre
1a128d62f2 Suppress output during caveman plugin installation
Redirect output of caveman plugin installation to /dev/null.
2026-07-07 18:11:29 +02:00
Alexandre
0fc40f339c Merge pull request #2827 from alexbelgium/claude/desktop-headroom-wrap-fix-r4qaqe
claude_desktop: don't wrap desktop launch with invalid headroom command
2026-07-07 16:52:41 +02:00
Claude
86395a85a0 claude_desktop: surface headroom MCP removal failures instead of swallowing them
The removal block's json.loads() was wrapped in a bare try/except that set
data = None on any parse error, then silently no-op'd (isinstance(data, dict)
is False) with exit 0 -- so a malformed claude_desktop_config.json meant the
headroom entry was never removed and bashio::log.warning never fired.

Drop the try/except so parse errors propagate naturally and the script exits
non-zero, matching the sibling rtk removal block's existing convention and
triggering the bashio::log.warning fallback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgMcG1QjsSHcDQBxgPpRUw
2026-07-07 14:35:52 +00:00
Claude
a9cf84077b claude_desktop: register headroom MCP server instead of proxy launch
headroom's proxy/wrap routing relies on ANTHROPIC_BASE_URL, which Claude
Desktop force-overrides (headroom #869), so it cannot transparently compress
the desktop app. Register headroom's MCP server (headroom mcp serve) in Claude
Desktop's claude_desktop_config.json instead -- the supported integration --
exposing the headroom_compress/headroom_retrieve/headroom_stats tools inside
the app. The plain desktop launch is left untouched.

The JSON merge is idempotent, preserves any other MCP servers and top-level
keys, backs up malformed config, and removes the entry again when
install_headroom is disabled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgMcG1QjsSHcDQBxgPpRUw
2026-07-07 14:24:38 +00:00
Claude
f65d5af969 claude_desktop: launch through headroom proxy instead of invalid wrap
headroom "wrap" only accepts coding-agent CLIs (claude, codex, cursor, ...)
with agent args after "--", so it cannot wrap the claude-desktop Electron app.
Route the launch through headroom's standalone compression proxy instead: the
command file now starts "headroom proxy" and points Claude Desktop at it via
ANTHROPIC_BASE_URL, while the autostart still falls back to a plain launch if
that fails.

Claude Desktop currently force-overrides ANTHROPIC_BASE_URL (headroom #869), so
transparent compression only engages once upstream adds Desktop support; until
then the app launches normally. Update the README/CHANGELOG accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgMcG1QjsSHcDQBxgPpRUw
2026-07-07 14:05:53 +00:00
Claude
74e25ba41b claude_desktop: don't wrap desktop launch with invalid headroom command
install_headroom defaults to true and headroom is baked into the image, so
82-claude_tools.sh rewrote the desktop launch to "headroom wrap claude-desktop
--no-sandbox ...". headroom "wrap" only accepts coding-agent CLIs (claude,
codex, cursor, ...) and expects agent arguments after a "--" separator, so
"claude-desktop" plus the Electron flags is an invalid wrap target/options.
The autostart ran only that command with no fallback, leaving the desktop app
unlaunched for default users.

- Keep the plain claude-desktop launch; just expose headroom with a usage hint
  (matches the documented "make headroom available and log a usage hint").
- Harden autostart to fall back to the default launch if a custom/wrapped
  command fails to start.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgMcG1QjsSHcDQBxgPpRUw
2026-07-07 13:18:11 +00:00
Alexandre
4d768ae1fa Merge pull request #2826 from alexbelgium/codex/fix-default-settings-for-claude-addon
Fix Claude Desktop optional tool startup
2026-07-07 15:03:18 +02:00
Alexandre
cc5d04c0f5 Fix Claude Desktop optional tool startup 2026-07-07 15:02:51 +02:00
Alexandre
f4343a6af4 Update Dockerfile 2026-07-07 14:42:25 +02:00
Alexandre
f5c6851344 Update Dockerfile 2026-07-07 13:46:08 +02:00
Alexandre
b873d57dca Update Dockerfile 2026-07-07 13:44:56 +02:00
Alexandre
4fc8f309f8 Merge pull request #2825 from alexbelgium/codex/add-default-local-folder-and-new-options
claude_desktop: switch default data_location to /data/data; add HA smart context, MCP and dangerous-permissions option; bump to v1.5
2026-07-07 13:39:31 +02:00
Alexandre
582d05815d Address Claude Desktop review comments 2026-07-07 13:37:54 +02:00
Alexandre
7ce85f9265 Merge pull request #2823 from alexbelgium/fix/claude-desktop-startup-and-keyring
fix(claude_desktop): fix startup crash loop and persist sign-in via g…
2026-07-07 13:25:38 +02:00
Alexandre
43a25654d0 Merge branch 'master' into fix/claude-desktop-startup-and-keyring 2026-07-07 13:25:14 +02:00
Alexandre
421101d7fe Merge pull request #2824 from alexbelgium/codex/review-and-enhance-claude_desktop-addon
Improve Claude Desktop tooling setup: add git/gh and ensure rtk/headroom ready
2026-07-07 13:24:21 +02:00
Alexandre
43fd7c8917 Improve Claude Desktop tooling setup 2026-07-07 13:23:52 +02:00
Alexandre Pary
a3c226320d fix(claude_desktop): fix startup crash loop and persist sign-in via gnome-keyring
Startup crash loop ("All subprocesses terminated. Exiting."):
- Drop the misplaced shm_size env var (Home Assistant ignores it) and run Claude
  Desktop with --disable-dev-shm-usage so the Electron renderer survives the default
  64 MB /dev/shm.
- Make the Selkies desktop init oneshots (init-video, init-selkies-config) tolerant so
  a partially-permitted device/permission op in the HA sandbox no longer fails add-on
  bringup and crash-loops the container.
- Pre-create /tmp/selkies_js.log so the base image's "chmod 777 /tmp/selkies*" calls
  never fail on an empty glob; reconcile XDG_RUNTIME_DIR to the tmpfs runtime dir.

Sign-in persistence ("Your sign-in won't be saved on this device"):
- Bundle gnome-keyring/libsecret-1-0/dbus-x11 and start an unlocked Secret Service in
  the desktop session, launching with --password-store=gnome-libsecret. The keyring DB
  lives on persistent storage (/config/data), so the session survives restarts.

Docs: add SIGN_IN.md documenting both problems and the (deferred) in-desktop browser
option needed to complete the initial OAuth login.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XycVEAj8oZgmQszn9gdQ2E
2026-07-07 13:09:11 +02:00
Alexandre
0bdfd371ca Merge pull request #2822 from alexbelgium/codex/fix-claude-desktop-addon-startup-issues
Fix Claude Desktop Selkies startup by creating s6 envdir and XDG runtime dir
2026-07-07 11:44:17 +02:00
Alexandre
a13f5a4259 Fix Claude Desktop Selkies startup 2026-07-07 11:43:50 +02:00
Alexandre
5b0c60370f Add files via upload 2026-07-07 10:24:47 +02:00
Alexandre
5ede162a72 Add files via upload 2026-07-07 10:24:32 +02:00
github-actions
6d8a15d249 GitHub bot: changelog [nobuild] 2026-07-07 08:21:51 +00:00
Alexandre
3bf20b4c5a Update 20-folders.sh 2026-07-07 10:08:37 +02:00
Alexandre
5c12288d68 Update config.yaml 2026-07-07 10:07:54 +02:00
Alexandre
4bfe3e4f9f Update config.yaml 2026-07-07 10:07:40 +02:00
Alexandre
a4560496be Update config.yaml 2026-07-07 10:07:22 +02:00
Alexandre
690abc8efe Merge pull request #2821 from alexbelgium/copilot/fix-failing-github-actions-job
fix(claude_desktop): set HOME=/root when running rtk install script
2026-07-07 10:06:56 +02:00
copilot-swe-agent[bot]
3c51a4176e Fix rtk binary install path in claude_desktop Dockerfile 2026-07-07 08:01:56 +00:00
GitHub Actions
dd5a8d0099 Revert "Update config.yaml"
This reverts commit 24fed3cb7c.
2026-07-07 07:59:42 +00:00
copilot-swe-agent[bot]
cf070880c8 Initial plan 2026-07-07 07:59:13 +00:00
Alexandre
24fed3cb7c Update config.yaml 2026-07-07 09:54:18 +02:00
Alexandre
9a3edd7b69 Merge pull request #2820 from alexbelgium/copilot/fix-failing-github-actions-job
Fix claude_desktop build: remove conflicting npm apt package
2026-07-07 09:53:40 +02:00
copilot-swe-agent[bot]
09a94d5b3b Fix claude_desktop build: remove conflicting npm apt package 2026-07-07 07:52:34 +00:00
copilot-swe-agent[bot]
8f325722cf Initial plan 2026-07-07 07:48:50 +00:00
GitHub Actions
a5ea1739c4 Revert "Update config.yaml"
This reverts commit 95d75d2acd.
2026-07-07 07:43:53 +00:00
Alexandre
95d75d2acd Update config.yaml 2026-07-07 09:41:12 +02:00
Alexandre
1eb403f44a nobuild
Add Claude Desktop Home Assistant add-on (Selkies)
2026-07-07 09:40:29 +02:00
Alexandre
b3bebb63f1 Update config.yaml 2026-07-07 09:40:11 +02:00
Alexandre
3703e6d599 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:38:19 +02:00
Alexandre
1ffc609b50 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:38:08 +02:00
Alexandre
e452f51957 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:38:01 +02:00
Alexandre
0438f41630 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:37:48 +02:00
Alexandre
10b0a80991 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:37:38 +02:00
Alexandre
74e2736c08 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:37:26 +02:00
Alexandre
7fb1989343 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:37:15 +02:00
Alexandre
e1cf37ff29 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 09:36:58 +02:00
Alexandre
20c7889a4e Remove binary assets from Claude Desktop add-on 2026-07-07 09:29:56 +02:00
Alexandre
f1daaa2ef6 Update config.yaml 2026-07-06 22:20:39 +02:00
github-actions
94c875b3d3 GitHub bot : README updated 2026-07-06 17:51:46 +00:00
github-actions
2d7054c2d2 GitHub bot: changelog [nobuild] 2026-07-06 11:36:07 +00:00
Alexandre
8abbaf00d5 Update config.yaml 2026-07-06 13:29:47 +02:00
github-actions
76464ed663 GitHub bot: sanitize (spaces + LF endings) & chmod [nobuild] 2026-07-06 07:28:38 +00:00
Alexandre
6ab3ded3da Update config.yaml 2026-07-06 09:27:48 +02:00
Alexandre
6f6bb17f0a Merge pull request #2816 from alexbelgium/claude/spotify-to-plex-addon-3blmet
Add Spotify to Plex add-on
2026-07-06 09:27:16 +02:00
Claude
15c0c296f4 Fail safely instead of degrading silently in spotify_to_plex startup
- Exit with a clear error if the /app/config migration fails, rather than
  continuing to run against the non-persistent source directory (user
  changes would otherwise silently vanish on the next restart).
- Exit with a clear error if /app is missing instead of swallowing a
  broken upstream image layout with `|| true`.

Addresses further review feedback from PR #2816.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NZTfSk3GQRU7oD85TnjsmW
2026-07-06 07:20:09 +00:00
Alexandre
f60574d6f8 Merge branch 'master' into claude/spotify-to-plex-addon-3blmet 2026-07-06 09:16:51 +02:00
Claude
00e5e20175 Fix encryption key format and config migration safety in spotify_to_plex
- Generate ENCRYPTION_KEY as 64 hex chars instead of base64: upstream reads
  it via Buffer.from(ENCRYPTION_KEY, 'hex') for aes-256-cbc, so a base64
  value silently broke Spotify token encryption for anyone leaving the
  option blank (the default path).
- Store the key without a trailing newline and chmod 600 it.
- Only delete /app/config after a successful copy into /config, so a
  failed migration (permissions, disk full) can't silently wipe the
  upstream default config.

Addresses review feedback from PR #2816.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NZTfSk3GQRU7oD85TnjsmW
2026-07-06 07:15:35 +00:00
Alexandre
725f42932d Add files via upload 2026-07-06 09:14:01 +02:00
github-actions
d32117dab6 GitHub bot: changelog [nobuild] 2026-07-06 07:12:33 +00:00
Alexandre
610c21b55e Update version to 0.146.1-2 in config.yaml 2026-07-06 09:10:17 +02:00
Alexandre
d9a62fa14e Update run.sh 2026-07-06 09:09:12 +02:00
Alexandre
91096f29da Merge pull request #2815 from alexbelgium/codex/add-public-host-name-option
Add Manyfold public hostname option
2026-07-06 09:02:59 +02:00
Claude
c1965de873 Add Spotify to Plex add-on
New add-on wrapping the upstream jjdenhertog/spotify-to-plex Docker image
(requested in issue #2814). Keeps Spotify playlists synced to Plex.

- Wraps the multi-arch upstream image via BUILD_FROM (amd64 + aarch64)
- Uses the shared ha_entrypoint framework with 00-global_var.sh so add-on
  options (Spotify credentials, redirect URI, encryption key) become the
  environment variables the app expects
- 99-run.sh persists /app/config into the add-on config dir, auto-generates
  and stores a stable ENCRYPTION_KEY when left blank, then hands off to the
  upstream supervisord
- Web UI exposed directly on port 9030

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NZTfSk3GQRU7oD85TnjsmW
2026-07-06 07:01:05 +00:00
Alexandre
b36ebeab59 Fix Manyfold public hostname detection 2026-07-06 08:52:51 +02:00
github-actions
3b57934ef2 GitHub bot: changelog [nobuild] 2026-07-06 05:48:31 +00:00
Alexandre
9513420051 Add Manyfold public hostname option 2026-07-06 07:44:05 +02:00
Alexandre
2255ec8259 Update config.yaml 2026-07-06 07:42:14 +02:00
github-actions
d3fdf52602 Github bot : image compressed 2026-07-05 23:26:27 +00:00
Alexandre
fb977d5d08 Merge pull request #2813 from alexbelgium/claude/birdnet-db-reboot-persistence-dzku9z
fix(birdnet-go): harden sqlite path rewrite (traversal + parent dir)
2026-07-05 22:44:39 +02:00
Claude
3bbb519ed0 fix(birdnet-go): reject path traversal and create parent dir for sqlite path rewrite
Addresses review feedback on the sqlite persistence fix (PR #2812):
- validate_safe_path now rejects ".." path segments so a relative
  output.sqlite.path can't traverse outside /config when rewritten.
- The rewrite now creates the destination's parent directory, since
  SQLite won't create one itself for a path like "db/birdnet.db".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KHJ4o22cdcgdNtb81tBTPJ
2026-07-05 20:42:36 +00:00
github-actions
dce470baf4 GitHub bot: changelog [nobuild] 2026-07-05 20:38:45 +00:00
Alexandre
d42108df4a Merge pull request #2812 from alexbelgium/claude/birdnet-db-reboot-persistence-dzku9z
fix(birdnet-go): persist sqlite database across restarts
2026-07-05 22:32:38 +02:00
Claude
5e94de0e59 fix(birdnet-go): persist sqlite database across restarts
Upstream's shipped default config.yaml explicitly sets
output.sqlite.path to the relative "birdnet.db", so the missing-only
("//=") seeding of that key never fired. A relative path resolves
against the app's ephemeral container working directory instead of the
persistent /config volume, so the database was silently recreated
empty on every restart.

Rewrite any relative output.sqlite.path to live under /config on
startup, leaving already-absolute (user-customized) paths untouched.

Fixes https://github.com/tphakala/birdnet-go/discussions/3774

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KHJ4o22cdcgdNtb81tBTPJ
2026-07-05 19:59:27 +00:00
github-actions
385f612ab1 GitHub bot: changelog [nobuild] 2026-07-05 18:43:33 +00:00
Alexandre
0c02125f13 Update config.yaml 2026-07-05 20:37:32 +02:00
github-actions
81a6c1cd5b GitHub bot: changelog [nobuild] 2026-07-05 18:32:40 +00:00
Alexandre
c659a7463b Update config.yaml 2026-07-05 20:27:04 +02:00
GitHub Actions
6ff1302d6d Revert "Update config.yaml"
This reverts commit a952e792df.
2026-07-05 18:19:53 +00:00
Alexandre
a952e792df Update config.yaml 2026-07-05 20:17:50 +02:00
GitHub Actions
551d7f46ce Revert "Update config.yaml"
This reverts commit 3bb1193a1e.
2026-07-05 18:16:03 +00:00
Alexandre
3bb1193a1e Update config.yaml 2026-07-05 20:13:59 +02:00
GitHub Actions
f05b57c656 Revert "Update config.yaml"
This reverts commit b97168ada5.
2026-07-05 18:10:11 +00:00
Alexandre
b97168ada5 Update config.yaml 2026-07-05 20:08:02 +02:00
GitHub Actions
8102231d57 Revert "Update config.yaml"
This reverts commit 63d67b5e4d.
2026-07-05 18:03:22 +00:00
Alexandre
63d67b5e4d Update config.yaml 2026-07-05 20:01:22 +02:00
github-actions
4aaca3e520 GitHub bot : README updated 2026-07-05 17:26:06 +00:00
Alexandre
c27aa2ab12 Merge pull request #2811 from Suncuss/fix/birdnet-pipy-version-ordering
fix(birdnet-pipy): re-tag 0.8.2-1 as 0.8.2.1 so HA offers it as an update
2026-07-05 18:47:09 +02:00
Yudong Sun
83fb6ed112 fix(birdnet-pipy): re-tag 0.8.2-1 as 0.8.2.1 so HA offers it as an update
awesomeversion (what HA's update entity uses since core's is-newer
check) parses "0.8.2-1" as semver with pre-release "1", and pre-releases
sort BELOW the base version: AwesomeVersion("0.8.2-1") >
AwesomeVersion("0.8.2") is False. Users stuck on the broken 0.8.2 nginx
release therefore see 0.8.2-1 as "Up-to-date" with a disabled Update
button and get no update notification. Four-segment 0.8.2.1 compares
strictly greater than both 0.8.2 and 0.8.2-1, and less than the next
upstream 0.8.3, so the fix becomes installable for everyone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016acGazhHvyCNUFt71xzFxm
2026-07-05 12:05:41 -04:00
github-actions
48dadb2ea0 GitHub bot: sanitize (spaces + LF endings) & chmod [nobuild] 2026-07-05 09:30:08 +00:00
Alexandre
528783dc8e Reapply "Build"
This reverts commit abc7e454f0.
2026-07-05 09:29:15 +02:00
GitHub Actions
abc7e454f0 Revert "Build"
This reverts commit ffc3a1bfe3.
2026-07-05 09:24:29 +00:00
GitHub Actions
594d875477 Revert "GitHub bot: sanitize (spaces + LF endings) & chmod [nobuild]"
This reverts commit 3bb31a2e80.
2026-07-05 09:24:29 +00:00
github-actions
3bb31a2e80 GitHub bot: sanitize (spaces + LF endings) & chmod [nobuild] 2026-07-05 09:18:24 +00:00
Alexandre
ffc3a1bfe3 Build 2026-07-05 11:17:41 +02:00
github-actions[bot]
fd087311a1 Update stargazer map & cache 2026-07-05 01:32:19 +00:00
github-actions
c2edee0131 GitHub bot: changelog [nobuild] 2026-07-04 17:48:42 +00:00
github-actions
24c1d3f017 GitHub bot: changelog [nobuild] 2026-07-04 17:47:07 +00:00
github-actions
e060cda578 GitHub bot: changelog [nobuild] 2026-07-04 17:46:31 +00:00
Alexandre
068f6ae8b1 Update version to 3.0.1-2 in config.yaml 2026-07-04 19:41:48 +02:00
Alexandre
de124527b5 Update version to 3.0.1-2 in config.yaml 2026-07-04 19:41:24 +02:00
Alexandre
b13bf44311 Update version to 3.0.1-2 in config.yaml 2026-07-04 19:40:54 +02:00
github-actions
13d4e552c0 GitHub bot: changelog [nobuild] 2026-07-04 17:37:29 +00:00
Alexandre
2affe938b5 Update version to 3.0.1-2 in config.yaml 2026-07-04 19:32:08 +02:00
Alexandre
24843a768a Merge pull request #2807 from alexbelgium/claude/immich-addons-v3-1cgj5x
Immich: fix build failure by dropping the redundant postgresql-client install
2026-07-04 19:31:24 +02:00
Claude
f2ae69f4e3 Immich: fail the build early if the base image ever drops postgresql-client
Per Copilot review feedback on #2807: add a build-time assertion for `psql`
so a future base-image change that drops postgresql-client is caught at
`docker build` time with a clear error, instead of surfacing as a cryptic
runtime failure in 99-run.sh. Also drop the hard-coded "14 through 18"
client version range from the comment, since that's specific to the
current base image and could go stale.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PcbbgB7A5LLbPuPRjLUouk
2026-07-04 17:30:09 +00:00
Claude
15c5a3b43d Immich: fix build failure by dropping the redundant postgresql-client install
The imagegenius v3 base image moved to a newer Ubuntu release where the
apt-key binary no longer exists, breaking the Dockerfiles' manual
PGDG-repo install of postgresql-client-15 (`wget ... | apt-key add -`).

That install was already redundant: imagegenius's own Dockerfile installs
postgresql-client-14 through 18 itself (via the modern signed-by keyring
method), so the psql CLI used by 99-run.sh is already present in the base
image. Remove the downstream install entirely rather than patching it to
use a keyring, since it duplicated work the base image already does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PcbbgB7A5LLbPuPRjLUouk
2026-07-04 17:25:16 +00:00
github-actions
fc60cfcab1 GitHub bot : README updated 2026-07-04 17:22:53 +00:00
Alexandre
e5a3e9b5af Merge pull request #2804 from Suncuss/fix/birdnet-pipy-limit-req-zone
fix(birdnet-pipy): nginx fails to start on 0.8.2 — build ingress.conf from the server block only
2026-07-04 19:11:47 +02:00
Alexandre
63500f714d Merge pull request #2805 from alexbelgium/claude/immich-addons-v3-1cgj5x
Immich: migrate immich, immich_cuda, immich_noml and immich_openvino to Immich v3
2026-07-04 19:08:53 +02:00
Alexandre
7802184b9c Merge pull request #2806 from juhenriquez/update-filebrowser-addon
Thanks
2026-07-04 19:06:52 +02:00
Juliano Henriquez
7272cd9aac Update Filebrowser addon 2026-07-04 11:15:54 -04:00
Claude
1d9ace0337 Immich: address Copilot PR review findings
- Fix check_vchord_extension (and check_vector_extension) to query
  pg_available_extensions against the actual immich database instead of
  pg_extension on the default connection database. Immich creates the vchord
  extension itself on first startup, so checking pg_extension before Immich
  ever runs produced a false warning on every fresh install; checking
  pg_available_extensions reports whether the server CAN provide the
  extension, which is what the startup diagnostic actually needs.
- Drop the vestigial `services: - mysql:want` Supervisor service-discovery
  hint from the four Immich config.yaml files: nothing in the add-on reads
  it, and the scripts are hard-coded to PostgreSQL via psql — Immich has
  never supported MySQL. Also fix the matching misleading line in the base
  README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PcbbgB7A5LLbPuPRjLUouk
2026-07-04 14:12:14 +00:00
Claude
275def9fb1 Immich: retarget addons_updater to immich-app/immich so v3+ is tracked
imagegenius/docker-immich stopped publishing GitHub releases (its newest release
is a "2026.0.0" announcement to that effect) and ships v3 only to GHCR, so
lastversion was stuck returning 2.7.5 and the add-ons never updated past v2.

Point the four Immich updater.json files at the real product repo
immich-app/immich, which publishes clean stable vX.Y.Z releases (currently
v3.0.1). The build.json images stay on the imagegenius :3 rolling tag, which
serves the newest 3.x build, keeping the tracked version and the pulled image
coherent. Also clear the now-obsolete github_exclude "2026" guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PcbbgB7A5LLbPuPRjLUouk
2026-07-04 13:48:28 +00:00
Yudong Sun
809d5271ef fix(birdnet-pipy): build ingress.conf from the server block only
Upstream 0.8.2 added http-level limit_req_zone directives to its
nginx.conf. The ingress config was a wholesale copy of that file, and
both land in the same http context via servers/*.conf — nginx refuses
to declare a named shared-memory zone twice and dies at startup with
'limit_req_zone "api_rl" already bound' (502 on every page). Extract
everything from the column-0 "server {" onward instead, so maps and
zones stay declared once; zone/variable references resolve across
included files regardless of include order. Also comment out the new
Content-Security-Policy header in the ingress copy, matching the
existing X-* handling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016acGazhHvyCNUFt71xzFxm
2026-07-04 09:37:02 -04:00
Claude
548692a028 Immich: migrate immich, immich_cuda, immich_noml and immich_openvino to Immich v3
- Pin build.json to the imagegenius v3 image line (:3, :3-cuda, :3-noml, :3-openvino)
- Bump config.yaml version to 3.0.1 and record upstream_version 3.0.1 in updater.json
- Add a non-fatal VectorChord (vchord) startup check in the shared 99-run.sh so users
  on a non-VectorChord database get a clear diagnostic (v3 drops pgvecto.rs)
- Document the Immich v3 database (VectorChord) and CPU requirements in each README and
  point users at the Postgres 15 / Postgres 17 add-ons
- CHANGELOG entries for all four add-ons

The Postgres 15 / Postgres 17 add-ons already ship the official immich VectorChord image
(vectorchord0.4.3, matching Immich v3's pinned database) and are intentionally left
unchanged to preserve the pgvecto.rs to VectorChord migration path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PcbbgB7A5LLbPuPRjLUouk
2026-07-04 13:34:21 +00:00
github-actions
b3533d0c01 Github bot : issues linked to readme 2026-07-04 06:34:13 +00:00
231 changed files with 4198 additions and 104 deletions

View File

@@ -33,6 +33,7 @@ AbeltjeNL,
Acid-base,
Actpohomoc,Ukraine
Addonniss,
Adouchee,France
Adrian-Rosoga,United Kingdom
Aersaud,United States
Ag4m3mN0n,
@@ -603,6 +604,7 @@ RicLund,Canada
Richyread,
RimmeVG,
Rimpla,
Rivlin77,
Robbot,Ireland
RobertZwets,
RocketKuhr,
@@ -650,6 +652,7 @@ Senpa,Italy
SeriousM,Austria
Seshpenguin,Canada
Shadoweee77,
SharkyRawr,Germany
ShauneBoy,
Shurik85,
SieGoz,
@@ -724,6 +727,7 @@ TheSpaceGod,
Theboldhog,
Theswedishgold,
Thib1404,
ThomasADavis,
Thullalong69,Germany
Tifeko,
TiiMMY2CHAiiNZ,
@@ -1153,7 +1157,7 @@ danctrl,Germany
danez,United States
danieldotnl,Netherlands
dannybeeckman,
dannybloomfield,
dannybloomfield,United States
danveitch76,
darcouk,
darianferrer,United Kingdom
@@ -1394,6 +1398,7 @@ geniusboywonder,
gentslava,
geoffreylagaisse,Belgium
georgek42,
georgemitsionis,United Kingdom
georgiasouthernbcilab,
gerbal,United States
gericho,
@@ -1559,6 +1564,7 @@ jdungen,Netherlands
jeff89179,United States
jeffest-jeff,United States
jeffreyphillips25907jeffreyphillips,
jeffreyvangorkum,Netherlands
jefmenegazzo,Brazil
jeko89,
jeroenmartens,
@@ -1584,6 +1590,7 @@ joao-ribafeita,
joaolpinho,Portugal
jodfie,United States
joe2k9b,
joe8628,
joealden,United Kingdom
joeck,
joetyson,United States
@@ -1798,6 +1805,7 @@ marcschraepler,Austria
marcusrbrown,
mareklab,
marevers,Germany
marian-paun,Romania
mariusvslprts,Germany
markcshaz,
markist,
@@ -2160,6 +2168,7 @@ richmaster1343,
rickwilliams08,
rigerc,
rikardronnkvist,Sweden
rindlerblabla,
rine77,
ringzero24,
rj10rd,
1 username country
33 Acid-base
34 Actpohomoc Ukraine
35 Addonniss
36 Adouchee France
37 Adrian-Rosoga United Kingdom
38 Aersaud United States
39 Ag4m3mN0n
604 Richyread
605 RimmeVG
606 Rimpla
607 Rivlin77
608 Robbot Ireland
609 RobertZwets
610 RocketKuhr
652 SeriousM Austria
653 Seshpenguin Canada
654 Shadoweee77
655 SharkyRawr Germany
656 ShauneBoy
657 Shurik85
658 SieGoz
727 Theboldhog
728 Theswedishgold
729 Thib1404
730 ThomasADavis
731 Thullalong69 Germany
732 Tifeko
733 TiiMMY2CHAiiNZ
1157 danez United States
1158 danieldotnl Netherlands
1159 dannybeeckman
1160 dannybloomfield United States
1161 danveitch76
1162 darcouk
1163 darianferrer United Kingdom
1398 gentslava
1399 geoffreylagaisse Belgium
1400 georgek42
1401 georgemitsionis United Kingdom
1402 georgiasouthernbcilab
1403 gerbal United States
1404 gericho
1564 jeff89179 United States
1565 jeffest-jeff United States
1566 jeffreyphillips25907jeffreyphillips
1567 jeffreyvangorkum Netherlands
1568 jefmenegazzo Brazil
1569 jeko89
1570 jeroenmartens
1590 joaolpinho Portugal
1591 jodfie United States
1592 joe2k9b
1593 joe8628
1594 joealden United Kingdom
1595 joeck
1596 joetyson United States
1805 marcusrbrown
1806 mareklab
1807 marevers Germany
1808 marian-paun Romania
1809 mariusvslprts Germany
1810 markcshaz
1811 markist
2168 rickwilliams08
2169 rigerc
2170 rikardronnkvist Sweden
2171 rindlerblabla
2172 rine77
2173 ringzero24
2174 rj10rd

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 63 KiB

BIN
.github/stats.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -56,7 +56,7 @@ If you want to do add the repository manually, please follow the procedure highl
### Number of addons
- In the repository : 132
- In the repository : 135
- Installed : 508490
### Top 3
@@ -176,6 +176,18 @@ If you want to do add the repository manually, please follow the procedure highl
![smb][smb-badge]
![localdisks][localdisks-badge]
&#10003; ![image](https://api.iconify.design/mdi/bird.svg) [Birdnet-go (custom version)](birdnet-go-dev/) : Realtime BirdNET soundscape analyzer, compiled from the alexbelgium/birdnet-go fork with all open PRs merged, with OpenVINO enabled for Intel CPU/iGPU acceleration (amd64-only test build)
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbirdnet-go-dev%2Fconfig.yaml)
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbirdnet-go-dev%2Fupdater.json)
![aarch64no][aarch64no-badge]
![amd64][amd64-badge]
![ingress][ingress-badge]
![MariaDB][mariadb-badge]
![mqtt][mqtt-badge]
![smb][smb-badge]
![localdisks][localdisks-badge]
&#10003; [Booksonic air](booksonic_air/) : platform for accessing the audiobooks you own wherever you are
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbooksonic_air%2Fconfig.yaml)
@@ -235,6 +247,14 @@ If you want to do add the repository manually, please follow the procedure highl
![amd64][amd64-badge]
![ingress][ingress-badge]
&#10003; ![image](https://api.iconify.design/mdi/robot-happy.svg) [Claude Desktop](claude_desktop/) : Claude Desktop streamed through a browser with LinuxServer Selkies
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fclaude_desktop%2Fconfig.yaml)
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fclaude_desktop%2Fupdater.json)
![aarch64][aarch64-badge]
![amd64][amd64-badge]
![ingress][ingress-badge]
&#10003; ![image](https://api.iconify.design/mdi/movie-search.svg) [Cleanuparr](cleanuparr/) : Automatically removes stuck and unwanted downloads from your *arr and download clients
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fcleanuparr%2Fconfig.yaml)
@@ -428,7 +448,6 @@ If you want to do add the repository manually, please follow the procedure highl
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fimmich%2Fupdater.json)
![aarch64][aarch64-badge]
![amd64][amd64-badge]
![MariaDB][mariadb-badge]
![smb][smb-badge]
![localdisks][localdisks-badge]
@@ -438,7 +457,6 @@ If you want to do add the repository manually, please follow the procedure highl
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fimmich_cuda%2Fupdater.json)
![aarch64no][aarch64no-badge]
![amd64][amd64-badge]
![MariaDB][mariadb-badge]
![smb][smb-badge]
![localdisks][localdisks-badge]
@@ -455,7 +473,6 @@ If you want to do add the repository manually, please follow the procedure highl
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fimmich_openvino%2Fupdater.json)
![aarch64no][aarch64no-badge]
![amd64][amd64-badge]
![MariaDB][mariadb-badge]
![smb][smb-badge]
![localdisks][localdisks-badge]
@@ -472,7 +489,6 @@ If you want to do add the repository manually, please follow the procedure highl
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fimmich_noml%2Fupdater.json)
![aarch64][aarch64-badge]
![amd64][amd64-badge]
![MariaDB][mariadb-badge]
![smb][smb-badge]
![localdisks][localdisks-badge]
@@ -901,6 +917,13 @@ If you want to do add the repository manually, please follow the procedure highl
![smb][smb-badge]
![localdisks][localdisks-badge]
&#10003; ![image](https://api.iconify.design/mdi/spotify.svg) [Spotify to Plex](spotify_to_plex/) : Sync Spotify playlists to Plex
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fspotify_to_plex%2Fconfig.yaml)
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fspotify_to_plex%2Fupdater.json)
![aarch64][aarch64-badge]
![amd64][amd64-badge]
&#10003; ![image](https://api.iconify.design/mdi/chart-timeline.svg) [Spotweb](spotweb/) : Spotweb is a decentralized usenet community based on the Spotnet protocol
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fspotweb%2Fconfig.yaml)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

495
birdnet-go-dev/CHANGELOG.md Normal file
View File

@@ -0,0 +1,495 @@
## source-20260708-2 (08-07-2026)
- Minor bugs fixed
## source-20260708 (08-07-2026)
- Minor bugs fixed
## source-20260706-update (06-07-2026)
- Minor bugs fixed
## source-20260706 (06-07-2026)
- Minor bugs fixed
## source-20260705-3 (05-07-2026)
- Harden the `output.sqlite.path` rewrite added for the persistence fix: reject paths containing `..` traversal segments (shared `validate_safe_path` check), and create the destination's parent directory under `/config` when the relative path includes a subdirectory (e.g. `db/birdnet.db`), since SQLite does not create missing parent directories itself.
## source-20260705-2 (05-07-2026)
- Fix detections/database not persisting across restarts on a fresh install: upstream's default `config.yaml` ships `output.sqlite.path: birdnet.db` (relative) explicitly, so the missing-only (`//=`) seeding introduced previously never rewrote it to an absolute path. A relative path resolves against the app's ephemeral working directory, so the database was silently recreated empty on every restart. Any relative `output.sqlite.path` is now rewritten to live under the persistent `/config` on startup; values already set to an absolute path are left untouched. (https://github.com/tphakala/birdnet-go/discussions/3774)
## source-20260705 (05-07-2026)
- Minor bugs fixed
## source-2026070 (05-07-2026)
- Minor bugs fixed
## source-20260703 (03-07-2026)
- Minor bugs fixed
## source-20260702-em (02-07-2026)
- Minor bugs fixed
## source-20260702-2 (02-07-2026)
- Minor bugs fixed
## source-20260701 (01-07-2026)
- Minor bugs fixed
## source-20260629-3 (29-06-2026)
- Minor bugs fixed
## source-20260629-2 (29-06-2026)
- Minor bugs fixed
## source-20260629 (29-06-2026)
- Minor bugs fixed
## source-20260627-v3 (28-06-2026)
- Minor bugs fixed
## source-20260627-v2 (27-06-2026)
- Minor bugs fixed
## source-20260627 (27-06-2026)
- Minor bugs fixed
## source-20260626-5 (26-06-2026)
- Minor bugs fixed
## source-20260626-4 (26-06-2026)
- Minor bugs fixed
## source-20260626-3 (26-06-2026)
- Minor bugs fixed
## source-20260626 (26-06-2026)
- Minor bugs fixed
## source-20260625-14 (26-06-2026)
- Minor bugs fixed
## source-20260625-13 (25-06-2026)
- Minor bugs fixed
## source-20260625-12 (25-06-2026)
- Minor bugs fixed
## source-20260625-11 (25-06-2026)
- Minor bugs fixed
## source-20260625-10 (25-06-2026)
- Minor bugs fixed
## source-20260625-5 (25-06-2026)
- Minor bugs fixed
## source-20260625-4 (25-06-2026)
- Minor bugs fixed
## source-20260625-3 (25-06-2026)
- Minor bugs fixed
## source-20260625-2 (25-06-2026)
- Minor bugs fixed
## source-20260625 (25-06-2026)
- Minor bugs fixed
## source-20260624-6 (24-06-2026)
- Minor bugs fixed
## source-20260624-5 (24-06-2026)
- Minor bugs fixed
## source-20260624-4 (24-06-2026)
- Minor bugs fixed
## source-20260624-3 (24-06-2026)
- Minor bugs fixed
## source-20260624-2 (24-06-2026)
- Minor bugs fixed
## source-20260623-3 (23-06-2026)
- Minor bugs fixed
## source-20260623-2 (23-06-2026)
- Minor bugs fixed
## source-20260623 (23-06-2026)
- Minor bugs fixed
## source-20260622 (23-06-2026)
- Minor bugs fixed
## source-20260621-5 (22-06-2026)
- Minor bugs fixed
## source-20260621-4 (22-06-2026)
- Minor bugs fixed
## source-20260621-3 (22-06-2026)
- Minor bugs fixed
## source-20260621-2 (22-06-2026)
- Minor bugs fixed
## source-20260621-1 (21-06-2026)
- Fix OpenVINO load failure: bundle oneTBB (libtbb.so.12) from OpenVINO 3rdparty libs so libopenvino_c.so resolves at runtime
## source-20260620-13 (21-06-2026)
- Minor bugs fixed
## source-20260620-12 (21-06-2026)
- Minor bugs fixed
## source-20260620-11 (21-06-2026)
- Minor bugs fixed
## source-20260620-10 (21-06-2026)
- Minor bugs fixed
## source-20260620-7 (21-06-2026)
- Minor bugs fixed
## source-20260620-6 (21-06-2026)
- Minor bugs fixed
## source-20260620-5 (21-06-2026)
- Minor bugs fixed
## source-20260620-4 (21-06-2026)
- Minor bugs fixed
## source-20260620-3 (21-06-2026)
- Minor bugs fixed
## source-20260620-2 (21-06-2026)
- Minor bugs fixed
## source-20260620 (21-06-2026)
- Minor bugs fixed
## source-20260619-8 (20-06-2026)
- Minor bugs fixed
## source-20260619-7 (20-06-2026)
- Minor bugs fixed
## source-20260619-6 (20-06-2026)
- Minor bugs fixed
## source-20260619-5 (20-06-2026)
- Minor bugs fixed
## source-20260619-4 (20-06-2026)
- Minor bugs fixed
## source-20260619-3 (20-06-2026)
- Minor bugs fixed
## source-20260619-2 (20-06-2026)
- Minor bugs fixed
## source-20260619 (20-06-2026)
- Minor bugs fixed
## source-20260618-3 (18-06-2026)
- Minor bugs fixed
## source-20260618-2 (18-06-2026)
- Minor bugs fixed
## source-20260617-4 (18-06-2026)
- Minor bugs fixed
## source-20260617-3 (17-06-2026)
- Minor bugs fixed
## source-20260617-2 (17-06-2026)
- Minor bugs fixed
## source-20260617 (17-06-2026)
- Minor bugs fixed
## source-20260616-2 (16-06-2026)
- Minor bugs fixed
## source-20260616 (16-06-2026)
- Minor bugs fixed
## source-20260615bats (15-06-2026)
- Minor bugs fixed
## source-20260615 (15-06-2026)
- Minor bugs fixed
## source-20260614-2 (14-06-2026)
- Minor bugs fixed
## source-20260614 (14-06-2026)
- Minor bugs fixed
## source-20260613 (13-06-2026)
- Minor bugs fixed
## source-20260612-4 (13-06-2026)
- Minor bugs fixed
## source-20260612-3 (12-06-2026)
- Minor bugs fixed
## source-20260612-2 (12-06-2026)
- Minor bugs fixed
## source-20260612 (12-06-2026)
- Minor bugs fixed
## source-20260610-9 (11-06-2026)
- Minor bugs fixed
## source-20260610-8 (11-06-2026)
- Minor bugs fixed
## source-20260610-7 (11-06-2026)
- Minor bugs fixed
## source-20260610-6 (11-06-2026)
- Minor bugs fixed
## source-20260610-5 (11-06-2026)
- Minor bugs fixed
## source-20260610-4 (10-06-2026)
- Minor bugs fixed
## source-20260610-3 (10-06-2026)
- Minor bugs fixed
## source-20260610-2 (10-06-2026)
- Minor bugs fixed
## source-20260610 (10-06-2026)
- Minor bugs fixed
## source-20260608-8 (09-06-2026)
- Minor bugs fixed
## source-20260608-7 (09-06-2026)
- Minor bugs fixed
## source-20260608-6 (08-06-2026)
- Minor bugs fixed
## source-20260608-5 (08-06-2026)
- Minor bugs fixed
## source-20260608-4 (08-06-2026)
- Minor bugs fixed
## source-20260608-3 (08-06-2026)
- Minor bugs fixed
## source-20260608-2 (08-06-2026)
- Minor bugs fixed
## source-20260608 (08-06-2026)
- Minor bugs fixed
## source-20260607-4 (07-06-2026)
- Minor bugs fixed
## source-20260607-3 (07-06-2026)
- Minor bugs fixed
## source-20260607-2 (07-06-2026)
- Minor bugs fixed
## source-20260607 (07-06-2026)
- **Test variant** of the birdnet-go add-on that compiles BirdNET-Go from the `alexbelgium/birdnet-go` fork instead of pulling the prebuilt `ghcr.io/tphakala/birdnet-go` image.
- At build time, `merge-prs.sh` syncs the fork's `main` with the `tphakala/birdnet-go` upstream and merges every open non-draft ("in review") pull request on the fly, so the resulting binary is upstream main + all work currently under review.
- Home Assistant integration layers (nginx ingress, modules, init scripts, options handling) are identical to the standard birdnet-go add-on.
- Published as a separate image (`ghcr.io/alexbelgium/birdnet-go-source-{arch}`) so it never overwrites the production add-on image.
- Fix (`01-structure.sh`): create the absolute `BIRDSONGS_FOLDER` target (e.g. the default `/config/clips`) before migrating clips from a legacy `/data/clips`, so upgrades with existing recordings no longer abort startup under `set -e`.
## nightly-20260601-2 (03-06-2026)
- Minor bugs fixed
## nightly-20260601 (2026-06-01)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260524 (2026-05-30)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260429-405 (2026-05-30)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260525-3 (28-05-2026)
- New `mqtt_auto_config` addon option (default `false`). When `true` and the Home Assistant MQTT addon is active, `realtime.mqtt.{enabled,broker,username,password}` are written directly to `config.yaml` on every restart. When `false` but Mosquitto is detected, the addon still logs the broker details and reminds you about the option — nothing is written.
- New `mariadb_auto_config` addon option (default `false`). When `true` and the Home Assistant MariaDB addon is active, `output.mysql.*` is filled in and `output.sqlite.enabled` is set to `false`. When `false` but MariaDB is detected, the addon logs the credentials and reminds you about the option.
- **Breaking**: `output.sqlite.path` and `logging.file_output.*` are now seeded only when missing from `config.yaml` (previously overwritten every restart). Values changed through the BirdNET-Go UI or by hand-editing `config.yaml` now survive container restarts. If you relied on `LOG_MAX_SIZE_MB` / `LOG_MAX_AGE_DAYS` addon options to override an existing setting in `config.yaml`, remove the existing key from `config.yaml` or edit it directly — the option will only be applied on first run.
- **Breaking (UI only)**: The nginx ingress reverse-proxy no longer rewrites HTML `href`/`src`/`action` attributes; upstream BirdNET-Go handles those itself via `X-Ingress-Path`. JavaScript string-literal rewrites are unchanged. Please file an issue if you see broken images, links, or forms in the ingress UI after upgrade.
- Fix database-migration restore: the timestamped backup created during a `BIRDSONGS_FOLDER` change was being written to the script's working directory and looked up under a fresh timestamp on restore, so a SQL failure left the user unable to recover. Backup path is now absolute and reused for restore.
- Harden the `BIRDSONGS_FOLDER` SQL/YAML path substitution: paths containing characters outside `[A-Za-z0-9._/-]` are now rejected up front instead of being interpolated raw into the SQL UPDATE statement.
- Tolerate a missing internet connection on first boot: if the default `config.yaml` cannot be downloaded from GitHub, the init script now seeds an empty YAML document so the addon-defaults block populates a usable config (rather than aborting the script on the next `yq` call under `set -e`).
- Warn (without failing the build) if the upstream `entrypoint.sh` patch target drifts in a new nightly.
- Remove a dead nginx upstream definition that pointed at an unused port.
## nightly-20260525-2 (26-05-2026)
- Suppress noisy startup logs: silence `chmod /dev/snd` errors on the read-only HA mount, and hide unavailable ALSA plugins (JACK, OSS, dsnoop) from device enumeration so libjack and pcm_oss/dsnoop probes no longer print at launch. ALSA overrides are written to `/root/.asoundrc` (since `/etc/asound.conf` is read-only in this environment).
- Allow advanced users to override the ALSA config by dropping a custom `asound.conf` into the addon config folder.
- Add LOG_MAX_SIZE_MB and LOG_MAX_AGE_DAYS addon options to manage log storage size
- Automatically trim log files exceeding configured age on startup
## nightly-20260525 (26-05-2026)
- Minor bugs fixed
## nightly-20260511-414-2 (22-05-2026)
- Minor bugs fixed
## nightly-20260511-414 (2026-05-16)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260429-405 (2026-05-02)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260321-397 (2026-03-26)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260315 (2026-03-21)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260311 (2026-03-14)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260118-2 (17-02-2026)
- Minor bugs fixed
## nightly-20260118 (2026-01-21)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260113 (2026-01-14)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260111 (2026-01-12)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20260110 (2026-01-10)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20251223-2 (2025-12-27)
- Minor bugs fixed
## nightly-20251224 (2025-12-24)
- Minor bugs fixed
## nightly-20251223 (2025-12-23)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20251214 (2025-12-20)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
- Added support for configuring extra environment variables via the `env_vars` add-on option alongside config.yaml. See https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2 for details.
- Preserve the microphone selected in the BirdNET-Go UI unless the `homeassistant_microphone` option explicitly forces the default device.
## "nightly-20251028" (2025-11-01)
- Minor bugs fixed
## nightly-20251028 (2025-11-01)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## "nightly-20251012" (2025-10-18)
- Minor bugs fixed
## nightly-20251012 (2025-10-18)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20251008 (2025-10-11)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20250904_6 (2025-09-17)
- New option "homeassistant_microphone". If set to true, will use homeassistant's microphone by setting the audio_card to "default". Please use the addon options to select the device to which "default" is allocated
## nightly-20250904 (2025-09-06)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20250826 (2025-08-30)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20250813 (2025-08-16)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20250805 (2025-08-09)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20250731-4 (2025-08-04)
- Minor bugs fixed
## nightly-20250731-3 (2025-08-04)
- Minor bugs fixed
## nightly-20250731-2 (2025-08-02)
- Minor bugs fixed
## nightly-20250731 (2025-08-01)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20250730 (2025-07-30)
- Minor bugs fixed
## nightly-20250725-2 (2025-07-28)
- Fix /asset path
- Added 9090 telemetry port
## nightly-20250725 (2025-07-25)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## nightly-20250710 (2025-07-12)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 20250710 (2025-07-12)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 20250704 (2025-07-07)
- Minor bugs fixed
## 20250508 (2025-07-05)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 20250419 (2025-05-17)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 20250427-7 (2025-05-15)
- Breaking change: COMMAND addon option removed. Please instead use the config.yaml to define the RTSP feeds
- Use entrypoint
## 20250427-2 (2025-04-27)
- Minor bugs fixed
## 20250427 (2025-04-27)
- Minor bugs fixed
## 20250316 (2025-04-26)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 0.6.4-3 (2025-04-07)
- Minor bugs fixed
## 0.6.4-2 (2025-03-30)
- Minor bugs fixed
## 0.6.4 (2025-03-17)
- Minor bugs fixed
## 0.6.3 (2025-03-15)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 0.6.2-2 (2025-02-21)
- Minor bugs fixed
## 0.6.2 (2025-02-21)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 20250126-2 (2025-02-21)
- Minor bugs fixed
## 20250126 (2025-02-15)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 0.6.0-nightly-20250124 (2025-01-25)
- Minor bugs fixed
## 0.6.0-4 (2025-01-21)
- Fix sounds play
- Correct sqlite for //
## 0.6.0 (2025-01-18)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 20250103-10 (2025-01-17)
- BREAKING CHANGE : improve implementation of addon options such as Birdsongs folder. Please check the log at first start if anything is different than you expected
- WARNING : your files will move to the new Birdsongs folder in case of change
- WARNING : your db will be modified in case of Birdsongs folder change to still allow access to files. A backup will always be created
- Fix ingress issues
## 20250103 (2025-01-11)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 25-4 (2024-12-29)
- Fixed https://github.com/alexbelgium/hassio-addons/issues/1687
## 25-3 (2024-12-28)
- avx2 support added by @tphakala
## 25-2 (2024-12-21)
- Minor bugs fixed
## 25 (2024-12-21)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 0.5.5-11 (2024-10-22)
- Minor bugs fixed
## 0.5.5-10 (2024-09-30)
- Minor bugs fixed
## 0.5.5-9 (2024-07-06)
- Correct indentation issue
## 0.5.5-8 (2024-07-03)
- New option : set the audio clip directory from addon options
## 0.5.5-2 (2024-06-25)
- Minor bugs fixed
## 0.5.5 (2024-06-22)
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
## 0.5.5 (2024-06-20)
- Minor bugs fixed
## 0.5.3-3 (2024-06-07)
- Minor bugs fixed
## 0.5.3-2 (2024-06-07)
- Minor bugs fixed
## 0.5.3 (2024-05-26)
- Minor bugs fixed
## 0.5.2 (2024-05-04)
- Minor bugs fixed
## 0.5.1-4 (2024-04-23)
- Feat : provide mariadb information in the startup log to allow its usage
## 0.5.1-3 (2024-04-23)
- Feat : Allow mounting of SMB and local drives to store the audio clips on an external drive
## 0.5.1 (2024-04-22)
- Initial build

365
birdnet-go-dev/Dockerfile Normal file
View File

@@ -0,0 +1,365 @@
# syntax=docker/dockerfile:1
#============================#
# ALEXBELGIUM'S DOCKERFILE #
# BirdNET-Go (OpenVINO/AMD64)#
#============================#
# _.------.
# _.-` ('>.-`"""-.
# '.--'` _'` _ .--.)
# -' '-.-';` `
# ' - _.' ``'--.
# '---` .-'""``
# /`
#=== Home Assistant Addon ===#
#
# AMD64-only, OpenVINO-enabled build of the BirdNET-Go add-on.
# The binary is compiled from the alexbelgium/birdnet-go fork with the
# OpenVINO inference backend enabled (openvino build tag). The OpenVINO
# 2026.2 runtime libraries are bundled in the image, enabling:
# - Faster CPU inference on modern Intel CPUs (AVX-512/AMX)
# - Intel iGPU inference when /dev/dri is passed from the host
#
# Before compiling, "merge-prs.sh" clones the fork, fast-forwards its main
# onto the tphakala/birdnet-go upstream and merges every OPEN, NON-DRAFT
# pull request on the fly. Home Assistant integration layers (2-6) are then
# applied on top and are byte-for-byte identical to the upstream variant.
#
# Build stages 0-1 mirror tphakala/birdnet-go's Docker/Dockerfile; if
# upstream changes its build (Go/Node/onnxruntime versions, task names)
# sync them here.
##########################################################
# 0 Prepare merged BirdNET-Go source tree (fork + PRs) #
##########################################################
FROM alpine/git:latest AS birdnet-source
ARG BIRDNET_FORK=alexbelgium/birdnet-go
ARG BIRDNET_UPSTREAM=tphakala/birdnet-go
# BUILD_DATE changes on every CI run, busting this layer's cache so the fork is
# re-cloned and the open PRs are re-merged "on the fly" on every build.
ARG BUILD_DATE=unknown
RUN apk add --no-cache bash curl jq
COPY merge-prs.sh /usr/local/bin/merge-prs.sh
RUN chmod +x /usr/local/bin/merge-prs.sh && \
echo "Build date marker: ${BUILD_DATE}" && \
BIRDNET_FORK="${BIRDNET_FORK}" BIRDNET_UPSTREAM="${BIRDNET_UPSTREAM}" \
/usr/local/bin/merge-prs.sh /birdnet-src
##########################################################
# 0b Download OpenVINO runtime libraries for amd64 #
##########################################################
FROM alpine:3 AS openvino-libs
ARG OPENVINO_BUILD=2026.2.0.21903.52ddc073857
ARG OPENVINO_RELEASE=2026.2
# Download the x86_64 OpenVINO runtime package and extract runtime/lib/intel64.
# The same build id is used for the C API headers downloaded during compilation
# (see Taskfile.yml OPENVINO_BUILD), ensuring header/runtime version parity.
# We use the arm64 package for headers (arch-independent C declarations) but the
# x86_64 package here for the actual runtime .so files.
#
# OpenVINO links against its own bundled oneTBB (libtbb.so.12, libtbbmalloc*),
# which does NOT live in runtime/lib/intel64 but in runtime/3rdparty/tbb/lib.
# It must be bundled too, otherwise dlopen("libopenvino_c.so") fails at runtime
# with "libtbb.so.12: cannot open shared object file: No such file or directory"
# and the OpenVINO backend silently falls back / GPU planning treats the device
# as unavailable.
RUN apk add --no-cache curl && \
mkdir -p /openvino && \
curl -fsSL \
"https://storage.openvinotoolkit.org/repositories/openvino/packages/${OPENVINO_RELEASE}/linux/openvino_toolkit_ubuntu22_${OPENVINO_BUILD}_x86_64.tgz" \
-o /tmp/openvino.tgz && \
tar -xzf /tmp/openvino.tgz -C /tmp/ --strip-components=1 && \
cd /tmp/runtime/lib/intel64 && \
tar -cf - . | tar -xf - -C /openvino/ && \
# Bundle OpenVINO's own oneTBB runtime (preserving symlinks with cp -a).
# Search the whole 3rdparty tree so the copy survives minor layout changes
# between OpenVINO releases. Fail the build loudly if libtbb is missing,
# since the runtime is unusable without it.
find /tmp/runtime/3rdparty -name 'libtbb*.so*' -exec cp -a {} /openvino/ \; && \
if ! ls /openvino/libtbb.so.12* >/dev/null 2>&1; then \
echo "ERROR: libtbb.so.12 not found in OpenVINO package 3rdparty/tbb" >&2; \
exit 1; \
fi && \
rm -rf /tmp/openvino.tgz /tmp/runtime /tmp/tools /tmp/python \
/tmp/samples /tmp/install_dependencies 2>/dev/null || true
#########################################################################
# 1a Build environment (mirrors tphakala/birdnet-go Docker/Dockerfile) #
#########################################################################
FROM --platform=$BUILDPLATFORM golang:1.26-trixie AS buildenv
ARG BUILD_VERSION
ENV BUILD_VERSION=${BUILD_VERSION:-unknown}
# Install Task and other dependencies
RUN apt-get update -q && apt-get install -q -y \
curl \
git \
sudo \
zip \
gcc-aarch64-linux-gnu && \
rm -rf /var/lib/apt/lists/*
# Install Node.js v24 from NodeSource
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
# Install Task
RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
# Create dev-user for building
RUN groupadd --gid 10001 dev-user && \
useradd --uid 10001 --gid dev-user --shell /bin/bash --create-home dev-user && \
usermod -aG sudo dev-user && \
usermod -aG audio dev-user && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
mkdir -p /home/dev-user/src && \
mkdir -p /home/dev-user/lib && \
mkdir -p /home/dev-user/.cache && \
mkdir -p /home/dev-user/.npm && \
chown -R dev-user:dev-user /home/dev-user
USER dev-user
WORKDIR /home/dev-user/src/BirdNET-Go
# Bring in the merged source tree (fork main + upstream sync + open PRs)
COPY --from=birdnet-source --chown=dev-user /birdnet-src ./
# Pre-build frontend once in the shared buildenv stage
ENV PUPPETEER_SKIP_DOWNLOAD=true
RUN task frontend-build
##################
# 1b Build stage #
##################
FROM --platform=$BUILDPLATFORM buildenv AS build
ARG BUILD_VERSION
ENV BUILD_VERSION=${BUILD_VERSION:-unknown}
ARG ONNXRUNTIME_VERSION=1.25.1
# Download ONNX Runtime for amd64 (x64)
RUN echo "Downloading ONNX Runtime ${ONNXRUNTIME_VERSION} for x64" && \
curl -fsSL "https://github.com/microsoft/onnxruntime/releases/download/v${ONNXRUNTIME_VERSION}/onnxruntime-linux-x64-${ONNXRUNTIME_VERSION}.tgz" \
-o /tmp/onnxruntime.tgz && \
mkdir -p /tmp/onnxruntime && \
tar -xzf /tmp/onnxruntime.tgz -C /tmp/onnxruntime --strip-components=1 && \
cp /tmp/onnxruntime/lib/libonnxruntime*.so* /home/dev-user/lib/ && \
rm -rf /tmp/onnxruntime /tmp/onnxruntime.tgz
# Build BirdNET-Go for amd64 with OpenVINO enabled.
# OPENVINO=true compiles in the openvino build tag, enabling the OpenVINO
# inference backend (dlopen'd at runtime; falls back to ONNX Runtime when
# libopenvino_c.so is absent). The noembed_linux_amd64 task defaults to
# OPENVINO=true anyway; we make it explicit here for clarity.
RUN --mount=type=cache,target=/go/pkg/mod,uid=10001,gid=10001 \
--mount=type=cache,target=/home/dev-user/.cache/go-build,uid=10001,gid=10001 \
task check-tensorflow && \
echo "Building amd64 with OpenVINO enabled, BUILD_VERSION=${BUILD_VERSION}" && \
BUILD_VERSION="${BUILD_VERSION}" DOCKER_LIB_DIR=/home/dev-user/lib task noembed_linux_amd64 OPENVINO=true
#####################################################
# 1c BirdNET-Go runtime base (mirrors upstream) #
#####################################################
FROM debian:trixie-slim AS birdnet-runtime
# Copy TFLite model files (amd64 uses TFLite; OpenVINO reads them via its
# TFLite frontend plugin bundled in the OpenVINO runtime below)
RUN mkdir -p /models
COPY --from=build /home/dev-user/src/BirdNET-Go/internal/classifier/data/*.tflite /models/
RUN chmod -R a+r /models/*.tflite 2>/dev/null || true && \
chmod a+x /models
# Install ALSA library, SOX for audio processing, and system utilities.
# ocl-icd-libopencl1 provides the OpenCL ICD loader; combined with
# /dev/dri passthrough from the host it enables Intel iGPU inference.
RUN apt-get update -q && apt-get install -q -y --no-install-recommends \
adduser \
ca-certificates \
libasound2 \
ffmpeg \
sox \
libsox-fmt-mp3 \
procps \
iproute2 \
net-tools \
curl \
wget \
nano \
vim \
less \
tzdata \
tzdata-legacy \
jq \
strace \
lsof \
bash-completion \
gosu \
ocl-icd-libopencl1 \
&& rm -rf /var/lib/apt/lists/*
# Copy TensorFlow Lite library from build stage
COPY --from=build /home/dev-user/lib/libtensorflowlite_c.so* /usr/lib/
# Copy ONNX Runtime libraries from build stage
COPY --from=build /home/dev-user/lib/libonnxruntime*.so* /usr/lib/
# Copy OpenVINO runtime libraries (libopenvino_c.so, CPU/GPU plugins,
# TFLite frontend, plugins.xml, bundled oneTBB libtbb.so.12, etc.) into a
# dedicated directory and register it with ldconfig so both
# dlopen("libopenvino_c.so") and its libtbb.so.12 dependency resolve.
# plugins.xml is co-located with libopenvino.so so device plugin
# discovery works without additional environment variables.
COPY --from=openvino-libs /openvino/ /usr/lib/openvino/
RUN echo "/usr/lib/openvino" > /etc/ld.so.conf.d/openvino.conf && ldconfig
# Include reset_auth tool from build stage
COPY --from=build /home/dev-user/src/BirdNET-Go/reset_auth.sh /usr/bin/
RUN chmod +x /usr/bin/reset_auth.sh
# Add entrypoint and startup wrapper scripts from build stage
COPY --from=build /home/dev-user/src/BirdNET-Go/Docker/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
COPY --from=build /home/dev-user/src/BirdNET-Go/Docker/startup-wrapper.sh /usr/bin/
RUN chmod +x /usr/bin/startup-wrapper.sh
# Create config and data directories with proper permissions for rootless compatibility
RUN mkdir -p /config /data/clips /data/models && \
chmod 777 /config /data /data/clips /data/models
# Install the freshly built BirdNET-Go binary
COPY --from=build /home/dev-user/src/BirdNET-Go/bin /usr/bin/
# Enable openvino
ARG NEO_VERSION=26.22.38646.4
ARG IGC_VERSION=2.36.3+21719
RUN groupadd -g 109 render || true && \
usermod -aG render root && \
\
apt-get update -q && apt-get install -q -y --no-install-recommends wget ocl-icd-libopencl1 libdrm2 libdrm-intel1 && \
mkdir -p /tmp/neo && cd /tmp/neo && \
wget -q https://github.com/intel/intel-graphics-compiler/releases/download/v2.36.3/intel-igc-core-2_${IGC_VERSION}_amd64.deb \
https://github.com/intel/intel-graphics-compiler/releases/download/v2.36.3/intel-igc-opencl-2_${IGC_VERSION}_amd64.deb \
https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl-icd_${NEO_VERSION}-0_amd64.deb \
https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/libigdgmm12_22.10.0_amd64.deb \
https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/libze-intel-gpu1_${NEO_VERSION}-0_amd64.deb && \
dpkg -i *.deb && \
rm -rf /tmp/neo && \
rm -rf /var/lib/apt/lists/*
#============================================================================#
# From here on, the Home Assistant integration layers are identical to the #
# standard alexbelgium/birdnet-go add-on (which builds FROM the prebuilt #
# upstream image). The only difference above is that the base is compiled #
# from the fork + open PRs with OpenVINO enabled. #
#============================================================================#
##################
# 2 Modify Image #
##################
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Set S6 wait time
ENV S6_CMD_WAIT_FOR_SERVICES=1 \
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
S6_SERVICES_GRACETIME=0
##################
# 3 Install apps #
##################
# Add rootfs
COPY rootfs/ /
RUN find . -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \;
# Silence "chmod: Read-only file system" noise from upstream entrypoint:
# Home Assistant mounts /dev/snd read-only, so the chmod is a no-op and the
# stderr output is just noise. The "|| true" already absorbs the exit code.
# Warn (without failing the build) if the upstream pattern drifts so we notice
# in CI logs and can update the patch.
RUN if [ -f /usr/bin/entrypoint.sh ]; then \
if grep -q 'chmod -R a+rw /dev/snd || true' /usr/bin/entrypoint.sh; then \
sed -i 's#chmod -R a+rw /dev/snd || true#chmod -R a+rw /dev/snd 2>/dev/null || true#' /usr/bin/entrypoint.sh; \
else \
echo "WARN: upstream entrypoint.sh /dev/snd chmod pattern not found; nightly drift?" >&2; \
fi; \
fi
# Uses /bin for compatibility purposes
# hadolint ignore=DL4005
RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \
if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi
# Modules
ARG MODULES="00-local_mounts.sh 00-smb_mounts.sh"
# Automatic modules download
COPY ha_automodules.sh /ha_automodules.sh
RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_automodules.sh
# Manual apps
ENV PACKAGES="alsa-utils libasound2-plugins mariadb-client nginx yq"
# Automatic apps & bashio
COPY ha_autoapps.sh /ha_autoapps.sh
RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh
################
# 4 Entrypoint #
################
# Add entrypoint
ENV S6_STAGE2_HOOK=/ha_entrypoint.sh
COPY ha_entrypoint.sh /ha_entrypoint.sh
RUN chmod 777 /ha_entrypoint.sh
# Install bashio
COPY bashio-standalone.sh /usr/local/lib/bashio-standalone.sh
RUN chmod 0755 /usr/local/lib/bashio-standalone.sh
# Avoid config.yaml interference
RUN sed -i "s|config.yaml|config_env.yaml|g" /etc/cont-init.d/01-config_yaml.sh
ENTRYPOINT [ "/usr/bin/env" ]
CMD [ "/ha_entrypoint.sh" ]
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
############
# 5 Labels #
############
ARG BUILD_ARCH
ARG BUILD_DATE
ARG BUILD_DESCRIPTION
ARG BUILD_NAME
ARG BUILD_REF
ARG BUILD_REPOSITORY
ARG BUILD_VERSION
ENV BUILD_VERSION="${BUILD_VERSION}"
LABEL \
io.hass.name="${BUILD_NAME}" \
io.hass.description="${BUILD_DESCRIPTION}" \
io.hass.arch="${BUILD_ARCH}" \
io.hass.type="addon" \
io.hass.version=${BUILD_VERSION} \
maintainer="alexbelgium (https://github.com/alexbelgium)" \
org.opencontainers.image.title="${BUILD_NAME}" \
org.opencontainers.image.description="${BUILD_DESCRIPTION}" \
org.opencontainers.image.vendor="Home Assistant Add-ons" \
org.opencontainers.image.authors="alexbelgium (https://github.com/alexbelgium)" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.url="https://github.com/alexbelgium" \
org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \
org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \
org.opencontainers.image.created=${BUILD_DATE} \
org.opencontainers.image.revision=${BUILD_REF} \
org.opencontainers.image.version=${BUILD_VERSION}
#################
# 6 Healthcheck #
#################

View File

@@ -0,0 +1,258 @@
# BirdNET-Go Addon: Home Assistant Integration
BirdNET-Go can be integrated with Home Assistant using a MQTT Broker.
> **💡 Easiest path — automatic discovery.** If you run the Home Assistant
> Mosquitto (MQTT) addon, just set `mqtt_auto_config: true` in this add-on's
> options. The add-on then wires in the broker credentials **and** enables
> BirdNET-Go's native Home Assistant MQTT auto-discovery, so the detection
> sensors appear in Home Assistant automatically with **no manual YAML at
> all**. The manual sensor/template/card configuration below is only needed if
> you want to build your own custom entities instead of (or in addition to) the
> auto-discovered ones.
## MQTT Configuration
Your Home Assistant must be setup with MQTT and BirdNET-Go MQTT integration must be enabled. Modify the BirdNET-Go config.yaml file to enable MQTT. If you are using the Mosquitto Broker addon, you will see a log message during the BirdNET-Go startup showing the internal MQTT server details needed for configuration similar to below.
```text
BirdNET-Go log snipped showing MQTT details:
/etc/cont-init.d/33-mqtt.sh: executing
---
MQTT addon is active on your system! Add the MQTT details below to the Birdnet-go config.yaml :
MQTT user : addons
MQTT password : Ri5ahV1aipeiw0aelerooteixai5ohtoeNg6oo3mo0thi5te0phiezuge4Phoore
MQTT broker : tcp://core-mosquitto:1883
---
Edit this section of config.yaml found in addon_configs/db21ed7f_birdnet-go/:
mqtt:
enabled: true # true to enable MQTT
broker: tcp://core-mosquitto:1883 # MQTT (tcp://host:port)
topic: birdnet # MQTT topic
username: addons # MQTT username
password: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # MQTT password
```
## BirdNET-Go MQTT Sensors
Add the [MQTT sensor](https://www.home-assistant.io/integrations/sensor.mqtt/) yaml configuration below to your Home Assistant configuration.yaml file. Reload the configuration and once BirdNET-Go publishes a new finding to MQTT the new BirdNET-Go sensors should show that latest finding data.
```yaml
mqtt:
sensor:
- name: "Birdnet-Go"
state_topic: "birdnet"
value_template: "{{ today_at(value_json.Time) }}"
json_attributes_topic: "birdnet"
json_attributes_template: "{{ value_json | tojson }}"
- name: "Birdnet-Go Bird Image Url"
state_topic: "birdnet"
value_template: "{{ value_json.BirdImage.URL }}"
- name: "Birdnet-Go Clip Name"
state_topic: "birdnet"
value_template: "{{ value_json.ClipName }}"
- name: "Birdnet-Go Common Name"
state_topic: "birdnet"
value_template: "{{ value_json.CommonName }}"
- name: "Birdnet-Go Confidence"
state_topic: "birdnet"
value_template: "{{ (value_json.Confidence | float * 100) | round(2) }}"
unit_of_measurement: "%"
- name: "Birdnet-Go Date"
state_topic: "birdnet"
value_template: "{{ value_json.Date }}"
- name: "Birdnet-Go ProcessingTime"
state_topic: "birdnet"
value_template: "{{ (value_json.ProcessingTime | float / 1000000000 ) | round(4) }}"
unit_of_measurement: "s"
- name: "Birdnet-Go Scientific Name"
state_topic: "birdnet"
value_template: "{{ value_json.ScientificName }}"
- name: "Birdnet-Go Sensitivity"
state_topic: "birdnet"
value_template: "{{ value_json.Sensitivity }}"
- name: "Birdnet-Go Source"
state_topic: "birdnet"
value_template: "{{ value_json.Source }}"
- name: "Birdnet-Go Species Code"
state_topic: "birdnet"
value_template: "{{ value_json.SpeciesCode }}"
- name: "Birdnet-Go Threshold"
state_topic: "birdnet"
value_template: "{{ value_json.Threshold }}"
- name: "Birdnet-Go Time"
state_topic: "birdnet"
value_template: "{{ today_at(value_json.Time) }}"
```
![BirdNET-go MQTT](./images/ha_birdnet_mqtt_sensor.png)
## BirdNET-Go Events Sensor[^1]
Then create a new template sensor using the configuration below.
```yaml
- trigger:
- platform: mqtt
topic: "birdnet"
id: birdnet
- platform: time
at: "00:00:00"
id: reset
sensor:
- unique_id: c893533c-3c06-4ebe-a5bb-da833da0a947
name: BirdNET-Go Events
state: >
{% if trigger.id == 'reset' %}
{{ now() }}
{% elif trigger.id == 'birdnet' %}
{{ today_at(trigger.payload_json.Time) }}
{% endif %}
attributes:
bird_events: >
{% if trigger.id == 'reset' %}
{{ [] }}
{% elif trigger.id == 'birdnet' %}
{% set time = trigger.payload_json.Time %}
{% set name = trigger.payload_json.CommonName %}
{% set confidence = trigger.payload_json.Confidence|round(2) * 100 ~ '%' %}
{% set current = this.attributes.get('bird_events', []) %}
{% set new = dict(time=time, name=name, confidence=confidence) %}
{{ current + [new] }}
{% endif %}
```
### BirdNET-Go Dashboard Cards
There are two versions listed below. The first example will link the Bird Name to Wikipedia. The other example will link to All About Birds. You will need to modify the Confidence link to match your Home Assistant setup.
![BirdNET-go Markdown Card Wikipedia](./images/ha_birdnet_markdown_card_wikipedia.png)
```yaml
type: markdown
title: BirdNET (Wikipedia)
content: >-
Time|&nbsp;&nbsp;Bird Name|Number Today| &nbsp;&nbsp;&nbsp;Max
[Confidence](http://ip_address_of_HA:8080/)
:---|:---|:---:|:---:
{%- set t = now() %}
{%- set bird_list = state_attr('sensor.birdnet_go_events','bird_events') |
sort(attribute='time', reverse=true) | map(attribute='name') | unique | list
%}
{%- set bird_objects = state_attr('sensor.birdnet_go_events','bird_events') |
sort(attribute='time', reverse=true) %}
{%- for thisbird in bird_list or [] %}
{%- set ubird = ((bird_objects | selectattr("name", "equalto", thisbird)) |
list)[0] %}
{%- set ubird_count = ((bird_objects | selectattr("name", "equalto",
thisbird)) | list) | length %}
{%- set ubird_max_confidence = ((bird_objects | selectattr("name", "equalto",
thisbird)) | map(attribute='confidence') | map('replace', '%', '') |
map('float') | max | round(0)) %}
{%- if ubird_max_confidence > 70 %}
{{ubird.time}}
|&nbsp;&nbsp;[{{ubird.name}}](https://en.wikipedia.org/wiki/{{ubird.name |
replace(' ', '_')}}) | {{ubird_count}} | {{ ubird_max_confidence }} %
{%- endif %}
{%- endfor %}
card_mod:
style:
$: |
.card-header {
display: flex !important;
align-items: center;
}
.card-header:before {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='m23 11.5l-3.05-1.13c-.26-1.15-.91-1.81-.91-1.81a4.19 4.19 0 0 0-5.93 0l-1.48 1.48L5 3c-1 4 0 8 2.45 11.22L2 19.5s8.89 2 14.07-2.05c2.76-2.16 3.38-3.42 3.77-4.75zm-5.29.22c-.39.39-1.03.39-1.42 0a.996.996 0 0 1 0-1.41c.39-.39 1.03-.39 1.42 0s.39 1.02 0 1.41'/%3E%3C/svg%3E");
height: 42px;
width: 42px;
margin-top: 0px;
padding-left: 0px;
padding-right: 14px;
}
@media (prefers-color-scheme: dark) {
.card-header:before {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23E1E1E1' d='m23 11.5l-3.05-1.13c-.26-1.15-.91-1.81-.91-1.81a4.19 4.19 0 0 0-5.93 0l-1.48 1.48L5 3c-1 4 0 8 2.45 11.22L2 19.5s8.89 2 14.07-2.05c2.76-2.16 3.38-3.42 3.77-4.75zm-5.29.22c-.39.39-1.03.39-1.42 0a.996.996 0 0 1 0-1.41c.39-.39 1.03-.39 1.42 0s.39 1.02 0 1.41'/%3E%3C/svg%3E");
height: 42px;
width: 42px;
margin-top: 0px;
padding-left: 0px;
padding-right: 14px;
}
}
```
![BirdNET-go Markdown Card All About Birds](./images/ha_birdnet_markdown_card_all_about_birds.png)
```yaml
type: markdown
title: BirdNET (All About Birds)
content: >-
Time|&nbsp;&nbsp;Bird Name|Number Today| &nbsp;&nbsp;&nbsp;Max
[Confidence](http://ip_address_of_HA:8080/)
:---|:---|:---:|:---:
{%- set t = now() %}
{%- set bird_list = state_attr('sensor.birdnet_go_events','bird_events') |
sort(attribute='time', reverse=true) | map(attribute='name') | unique | list
%}
{%- set bird_objects = state_attr('sensor.birdnet_go_events','bird_events') |
sort(attribute='time', reverse=true) %}
{%- for thisbird in bird_list or [] %}
{%- set ubird = ((bird_objects | selectattr("name", "equalto", thisbird)) |
list)[0] %}
{%- set ubird_count = ((bird_objects | selectattr("name", "equalto",
thisbird)) | list) | length %}
{%- set ubird_max_confidence = ((bird_objects | selectattr("name", "equalto",
thisbird)) | map(attribute='confidence') | map('replace', '%', '') |
map('float') | max | round(0)) %}
{%- if ubird_max_confidence > 70 %}
{{ubird.time}}
|&nbsp;&nbsp;[{{ubird.name}}](https://www.allaboutbirds.org/guide/{{ubird.name
| replace(' ', '_')}}) | {{ubird_count}} | {{ ubird_max_confidence }} %
{%- endif %}
{%- endfor %}
card_mod:
style:
$: |
.card-header {
display: flex !important;
align-items: center;
}
.card-header:before {
content: url("data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDYuODcgMTE2LjY2Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2Y0ZTUwNTt9LmNscy0ye2ZpbGw6I2UzMWUyNjt9LmNscy0ze2ZpbGw6I2ZmZjt9PC9zdHlsZT48L2RlZnM+PHBhdGggZD0iTTIwNi4zNywxNi42OHMtMTYuNDQtNC4zNC0yMi43Ni00LjljMCwwLTI1LDEzLjUtMzIsMThhMTkuMTYsMTkuMTYsMCwwLDAtOC42NywxMy44OWwzNS43MS0yNi4zMmgyOEMyMDcuMzEsMTcuMzksMjA2LjM3LDE2LjY4LDIwNi4zNywxNi42OFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMC42MykiLz48cGF0aCBkPSJNMTQ4LjU1LDI3LjMzYzcuMzItNC45LDMyLjYyLTE4LjczLDMyLjYyLTE4LjczbDAsMEEzMC42OSwzMC42OSwwLDAsMCwxNTktLjYzYTQ0LjIzLDQ0LjIzLDAsMCwwLTIwLjcxLDVIMGMwLDMuNzEsNS42LDYuNTYsMTIuMTQsNi41Nkg1Mi4zNkw4Ni42MiwzNS4xMlY3MS4zN2MwLDE1LjczLDguMjYsMjkuNDQsMjEuNzgsMzcuMzVTMTI4LjY4LDExNiwxMzguNjMsMTE2VjQ2Ljg3QzEzOC42Myw0MC43OCwxNDAuNDcsMzIuNzMsMTQ4LjU1LDI3LjMzWk0xNjcuODcsOGEyLjUxLDIuNTEsMCwxLDEtMi41MSwyLjUxQTIuNTEsMi41MSwwLDAsMSwxNjcuODcsOFptLTI5LjEzLDEzLDE1LjY5LTguNjgsNi44OS41N0wxMzguNzQsMjUuMzZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDAuNjMpIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTIuMzYsMTAuOTFIMTEwYy0xMi44OSwwLTIzLjQsMTAuMzUtMjMuNCwyNC4yMVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMC42MykiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xNzgsMTAuMzNBMzEuNzEsMzEuNzEsMCwwLDAsMTU3Ljc4LDIuOVYtLjYxbDEuMjUsMEEzMC42MywzMC42MywwLDAsMSwxODEuMTcsOC42WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAwLjYzKSIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTE3OC42MywxNy4zOWwtMjUsMTguNDNzLS4yOS0yLjcsMy40Ny01Ljc0LDI2LjUtMTguMywyNi41LTE4LjNaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDAuNjMpIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTI4LjE0LDY0LjQ3VjUyLjE1YzAtNS4xOC0yLjExLTguNzctNi45My0xMi4xOEwxMDAuNzksMjUuNTRhMTQuMzIsMTQuMzIsMCwwLDAsMiwyMVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMC42MykiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xMjguMTQsNjQuNDdWNTIuMTVjMC01LjE4LTIuMTEtOC43Ny02LjkzLTEyLjE4TDEwMC43OSwyNS41NGExNC4zMiwxNC4zMiwwLDAsMCwyLDIxWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAwLjYzKSIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTE1MS41OSwyOS44MmM3LTQuNTQsMzItMTgsMzItMThhMTYuMjQsMTYuMjQsMCwwLDAtMi40MS0zLjE1bDAsMHMtMjUuMywxMy44My0zMi42MiwxOC43My05LjU3LDEyLjE3LTkuODcsMThsLS4wNSwxLjUxLDQuMjktMy4xNkExOS4xNiwxOS4xNiwwLDAsMSwxNTEuNTksMjkuODJaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDAuNjMpIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTY3Ljg3LDhhMi41MSwyLjUxLDAsMSwxLTIuNTEsMi41MUEyLjUxLDIuNTEsMCwwLDEsMTY3Ljg3LDhaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDAuNjMpIi8+PHBvbHlnb24gY2xhc3M9ImNscy0zIiBwb2ludHM9IjEzOC43NCAyMS41NyAxNTQuNDMgMTIuODkgMTYxLjMyIDEzLjQ1IDEzOC43NCAyNS45OCAxMzguNzQgMjEuNTciLz48L3N2Zz4=");
height: 20px;
width: 60px;
margin-top: -10px;
padding-left: 8px;
padding-right: 18px;
}
```
## Footnotes
[^1]: [Displaying Birdnet-go detections](https://community.home-assistant.io/t/displaying-birdnet-go-detections/713611/22)

177
birdnet-go-dev/README.md Normal file
View File

@@ -0,0 +1,177 @@
# Home assistant add-on: Birdnet-Go (from source)
> **⚠️ Test build.** This is a special variant of the [standard birdnet-go add-on](https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-go). Instead of pulling the prebuilt `ghcr.io/tphakala/birdnet-go` image, it **compiles BirdNET-Go from the [`alexbelgium/birdnet-go`](https://github.com/alexbelgium/birdnet-go) fork**. At build time it syncs the fork's `main` with the `tphakala/birdnet-go` upstream and **merges every open non-draft ("in review") pull request on the fly** (see [`merge-prs.sh`](./merge-prs.sh)), so the binary reflects upstream main plus all work currently under review. Everything below is identical to the standard add-on.
I maintain this and other Home Assistant add-ons in my free time: keeping up with upstream changes, HA changes, and testing on real hardware takes a lot of time (and some money). I use around 5-10 of my >110 addons so regularly I install test machines (and purchase some test services such as vpn) that I don't use myself to troubleshoot and improve the addons
If this add-on saves you time or makes your setup easier, I would be very grateful for your support!
[![Buy me a coffee][donation-badge]](https://www.buymeacoffee.com/alexbelgium)
[![Donate via PayPal][paypal-badge]](https://www.paypal.com/donate/?hosted_button_id=DZFULJZTP3UQA)
## Addon informations
![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbirdnet-go%2Fconfig.yaml)
![Ingress](https://img.shields.io/badge/dynamic/yaml?label=Ingress&query=%24.ingress&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbirdnet-go%2Fconfig.yaml)
![Arch](https://img.shields.io/badge/dynamic/yaml?color=success&label=Arch&query=%24.arch&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbirdnet-go%2Fconfig.yaml)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9c6cf10bdbba45ecb202d7f579b5be0e)](https://www.codacy.com/gh/alexbelgium/hassio-addons/dashboard?utm_source=github.com&utm_medium=referral&utm_content=alexbelgium/hassio-addons&utm_campaign=Badge_Grade)
[![GitHub Super-Linter](https://img.shields.io/github/actions/workflow/status/alexbelgium/hassio-addons/weekly-supelinter.yaml?label=Lint%20code%20base)](https://github.com/alexbelgium/hassio-addons/actions/workflows/weekly-supelinter.yaml)
[![Builder](https://img.shields.io/github/actions/workflow/status/alexbelgium/hassio-addons/onpush_builder.yaml?label=Builder)](https://github.com/alexbelgium/hassio-addons/actions/workflows/onpush_builder.yaml)
[donation-badge]: https://img.shields.io/badge/Buy%20me%20a%20coffee-%23d32f2f?logo=buy-me-a-coffee&style=flat&logoColor=white
[paypal-badge]: https://img.shields.io/badge/Donate%20via%20PayPal-0070BA?logo=paypal&style=flat&logoColor=white
_Thanks to everyone having starred my repo! To star it click on the image below, then it will be on top right. Thanks!_
[![Stargazers repo roster for @alexbelgium/hassio-addons](https://reporoster.com/stars/alexbelgium/hassio-addons)](https://github.com/alexbelgium/hassio-addons/stargazers)
![downloads evolution](https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/birdnet-go/stats.png)
## About
[BirdNET-Go](https://github.com/tphakala/birdnet-go/tree/main) is an AI solution for continuous avian monitoring and identification developed by @tphakala
This addon is based on their docker image.
## Configuration
Install, then start the addon a first time. Webui can be found at <http://homeassistant:8080>.
You'll need a microphone : either use one connected to HA or the audio stream of a rstp camera.
The audio clips folder can be stored on an external or SMB drive by mounting it from the addon options, then specifying the path instead of "clips/". For example, "/mnt/NAS/Birdnet/"
Options can be configured through three ways :
- Addon options
```yaml
BIRDSONGS_FOLDER: /config/clips # where audio clips are stored (can be on a mounted drive)
LOG_MAX_SIZE_MB: 50 # max log file size before rotation
LOG_MAX_AGE_DAYS: 7 # max log retention in days
homeassistant_microphone: false # when true, force audio source to "default" (HA microphone)
env_vars: [] # extra environment variables to pass to the container
TZ: Etc/UTC # timezone, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
mqtt_auto_config: false # set true to auto-wire the Home Assistant MQTT addon into config.yaml
mariadb_auto_config: false # set true to auto-wire the Home Assistant MariaDB addon into config.yaml (also disables SQLite)
```
- Config.yaml
Additional variables can be configured using the config.yaml file found in /config/db21ed7f_birdnet-go/config.yaml using the Filebrowser addon
- Config_env.yaml
Additional environment variables can be configured there
### MQTT and MariaDB auto-configuration (opt-in)
If the Home Assistant **MQTT** addon is installed and running and you set `mqtt_auto_config: true` in the addon options, the addon writes the HA Mosquitto credentials directly into BirdNET-Go's `config.yaml` on every startup: `realtime.mqtt.enabled`, `broker`, `username`, and `password` are populated, and the topic defaults to `birdnet`. In addition, it enables BirdNET-Go's **native Home Assistant MQTT auto-discovery** (`realtime.mqtt.homeassistant.enabled`), so the detection sensors show up in Home Assistant automatically — **no manual MQTT sensor YAML required** (the hand-written sensors in [HAINTEGRATION.md](./HAINTEGRATION.md) remain available if you prefer to build your own). Messages are also retained (`realtime.mqtt.retain: true`) so sensor states survive Home Assistant restarts. When the option is `false` (the default), the addon still logs the broker details and reminds you about the option whenever Mosquitto is detected — nothing is written.
If the Home Assistant **MariaDB** addon is installed and running and you set `mariadb_auto_config: true`, the addon writes the HA credentials into `output.mysql.*` and sets `output.sqlite.enabled` to `false` (database name `birdnet`, created on first connect). When the option is `false` (the default), the addon only logs the credentials so you can configure them manually.
The addon also seeds `output.sqlite.path` and `logging.file_output.*` defaults only when those keys are missing from `config.yaml`, so values you change through the BirdNET-Go UI now survive container restarts.
### Mounting Drives
This addon supports mounting both local drives and remote SMB shares:
- **Local drives**: See [Mounting Local Drives in Addons](https://github.com/alexbelgium/hassio-addons/wiki/Mounting-Local-Drives-in-Addons)
- **Remote shares**: See [Mounting Remote Shares in Addons](https://github.com/alexbelgium/hassio-addons/wiki/Mounting-remote-shares-in-Addons)
### Custom Scripts and Environment Variables
This addon supports custom scripts and environment variables through the `addon_config` mapping:
- **Custom scripts**: See [Running Custom Scripts in Addons](https://github.com/alexbelgium/hassio-addons/wiki/Running-custom-scripts-in-Addons)
- **env_vars option**: Use the add-on `env_vars` option to pass extra environment variables (uppercase or lowercase names). See https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2 for details.
## Installation
The installation of this add-on is pretty straightforward and not different in comparison to installing any other add-on.
1. Add my add-ons repository to your home assistant instance (in supervisor addons store at top right, or click button below if you have configured my HA)
[![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons)
1. Install this add-on.
1. Click the `Save` button to store your configuration.
1. Set the add-on options to your preferences
1. Start the add-on.
1. Check the logs of the add-on to see if everything went well.
1. Open the webUI and adapt the software options
## Integration with HA
Home Assistant Integration instructions are found here, [Birdnet-Go Addon: Home Assistant Integration](./HAINTEGRATION.md)
## Setting up a RTSP Source using VLC
VLC opens a TCP port but the stream is udp. Because of this will need to configure Birdnet-Go to use udp. Adjust the config.yaml file to udp or use the birdnet-go command line option:
`--rtsptransport udp --rtsp rtsp://192.168.1.21:8080/stream.sdp`
### Linux instructions
Run vlc without an interface using one of these commands:
```bash
# This should work for most devices
/usr/bin/vlc -I dummy -vvv alsa://hw:0,0 --no-sout-all --sout-keep --sout '#transcode{acodec=mpga}:rtp{sdp=rtsp://:8080/stream.sdp}'
# Try this if the first command does not work
/usr/bin/vlc -I dummy -vvv alsa://hw:4,0 --no-sout-all --sout-keep --sout '#rtp{sdp=rtsp://:8080/stream.sdp}'
```
Run `arecord -l` to get microphone hardware info
```text
**** List of CAPTURE Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: ALC3220 Analog [ALC3220 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 2: S7 [SteelSeries Arctis 7], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 3: Nano [Yeti Nano], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 4: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]
Subdevices: 0/1
Subdevice #0: subdevice #0
```
hw:4,0 = **card 4**: Device [USB PnP Sound Device], **device 0**: USB Audio [USB Audio]
Systemd service file example. Adjust the user:group accordingly. If you want to run as root, you will likely need to run vlc-wrapper instead of vlc.
```text
[Unit]
Description=VLC Birdnet RTSP Server
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
StandardOutput=journal
ExecStart=/usr/bin/vlc -I dummy -vvv alsa://hw:0,0 --sout '#transcode{acodec=mpga}:rtp{sdp=rtsp://:8080/stream.sdp}'
User=someone
Group=somegroup
[Install]
WantedBy=multi-user.target
```
## Common issues
Not yet available
## Support
Create an issue on github
---
![illustration](https://raw.githubusercontent.com/tphakala/birdnet-go/main/doc/BirdNET-Go-dashboard.webp)

View File

@@ -0,0 +1,67 @@
#include <tunables/global>
profile db21ed7f_birdnet-go flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
capability chown,
capability dac_override,
capability dac_read_search,
capability fowner,
capability setgid,
capability setuid,
capability sys_admin,
capability sys_resource,
file,
signal,
mount,
umount,
remount,
network udp,
network tcp,
network dgram,
network stream,
network inet,
network inet6,
network netlink raw,
network unix dgram,
# S6-Overlay
/init ix,
/run/{s6,s6-rc*,service}/** ix,
/package/** ix,
/command/** ix,
/run/{,**} rwk,
/dev/tty rw,
/bin/** ix,
/usr/bin/** ix,
/usr/lib/bashio/** ix,
/etc/s6/** rix,
/run/s6/** rix,
/etc/services.d/** rwix,
/etc/cont-init.d/** rwix,
/etc/cont-finish.d/** rwix,
/init rix,
/var/run/** mrwkl,
/var/run/ mrwkl,
/dev/i2c-1 mrwkl,
# Files required
/dev/fuse mrwkl,
/dev/sda1 mrwkl,
/dev/sdb1 mrwkl,
/dev/nvme0 mrwkl,
/dev/nvme1 mrwkl,
/dev/mmcblk0p1 mrwkl,
/dev/* mrwkl,
/tmp/** mrkwl,
# Data access
/data/** rw,
# suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
ptrace (trace,read) peer=docker-default,
# docker daemon confinement requires explict allow rule for signal
signal (receive) set=(kill,term) peer=/usr/bin/docker,
}

131
birdnet-go-dev/config.yaml Normal file
View File

@@ -0,0 +1,131 @@
arch:
- amd64
audio: true
backup_exclude:
- logs/**
description: Realtime BirdNET soundscape analyzer, compiled from the alexbelgium/birdnet-go fork with all open PRs merged, with OpenVINO enabled for Intel CPU/iGPU acceleration (amd64-only test build)
devices:
- /dev/dri
- /dev/snd
- /dev/snd/controlC0
- /dev/snd/controlC1
- /dev/snd/pcmC1D0c
- /dev/snd/pcmC1D0p
- /dev/dri/card0
- /dev/dri/card1
- /dev/dri/renderD128
- /dev/vchiq
- /dev/video10
- /dev/video11
- /dev/video12
- /dev/video13
- /dev/video14
- /dev/video15
- /dev/video16
- /dev/ttyUSB0
- /dev/sda
- /dev/sdb
- /dev/sdc
- /dev/sdd
- /dev/sde
- /dev/sdf
- /dev/sdg
- /dev/nvme
- /dev/nvme0
- /dev/nvme0n1
- /dev/nvme0n1p1
- /dev/nvme0n1p2
- /dev/nvme0n1p3
- /dev/nvme1n1
- /dev/nvme1n1p1
- /dev/nvme1n1p2
- /dev/nvme1n1p3
- /dev/nvme2n1
- /dev/nvme2n1p1
- /dev/nvme2n1p2
- /dev/nvme2n3p3
- /dev/mmcblk
- /dev/fuse
- /dev/sda1
- /dev/sdb1
- /dev/sdc1
- /dev/sdd1
- /dev/sde1
- /dev/sdf1
- /dev/sdg1
- /dev/sda2
- /dev/sdb2
- /dev/sdc2
- /dev/sdd2
- /dev/sde2
- /dev/sdf2
- /dev/sdg2
- /dev/sda3
- /dev/sdb3
- /dev/sda4
- /dev/sdb4
- /dev/sda5
- /dev/sda6
- /dev/sda7
- /dev/sda8
- /dev/nvme0
- /dev/nvme1
- /dev/nvme2
environment:
BIRDNET_GID: "0"
BIRDNET_UID: "0"
image: ghcr.io/alexbelgium/birdnet-go-dev-{arch}
ingress: true
ingress_entry: "ui/dashboard"
ingress_stream: true
init: false
map:
- addon_config:rw
- media:rw
- share:rw
name: Birdnet-go (custom version)
options:
env_vars: []
BIRDSONGS_FOLDER: /config/clips
LOG_MAX_SIZE_MB: 50
LOG_MAX_AGE_DAYS: 7
homeassistant_microphone: false
mqtt_auto_config: false
mariadb_auto_config: false
panel_admin: false
panel_icon: mdi:bird
ports:
8080/tcp: 8080
9090/tcp: 9090
ports_description:
8080/tcp: Web ui
9090/tcp: Telemetry endpoint
privileged:
- SYS_ADMIN
- DAC_READ_SEARCH
- SYS_RESOURCE
schema:
env_vars:
- name: match(^[A-Za-z0-9_]+$)
value: str?
BIRDSONGS_FOLDER: str?
LOG_MAX_SIZE_MB: int(1,1000)?
LOG_MAX_AGE_DAYS: int(1,365)?
TZ: str?
cifsdomain: str?
cifspassword: str?
cifsusername: str?
homeassistant_microphone: bool?
localdisks: str?
mariadb_auto_config: bool?
mqtt_auto_config: bool?
networkdisks: str?
services:
- mysql:want
- mqtt:want
slug: birdnet-go-dev
udev: true
url: https://github.com/alexbelgium/hassio-addons
usb: true
version: "source-20260708-2"
video: true

BIN
birdnet-go-dev/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
birdnet-go-dev/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

88
birdnet-go-dev/merge-prs.sh Executable file
View File

@@ -0,0 +1,88 @@
#!/usr/bin/env bash
#
# Build-time helper for the BirdNET-Go "from source" Home Assistant add-on.
#
# Clones the alexbelgium/birdnet-go fork, fast-forwards its main onto the
# tphakala/birdnet-go upstream (so main is fully synced with upstream), then
# merges every OPEN, NON-DRAFT ("in review") pull request on top - producing a
# source tree that is upstream main plus all work currently under review.
#
# The merged tree (including .git, which the BirdNET-Go build uses for version
# stamping) is written to the directory given as $1 so the Docker build can
# compile it.
#
# Environment:
# BIRDNET_FORK owner/repo of the fork (default alexbelgium/birdnet-go)
# BIRDNET_UPSTREAM owner/repo of the upstream (default tphakala/birdnet-go)
# GH_TOKEN / GITHUB_TOKEN optional, lifts the 60 req/h unauthenticated
# GitHub API rate limit; not required for public repos
#
set -euo pipefail
TARGET_DIR="${1:?usage: merge-prs.sh <target-dir>}"
FORK="${BIRDNET_FORK:-alexbelgium/birdnet-go}"
UPSTREAM="${BIRDNET_UPSTREAM:-tphakala/birdnet-go}"
FORK_URL="https://github.com/${FORK}.git"
UPSTREAM_URL="https://github.com/${UPSTREAM}.git"
API_URL="https://api.github.com/repos/${FORK}/pulls?state=open&per_page=100"
# Optional token to lift the unauthenticated GitHub API rate limit.
GH_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-}}"
log() { echo ">>> $*"; }
git config --global user.email "addon-builder@users.noreply.github.com"
git config --global user.name "BirdNET-Go Addon Builder"
git config --global advice.detachedHead false
log "Cloning fork ${FORK}"
git clone "${FORK_URL}" "${TARGET_DIR}"
cd "${TARGET_DIR}"
git checkout main
log "Syncing main with upstream ${UPSTREAM}"
git remote add upstream "${UPSTREAM_URL}"
git fetch --no-tags upstream main
# --no-ff keeps an explicit sync commit; a no-op when main is already current.
git merge --no-edit --no-ff upstream/main
log "Querying open non-draft PRs from ${FORK}"
auth_header=()
if [ -n "${GH_TOKEN}" ]; then
auth_header=(-H "Authorization: Bearer ${GH_TOKEN}")
fi
pr_json="$(curl -fsSL "${auth_header[@]}" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${API_URL}")"
# Open, non-draft PRs only ("in review"), oldest first for a stable merge order.
mapfile -t prs < <(echo "${pr_json}" \
| jq -r 'sort_by(.number) | .[] | select(.draft == false) | "\(.number)\t\(.head.sha)\t\(.title)"')
if [ "${#prs[@]}" -eq 0 ]; then
log "No open non-draft PRs to merge - building upstream-synced main only"
else
log "Merging ${#prs[@]} open non-draft PR(s)"
fi
for entry in "${prs[@]}"; do
number="$(printf '%s' "${entry}" | cut -f1)"
sha="$(printf '%s' "${entry}" | cut -f2)"
title="$(printf '%s' "${entry}" | cut -f3-)"
log "Merging PR #${number}: ${title} (${sha})"
# Fetch the PR head commit by number; works unauthenticated for public repos.
git fetch --no-tags origin "refs/pull/${number}/head"
if ! git merge --no-edit --no-ff -m "Merge PR #${number}: ${title}" "${sha}"; then
echo "!!! Merge conflict while merging PR #${number} (${title})." >&2
echo "!!! Resolve the conflict in the fork or pause this PR, then rebuild." >&2
git merge --abort || true
exit 1
fi
done
log "Merged HEAD: $(git rev-parse --short HEAD)"
log "Source tree ready at ${TARGET_DIR}"

View File

@@ -0,0 +1,194 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
# Default Variables
DEFAULT_BIRDSONGS_FOLDER="/data/clips"
CONFIG_LOCATION="/config/config.yaml"
# Strip trailing slashes; canonical internal form has none.
normalize_path() {
local p="$1"
while [[ "$p" == */ && "$p" != "/" ]]; do
p="${p%/}"
done
printf '%s' "$p"
}
# Reject paths containing characters that would break the SQL/YAML literals
# we substitute into below. We deliberately allow only "safe" filename chars.
# Also reject ".." path segments so a relative path can't traverse outside
# the directory we prefix it with (e.g. "/config") when we rewrite it below.
validate_safe_path() {
local p="$1"
if [[ ! "$p" =~ ^[A-Za-z0-9._/-]+$ ]]; then
bashio::log.fatal "Refusing unsafe path: '$p' (only [A-Za-z0-9._/-] allowed)"
exit 1
fi
local segment
local IFS=/
for segment in $p; do
if [[ "$segment" == ".." ]]; then
bashio::log.fatal "Refusing unsafe path: '$p' (path traversal '..' is not allowed)"
exit 1
fi
done
}
if [ ! -f "$CONFIG_LOCATION" ]; then
bashio::log.warning "There is no config.yaml yet in the config folder, downloading a default one. Please customize"
# Network may be unreachable on first boot. If the download fails, seed
# an empty YAML document so the yq reads/writes below succeed and the
# default-value seeding logic later in this script populates a usable
# config. (We can't remove the file and continue — subsequent yq calls
# under set -e would abort the init script.)
if ! curl -fL -s -S \
https://raw.githubusercontent.com/tphakala/birdnet-go/refs/heads/main/internal/conf/config.yaml \
-o "$CONFIG_LOCATION"; then
bashio::log.warning "Could not download default config.yaml; seeding an empty document so addon defaults can populate it"
echo '{}' > "$CONFIG_LOCATION"
fi
fi
#################
# Migrate Database
#################
if [ -f /data/birdnet.db ]; then
bashio::log.warning "Moving birdnet.db to /config/birdnet.db"
mv /data/birdnet.db /config
fi
######################
# Birdsongs Location
######################
# Read the current folder from config.yaml; "// """ collapses both missing
# keys and explicit nulls (e.g. in a freshly seeded "{}" doc) to an empty
# string so the ${VAR:-DEFAULT} fallback below kicks in.
CURRENT_BIRDSONGS_FOLDER="$(yq -r '.realtime.audio.export.path // ""' "$CONFIG_LOCATION")"
CURRENT_BIRDSONGS_FOLDER="${CURRENT_BIRDSONGS_FOLDER:-$DEFAULT_BIRDSONGS_FOLDER}"
# Treat the upstream-shipped relative "clips/" as the legacy default.
if [[ "$CURRENT_BIRDSONGS_FOLDER" == "clips" || "$CURRENT_BIRDSONGS_FOLDER" == "clips/" ]]; then
CURRENT_BIRDSONGS_FOLDER="$DEFAULT_BIRDSONGS_FOLDER"
fi
CURRENT_BIRDSONGS_FOLDER="$(normalize_path "$CURRENT_BIRDSONGS_FOLDER")"
# Set the new birdsongs folder from addon options (default: relative "clips").
BIRDSONGS_FOLDER="$(bashio::config "BIRDSONGS_FOLDER")"
BIRDSONGS_FOLDER="$(normalize_path "${BIRDSONGS_FOLDER:-clips}")"
validate_safe_path "$BIRDSONGS_FOLDER"
validate_safe_path "$CURRENT_BIRDSONGS_FOLDER"
if [[ ! "$BIRDSONGS_FOLDER" == /* ]]; then
if [ ! -d "/config/$BIRDSONGS_FOLDER" ]; then
mkdir -p "/config/$BIRDSONGS_FOLDER"
fi
if [ -d "/data/$BIRDSONGS_FOLDER" ]; then
if [ -n "$(ls -A /data/"$BIRDSONGS_FOLDER" 2> /dev/null)" ]; then
cp -rf /data/"$BIRDSONGS_FOLDER"/* "/config/$BIRDSONGS_FOLDER"/
fi
rm -r "/data/$BIRDSONGS_FOLDER"
fi
ln -sf "/config/$BIRDSONGS_FOLDER" "/data/$BIRDSONGS_FOLDER"
fi
bashio::log.info "... audio clips saved to $BIRDSONGS_FOLDER according to addon options"
# Migrate data if the folder has changed
if [[ "$CURRENT_BIRDSONGS_FOLDER" != "$BIRDSONGS_FOLDER" ]]; then
bashio::log.warning "Birdsongs folder changed from $CURRENT_BIRDSONGS_FOLDER to $BIRDSONGS_FOLDER"
# Update config.yaml with the new birdsongs folder path (trailing slash
# restored only at the boundary, since birdnet-go expects it).
yq -i -y ".realtime.audio.export.path = \"${BIRDSONGS_FOLDER}/\"" "$CONFIG_LOCATION"
# Move files only if sqlite paths changed
if [[ -d "$CURRENT_BIRDSONGS_FOLDER" && "$(ls -A "$CURRENT_BIRDSONGS_FOLDER")" ]]; then
bashio::log.warning "Migrating files from $CURRENT_BIRDSONGS_FOLDER to $BIRDSONGS_FOLDER"
# The absolute-path target (e.g. the default /config/clips) is never
# created by the relative-path block above, so ensure it exists before
# copying into it; otherwise cp aborts the init script under set -e.
mkdir -p "$BIRDSONGS_FOLDER"
cp -rnf "$CURRENT_BIRDSONGS_FOLDER"/* "$BIRDSONGS_FOLDER"/
mv "$CURRENT_BIRDSONGS_FOLDER" "${CURRENT_BIRDSONGS_FOLDER}_migrated"
fi
# Adapt the database
if [ -f /config/birdnet.db ]; then
backup="$(date +%Y%m%d_%H%M%S)"
BACKUP_FILE="/config/birdnet.db_${backup}"
bashio::log.warning "Modifying database paths from $CURRENT_BIRDSONGS_FOLDER to $BIRDSONGS_FOLDER. A backup will be created at ${BACKUP_FILE}"
# Create backup at the absolute path we'll restore from on failure.
if ! cp /config/birdnet.db "$BACKUP_FILE"; then
bashio::log.error "Failed to create a backup of the database. Aborting path modification."
exit 1
fi
# Paths were validated above against [A-Za-z0-9._/-]+ so quote
# escaping in the SQL literal is not a concern.
SQL_QUERY="UPDATE notes SET clip_name = '${BIRDSONGS_FOLDER}/' || substr(clip_name, length('${CURRENT_BIRDSONGS_FOLDER}/') + 1) WHERE clip_name LIKE '${CURRENT_BIRDSONGS_FOLDER}/%';"
if ! sqlite3 /config/birdnet.db "$SQL_QUERY"; then
bashio::log.warning "An error occurred while updating the paths. The database backup will be restored."
if [ -f "$BACKUP_FILE" ]; then
mv "$BACKUP_FILE" /config/birdnet.db
bashio::log.info "The database backup has been restored."
else
bashio::log.error "Backup file $BACKUP_FILE not found! Manual intervention required."
fi
else
bashio::log.info "Paths have been successfully updated."
fi
fi
fi
####################
# Correct Defaults
####################
# Seed addon-specific defaults only if the user has not set them in
# config.yaml. The "//=" form leaves any user-edited value alone, so
# changes made via the BirdNET-Go UI or by hand-editing /config/config.yaml
# survive container restarts.
bashio::log.info "Seeding default configuration values (only if missing)"
# Upstream's shipped default config.yaml explicitly sets output.sqlite.path
# to the relative "birdnet.db", so the "//=" below (default-if-missing)
# never fires for it. A relative path resolves against the app's working
# directory, which lives in the ephemeral container filesystem rather than
# a persistent volume, so the database is silently recreated empty on every
# restart. Rewrite any relative sqlite path to live under the persistent
# /config so detections survive reboots.
CURRENT_SQLITE_PATH="$(yq -r '.output.sqlite.path // ""' "$CONFIG_LOCATION")"
if [[ -n "$CURRENT_SQLITE_PATH" && "$CURRENT_SQLITE_PATH" != /* ]]; then
validate_safe_path "$CURRENT_SQLITE_PATH"
NEW_SQLITE_PATH="/config/${CURRENT_SQLITE_PATH}"
bashio::log.warning "output.sqlite.path ('$CURRENT_SQLITE_PATH') is relative and would not persist across restarts; rewriting to $NEW_SQLITE_PATH"
# SQLite does not create missing parent directories, so ensure one exists
# if the (validated, traversal-free) path includes a subdirectory.
mkdir -p "$(dirname "$NEW_SQLITE_PATH")"
yq -i -y ".output.sqlite.path = \"${NEW_SQLITE_PATH}\"" "$CONFIG_LOCATION"
fi
yq -i -y '.output.sqlite.path //= "/config/birdnet.db"' "$CONFIG_LOCATION"
####################
# Log Management
####################
LOG_MAX_SIZE_MB="$(bashio::config "LOG_MAX_SIZE_MB")"
LOG_MAX_SIZE_MB="${LOG_MAX_SIZE_MB:-50}"
LOG_MAX_AGE_DAYS="$(bashio::config "LOG_MAX_AGE_DAYS")"
LOG_MAX_AGE_DAYS="${LOG_MAX_AGE_DAYS:-7}"
bashio::log.info "Seeding default log rotation: max ${LOG_MAX_SIZE_MB}MB per file, max ${LOG_MAX_AGE_DAYS} days retention (only applied if not already set)"
# Seed log-rotation defaults; do not clobber user-edited values.
yq -i -y ".logging.file_output.max_size //= ${LOG_MAX_SIZE_MB}" "$CONFIG_LOCATION"
yq -i -y ".logging.file_output.max_age //= ${LOG_MAX_AGE_DAYS}" "$CONFIG_LOCATION"
yq -i -y '.logging.file_output.max_rotated_files //= 3' "$CONFIG_LOCATION"
yq -i -y '.logging.file_output.compress //= true' "$CONFIG_LOCATION"
# Trim existing log files that exceed the configured max age
LOG_DIR="/config/logs"
if [ -d "$LOG_DIR" ]; then
bashio::log.info "Trimming log files older than ${LOG_MAX_AGE_DAYS} days in ${LOG_DIR}"
ln -sf "$LOG_DIR" /logs
find "$LOG_DIR" -type f -name "*.log*" -mtime +"$LOG_MAX_AGE_DAYS" -delete 2>/dev/null || true
fi

View File

@@ -0,0 +1,18 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
#################
# NGINX SETTING #
#################
declare ingress_interface
declare ingress_port
echo "Adapting for ingress"
ingress_port=$(bashio::addon.ingress_port)
ingress_interface=$(bashio::addon.ip_address)
ingress_entry=$(bashio::addon.ingress_entry)
sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf
sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf
sed -i "s|%%ingress_entry%%|${ingress_entry}|g" /etc/nginx/servers/ingress.conf

View File

@@ -0,0 +1,112 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
# When the Home Assistant MariaDB addon is active, optionally wire its
# credentials directly into BirdNET-Go's config.yaml. Upstream reads MySQL
# settings only from YAML (no env-var overrides exist), so this is the only
# way to auto-configure them. The behaviour is opt-in via the
# mariadb_auto_config addon option.
#
# When the option is off but MariaDB is detected, we log a one-shot hint and
# ensure config.yaml falls back to SQLite (reverting any previously written
# mysql block). This is safe because BirdNET-Go's config.yaml defaults to
# SQLite and the mysql block is only ever written by this script.
#
# When the option is on we:
# 1. Create the "birdnet" database if it does not already exist — birdnet-go
# connects to an existing schema and does not create it automatically.
# 2. Write the MySQL credentials into config.yaml and disable SQLite.
CONFIG_LOCATION="/config/config.yaml"
MYSQL_DATABASE="birdnet"
if ! bashio::services.available 'mysql'; then
exit 0
fi
MYSQL_HOST="$(bashio::services 'mysql' 'host')"
MYSQL_PORT="$(bashio::services 'mysql' 'port')"
MYSQL_USER="$(bashio::services 'mysql' 'username')"
MYSQL_PASS="$(bashio::services 'mysql' 'password')"
if ! bashio::config.true 'mariadb_auto_config'; then
bashio::log.green "---"
bashio::log.yellow "Home Assistant MariaDB addon detected but mariadb_auto_config is disabled; ensuring BirdNET-Go uses SQLite."
bashio::log.yellow "Set 'mariadb_auto_config: true' in the addon options to wire MariaDB into BirdNET-Go automatically. Connection details:"
bashio::log.blue "Database user : ${MYSQL_USER}"
bashio::log.blue "Database password: [redacted]"
bashio::log.blue "Database name : ${MYSQL_DATABASE}"
bashio::log.blue "Host-name : ${MYSQL_HOST}"
bashio::log.blue "Port : ${MYSQL_PORT}"
bashio::log.green "---"
if [ -f "$CONFIG_LOCATION" ]; then
# Only revert if config.yaml points at the HA MariaDB host we would have
# written — a mysql block pointing at a different host was set manually.
# shellcheck disable=SC2016
CURRENT_MYSQL_HOST="$(yq -r '.output.mysql.host // empty' "$CONFIG_LOCATION" 2>/dev/null || true)"
if yq -e '.output.mysql.enabled == true' "$CONFIG_LOCATION" >/dev/null 2>&1 \
&& [ "${CURRENT_MYSQL_HOST}" = "${MYSQL_HOST}" ]; then
yq -i -y \
'.output.mysql.enabled = false
| .output.sqlite.enabled = true' \
"$CONFIG_LOCATION"
fi
fi
exit 0
fi
if [ ! -f "$CONFIG_LOCATION" ]; then
bashio::log.warning "Skipping MariaDB auto-configuration: $CONFIG_LOCATION not found"
exit 0
fi
bashio::log.green "---"
bashio::log.blue "mariadb_auto_config enabled; creating MariaDB database and wiring credentials into BirdNET-Go config"
bashio::log.blue "Host: ${MYSQL_HOST}:${MYSQL_PORT}"
bashio::log.blue "User: ${MYSQL_USER}"
bashio::log.blue "Database: ${MYSQL_DATABASE}"
bashio::log.green "---"
# Resolve MariaDB hostname to IPv4: on HAOS >=17.3 the Supervisor network
# gained IPv6, but the MariaDB addon only grants its user from the IPv4
# subnet. Fall back to the raw hostname if resolution fails.
MYSQL_HOST_RESOLVED="$(getent ahostsv4 "${MYSQL_HOST}" 2>/dev/null | awk '{print $1; exit}')"
MYSQL_HOST_RESOLVED="${MYSQL_HOST_RESOLVED:-${MYSQL_HOST}}"
if [ "${MYSQL_HOST_RESOLVED}" != "${MYSQL_HOST}" ]; then
bashio::log.blue "Resolved ${MYSQL_HOST} -> ${MYSQL_HOST_RESOLVED} (forcing IPv4)"
fi
# Create the database — birdnet-go connects to an existing schema and does NOT
# create it automatically. MYSQL_PWD avoids exposing the password via the
# process command line.
if ! MYSQL_PWD="${MYSQL_PASS}" mysql \
--skip-ssl \
--host="${MYSQL_HOST_RESOLVED}" \
--port="${MYSQL_PORT}" \
--user="${MYSQL_USER}" \
--connect-timeout=10 \
-e "CREATE DATABASE IF NOT EXISTS \`${MYSQL_DATABASE}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"; then
bashio::log.error "Failed to create MariaDB database '${MYSQL_DATABASE}' — verify the MariaDB addon is running and the user has CREATE DATABASE privileges"
exit 1
fi
bashio::log.blue "Database '${MYSQL_DATABASE}' is ready"
# Upstream config.go stores port as a string; pass it as such to match.
# $host / $port / etc. are jq/yq variables, not shell expansions — the
# single quotes around the filter are intentional.
# shellcheck disable=SC2016
yq -i -y \
--arg host "$MYSQL_HOST" \
--arg port "$MYSQL_PORT" \
--arg user "$MYSQL_USER" \
--arg pass "$MYSQL_PASS" \
--arg db "$MYSQL_DATABASE" \
'.output.mysql.enabled = true
| .output.mysql.host = $host
| .output.mysql.port = $port
| .output.mysql.username = $user
| .output.mysql.password = $pass
| .output.mysql.database = $db
| .output.sqlite.enabled = false' \
"$CONFIG_LOCATION"

View File

@@ -0,0 +1,77 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
# When the Home Assistant MQTT addon is active, optionally wire its
# credentials directly into BirdNET-Go's config.yaml. Upstream reads MQTT
# settings only from YAML (no env-var overrides exist), so this is the only
# way to auto-configure them. The behaviour is opt-in via the
# mqtt_auto_config addon option. When the option is off but Mosquitto is
# detected, we log a one-shot hint pointing users at the option.
#
# In addition to the broker credentials we enable BirdNET-Go's native Home
# Assistant MQTT auto-discovery (realtime.mqtt.homeassistant.*). This makes
# the detection sensors appear in Home Assistant automatically, so users no
# longer have to hand-write the MQTT sensor YAML from HAINTEGRATION.md.
CONFIG_LOCATION="/config/config.yaml"
if ! bashio::services.available 'mqtt'; then
exit 0
fi
MQTT_HOST="$(bashio::services 'mqtt' 'host')"
MQTT_PORT="$(bashio::services 'mqtt' 'port')"
MQTT_USER="$(bashio::services 'mqtt' 'username')"
MQTT_PASS="$(bashio::services 'mqtt' 'password')"
MQTT_BROKER="tcp://${MQTT_HOST}:${MQTT_PORT}"
if ! bashio::config.true 'mqtt_auto_config'; then
bashio::log.green "---"
bashio::log.yellow "Home Assistant MQTT addon detected. Set 'mqtt_auto_config: true' in the addon options to wire it into BirdNET-Go automatically AND enable Home Assistant auto-discovery (sensors appear in HA with no manual YAML). Connection details:"
bashio::log.blue "MQTT user : ${MQTT_USER}"
bashio::log.blue "MQTT password: ${MQTT_PASS}"
bashio::log.blue "MQTT broker : ${MQTT_BROKER}"
bashio::log.green "---"
exit 0
fi
if [ ! -f "$CONFIG_LOCATION" ]; then
bashio::log.warning "Skipping MQTT auto-configuration: $CONFIG_LOCATION not found"
exit 0
fi
bashio::log.green "---"
bashio::log.blue "mqtt_auto_config enabled; writing Home Assistant MQTT credentials into BirdNET-Go config"
bashio::log.blue "Broker: ${MQTT_BROKER}"
bashio::log.blue "User: ${MQTT_USER}"
bashio::log.blue "Home Assistant auto-discovery: enabled (sensors appear in HA automatically)"
bashio::log.green "---"
# $broker / $user / $pass / "birdnet" are jq/yq variables and literals,
# not shell expansions, so the single quotes are intentional.
#
# Connection fields (enabled/broker/username/password) are force-set on every
# start so they track the HA MQTT addon's rotating credentials. Topic and the
# homeassistant.* discovery knobs use "//=" so they are only seeded when
# missing. retain is seeded via an explicit has() check rather than "//=",
# because jq treats a user-set "retain: false" as falsy and "//=" would wrongly
# flip it back to true; has() seeds the default only when the key is truly
# absent. Any value the user later changes in the BirdNET-Go UI or config.yaml
# therefore survives restarts. homeassistant.enabled is force-set to true
# because turning on discovery is the whole point of the auto-config option.
# shellcheck disable=SC2016
yq -i -y \
--arg broker "$MQTT_BROKER" \
--arg user "$MQTT_USER" \
--arg pass "$MQTT_PASS" \
'.realtime.mqtt.enabled = true
| .realtime.mqtt.broker = $broker
| .realtime.mqtt.username = $user
| .realtime.mqtt.password = $pass
| .realtime.mqtt.topic //= "birdnet"
| (if (.realtime.mqtt | has("retain")) then . else .realtime.mqtt.retain = true end)
| .realtime.mqtt.homeassistant.enabled = true
| .realtime.mqtt.homeassistant.discovery_prefix //= "homeassistant"
| .realtime.mqtt.homeassistant.device_name //= "BirdNET-Go"' \
"$CONFIG_LOCATION"

View File

@@ -0,0 +1,47 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e
#################
# INITALISATION #
#################
echo " "
# Allow advanced users to supply their own ALSA config (e.g. to enable JACK
# or a custom dsnoop chain) by dropping asound.conf into the addon config
# folder. Written to /root/.asoundrc because /etc/asound.conf is read-only.
if [ -f /config/asound.conf ]; then
if [ -r /config/asound.conf ]; then
bashio::log.info "Using user-provided /config/asound.conf, overriding addon defaults"
if ! cp /config/asound.conf /root/.asoundrc; then
bashio::log.warning "Failed to copy /config/asound.conf; continuing with bundled /root/.asoundrc defaults"
fi
else
bashio::log.warning "/config/asound.conf exists but is not readable; continuing with bundled /root/.asoundrc defaults"
fi
fi
# Check if alsa_card is provided
CONFIG_LOCATION="/config/config.yaml"
if bashio::config.true "homeassistant_microphone"; then
bashio::log.info "homeassistant_microphone option is selected. The audio card config value is set to 'default'. Set in the addon options to which this is set"
audio_card="default"
yq -iy ".realtime.audio.source = \"${audio_card}\"" "$CONFIG_LOCATION"
else
bashio::log.info "homeassistant_microphone option is not set, keeping audio source configured via the UI"
fi
########################
# CONFIGURE birdnet-go #
########################
bashio::log.info "Starting app..."
# shellcheck disable=SC2086
/usr/bin/entrypoint.sh birdnet-go realtime &
true
# Wait for app to become available to start nginx
bashio::net.wait_for 8080 localhost 900
bashio::log.info "Starting NGinx..."
exec nginx

View File

@@ -0,0 +1,96 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

View File

@@ -0,0 +1,16 @@
proxy_http_version 1.1;
proxy_ignore_client_abort off;
proxy_read_timeout 86400s;
proxy_redirect off;
proxy_send_timeout 86400s;
proxy_max_temp_file_size 0;
proxy_hide_header X-Frame-Options;
proxy_set_header Accept-Encoding "";
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;

View File

@@ -0,0 +1 @@
resolver 127.0.0.11 ipv6=off;

View File

@@ -0,0 +1,6 @@
root /dev/null;
server_name $hostname;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;

View File

@@ -0,0 +1,9 @@
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;

View File

@@ -0,0 +1,81 @@
# Run nginx in foreground.
daemon off;
# This is run inside Docker.
user root;
# Pid storage location.
pid /var/run/nginx.pid;
# Set number of worker processes.
worker_processes auto;
# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
# Write error log to Hass.io add-on log.
error_log /proc/1/fd/1 error;
# Load allowed environment vars
env HASSIO_TOKEN;
# Load dynamic modules.
include /etc/nginx/modules/*.conf;
# Max num of simultaneous connections by a worker process.
events {
worker_connections 8192;
}
http {
include /etc/nginx/includes/mime.types;
# https://emby.media/community/index.php?/topic/93074-how-to-emby-with-nginx-with-windows-specific-tips-and-csp-options/
server_names_hash_bucket_size 64;
gzip_disable "msie6";
gzip_comp_level 6;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_proxied any;
gzip_types
text/plain
text/css
text/js
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
image/svg+xml;
proxy_connect_timeout 1h;
log_format hassio '[$time_local] $status '
'$http_x_forwarded_for($remote_addr) '
'$request ($http_user_agent)';
access_log /proc/1/fd/1 hassio;
client_max_body_size 4G;
default_type application/octet-stream;
gzip on;
keepalive_timeout 65;
sendfile on;
server_tokens off;
tcp_nodelay on;
tcp_nopush on;
map $time_iso8601 $today {
default "";
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
include /etc/nginx/includes/resolver.conf;
include /etc/nginx/servers/*.conf;
}

View File

@@ -0,0 +1,54 @@
server {
listen %%interface%%:%%port%% default_server;
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
location / {
proxy_pass http://localhost:8080/;
rewrite ^%%ingress_entry%%/?(.*)$ /$1 break;
# Disable buffering (required for SSE and sub_filter)
proxy_buffering off;
proxy_request_buffering off;
proxy_cache off;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Tell BirdNET-Go its proxy prefix. Upstream honours X-Ingress-Path
# in internal/api/server.go and rewrites HTML href/src/action
# attributes itself, so we no longer duplicate that work here.
proxy_set_header X-Ingress-Path %%ingress_entry%%;
# Prevent timeouts
proxy_read_timeout 86400;
proxy_send_timeout 86400;
# sub_filter setup: keep gzip off and the type filter wide so JS gets
# touched too (the upstream HTML rewriter does not look inside JS).
proxy_set_header Accept-Encoding "";
sub_filter_once off;
sub_filter_types *;
# JS string-literal rewrites — paths embedded in JS code are not
# touched by upstream's HTML rewriter, so we patch them here.
sub_filter EventSource('/ EventSource('%%ingress_entry%%/;
sub_filter fetch('/ fetch('%%ingress_entry%%/;
sub_filter `/api/v `%%ingress_entry%%/api/v;
sub_filter "'/api/v" "'%%ingress_entry%%/api/v";
sub_filter \"/api/v \"%%ingress_entry%%/api/v;
sub_filter `/u `%%ingress_entry%%/u;
sub_filter "'/u" "'%%ingress_entry%%/u";
sub_filter \"/u \"%%ingress_entry%%/u;
sub_filter `/asset `%%ingress_entry%%/asset;
sub_filter "'/asset" "'%%ingress_entry%%/asset";
sub_filter \"/asset \"%%ingress_entry%%/asset;
sub_filter window.location.origin} window.location.origin}%%ingress_entry%%;
}
}

View File

@@ -0,0 +1,35 @@
# Home Assistant addon: birdnet-go
#
# Shipped at /root/.asoundrc because /etc/asound.conf is read-only in this
# environment. ALSA loads ~/.asoundrc as an additive layer on top of the
# system config, so the overrides below take effect for the birdnet-go process
# (which runs as root, HOME=/root).
#
# Replace ALSA PCM plugins whose backends are not available in the addon
# container (JACK server, OSS /dev/dsp) with type "null" and hide them from
# snd_device_name_hint(). Without this, miniaudio's capture-device enumeration
# probes every PCM hint at startup, triggering noisy errors like:
# - "Cannot connect to server socket" / "jack server is not running" (libjack)
# - "ALSA lib pcm_oss.c: Cannot open device /dev/dsp"
# - "ALSA lib pcm_dsnoop.c: unable to open slave"
# BirdNET-Go itself never opens these PCMs; only the enumeration probe does.
pcm.!jack {
type null
hint.show off
}
pcm.!oss {
type null
hint.show off
}
pcm.!dsp {
type null
hint.show off
}
pcm.!dsnoop {
type null
hint.show off
}

BIN
birdnet-go-dev/stats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,12 @@
{
"github_beta": true,
"github_exclude": "-4",
"github_fulltag": true,
"last_update": "2026-06-07",
"paused": true,
"repository": "alexbelgium/hassio-addons",
"slug": "birdnet-go",
"source": "github",
"upstream_repo": "tphakala/birdnet-go",
"upstream_version": "nightly-20260601"
}

View File

@@ -1,3 +1,9 @@
## nightly-20260615-3 (05-07-2026)
- Harden the `output.sqlite.path` rewrite added for the persistence fix: reject paths containing `..` traversal segments (shared `validate_safe_path` check), and create the destination's parent directory under `/config` when the relative path includes a subdirectory (e.g. `db/birdnet.db`), since SQLite does not create missing parent directories itself.
## nightly-20260615-2 (05-07-2026)
- Minor bugs fixed
- Fix detections/database not persisting across restarts on a fresh install: upstream's default `config.yaml` ships `output.sqlite.path: birdnet.db` (relative) explicitly, so the missing-only (`//=`) seeding introduced previously never rewrote it to an absolute path. A relative path resolves against the app's ephemeral working directory, so the database was silently recreated empty on every restart. Any relative `output.sqlite.path` is now rewritten to live under the persistent `/config` on startup; values already set to an absolute path are left untouched. (https://github.com/tphakala/birdnet-go/discussions/3774)
- MQTT auto-config now also enables BirdNET-Go's native Home Assistant MQTT auto-discovery: detection sensors appear in Home Assistant automatically with no manual YAML (existing UI/config.yaml edits are preserved)
- MQTT auto-config seeds `realtime.mqtt.retain: true` (only when unset) so sensor states survive Home Assistant restarts
- Added supervisor watchdog (tcp://[HOST]:[PORT:8080]) so the add-on is automatically restarted if BirdNET-Go stops responding

View File

@@ -128,4 +128,4 @@ slug: birdnet-go
udev: true
url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-go
usb: true
version: "nightly-20260615"
version: "nightly-20260615-3"

View File

@@ -17,12 +17,22 @@ normalize_path() {
# Reject paths containing characters that would break the SQL/YAML literals
# we substitute into below. We deliberately allow only "safe" filename chars.
# Also reject ".." path segments so a relative path can't traverse outside
# the directory we prefix it with (e.g. "/config") when we rewrite it below.
validate_safe_path() {
local p="$1"
if [[ ! "$p" =~ ^[A-Za-z0-9._/-]+$ ]]; then
bashio::log.fatal "Refusing unsafe path: '$p' (only [A-Za-z0-9._/-] allowed)"
exit 1
fi
local segment
local IFS=/
for segment in $p; do
if [[ "$segment" == ".." ]]; then
bashio::log.fatal "Refusing unsafe path: '$p' (path traversal '..' is not allowed)"
exit 1
fi
done
}
if [ ! -f "$CONFIG_LOCATION" ]; then
@@ -135,6 +145,24 @@ fi
# survive container restarts.
bashio::log.info "Seeding default configuration values (only if missing)"
# Upstream's shipped default config.yaml explicitly sets output.sqlite.path
# to the relative "birdnet.db", so the "//=" below (default-if-missing)
# never fires for it. A relative path resolves against the app's working
# directory, which lives in the ephemeral container filesystem rather than
# a persistent volume, so the database is silently recreated empty on every
# restart. Rewrite any relative sqlite path to live under the persistent
# /config so detections survive reboots.
CURRENT_SQLITE_PATH="$(yq -r '.output.sqlite.path // ""' "$CONFIG_LOCATION")"
if [[ -n "$CURRENT_SQLITE_PATH" && "$CURRENT_SQLITE_PATH" != /* ]]; then
validate_safe_path "$CURRENT_SQLITE_PATH"
NEW_SQLITE_PATH="/config/${CURRENT_SQLITE_PATH}"
bashio::log.warning "output.sqlite.path ('$CURRENT_SQLITE_PATH') is relative and would not persist across restarts; rewriting to $NEW_SQLITE_PATH"
# SQLite does not create missing parent directories, so ensure one exists
# if the (validated, traversal-free) path includes a subdirectory.
mkdir -p "$(dirname "$NEW_SQLITE_PATH")"
yq -i -y ".output.sqlite.path = \"${NEW_SQLITE_PATH}\"" "$CONFIG_LOCATION"
fi
yq -i -y '.output.sqlite.path //= "/config/birdnet.db"' "$CONFIG_LOCATION"
####################

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,4 +1,11 @@
## 0.8.2.1 (2026-07-05)
- Re-tag of 0.8.2-1 with no content change. Home Assistant compares add-on versions with semver semantics, where a `-N` suffix counts as a *pre-release* and sorts **below** the base version — so users already on 0.8.2 saw the 0.8.2-1 nginx fix as "Up-to-date" with the Update button disabled. Four-segment `0.8.2.1` sorts above both `0.8.2` and `0.8.2-1` (and below the next upstream `0.8.3`), so the update becomes installable everywhere.
## 0.8.2-1 (2026-07-04)
- Fix nginx failing to start with `limit_req_zone "api_rl" already bound` (502 on every page, Suncuss/BirdNET-PiPy#59). Upstream 0.8.2 added http-level rate-limit zones, and the ingress config was generated as a full copy of nginx.conf — duplicating those declarations in the shared http context. `ingress.conf` is now built from the `server` block only, so http-level directives stay declared once.
- Comment out the new upstream Content-Security-Policy header in ingress mode, matching the existing X-* security-header handling (ingress runs inside Home Assistant's authenticated frame; direct access keeps the full CSP).
## 0.8.2 (2026-07-04)
- Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases)

View File

@@ -96,4 +96,4 @@ schema:
ssl: bool?
slug: birdnet-pipy
url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-pipy
version: "0.8.2"
version: "0.8.2.1"

View File

@@ -24,11 +24,18 @@ if ! [[ "${ingress_port}" =~ ^[0-9]+$ ]] || [[ "${ingress_port}" -le 0 ]]; then
exit 0
fi
cp /etc/nginx/servers/nginx.conf /etc/nginx/servers/ingress.conf
# Build the ingress server from the server block ONLY. Both files in
# servers/ are included into one http context, so http-level directives
# (map, limit_req_zone, ...) must stay declared once, in nginx.conf — a
# duplicated limit_req_zone is a fatal "already bound" error and nginx
# never starts (502 on every page). Upstream keeps all http-level
# directives above its single column-0 "server {".
sed -n '/^server {/,$p' /etc/nginx/servers/nginx.conf > /etc/nginx/servers/ingress.conf
sed -i \
-e "s|listen 80;|listen ${ingress_interface}:${ingress_port} default_server;|g" \
-e "/index index.html;/a\\ include /etc/nginx/includes/ingress_params.conf;" \
-e 's|^[[:space:]]*add_header X|#&|g' \
-e 's|^[[:space:]]*add_header Content-Security-Policy|#&|g' \
/etc/nginx/servers/ingress.conf
sed -i "s#%%ingress_entry%%#${ingress_entry}#g" /etc/nginx/includes/ingress_params.conf

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,53 @@
## 1.8 (08-07-2026)
- Expose the Headroom live savings dashboard on mapped port `8787` when `install_headroom` is enabled, while keeping the proxy local-only if the port mapping is disabled.
- Map `8787/tcp` by default and document the dashboard URL.
## 1.7 (07-07-2026)
- Add an hourly rtk + headroom token-savings report to the add-on log (`claude-gains-report.sh`, seeded via `/defaults/crontabs/root` and run by the base image's cron). Lets you confirm at a glance that both tools keep running and see accumulated gains — if the numbers stop growing, the tool has stopped working.
- Run the Headroom optimization proxy as a supervised local backend (`svc-headroom` on 127.0.0.1:8787) so the `headroom_compress`/`headroom_retrieve` MCP tools can actually store/retrieve compressed content and record savings. It is a **backend only** — no client's `ANTHROPIC_BASE_URL` is routed through it, so the Claude Desktop app (which force-overrides it, [headroom #869](https://github.com/headroomlabs-ai/headroom/issues/869)) is unaffected. Previously the MCP server was registered but had no backend, so it recorded no savings.
- Nudge Claude to use the headroom compression tools via a managed, idempotent block appended to the user's `CLAUDE.md` (removed automatically when `install_headroom` is disabled).
## 1.6 (07-07-2026)
- Fix default desktop launch failing when `install_headroom` is enabled (the default): the code rewrote the launch to `headroom wrap claude-desktop …`, but `headroom wrap` only supports coding-agent CLIs (`claude`, `codex`, ...) with arguments after `--`, so it produced an invalid command that left the app unlaunched. Leave the plain launch intact and instead register the `headroom` MCP server (`headroom mcp serve`) in Claude Desktop's config, exposing the `headroom_compress`/`headroom_retrieve`/`headroom_stats` tools inside the app. This is the supported headroom integration for Desktop, which overrides `ANTHROPIC_BASE_URL` so transparent proxy compression is not possible ([headroom #869](https://github.com/headroomlabs-ai/headroom/issues/869)). Disabling the option removes the entry again.
- Make the autostart resilient: if the launch command fails to start, fall back to the plain Claude Desktop launch so the app always comes up.
## 1.5 (07-07-2026)
- Change the default Claude Desktop data location to `/data/data`.
- Add Home Assistant smart context, Home Assistant MCP, and dangerous permission skip options.
## 1.4 (07-07-2026)
- Persist Claude Desktop sign-in: bundle gnome-keyring/libsecret/dbus-x11 and start an unlocked Secret Service in the desktop session, and launch with --password-store=gnome-libsecret. Fixes "Your sign-in won't be saved on this device. Install and unlock a system keyring". The keyring DB lives on persistent storage (/config/data), so the session survives restarts.
## 1.3 (07-07-2026)
- Fix startup crash loop ("All subprocesses terminated. Exiting."):
- Make the Selkies desktop init oneshots (init-video, init-selkies-config) tolerant so a partially-permitted device/permission op in the HA sandbox no longer fails add-on bringup.
- Pre-create /tmp/selkies_js.log so the base image's "chmod 777 /tmp/selkies*" calls never fail on an empty glob.
- Reconcile XDG_RUNTIME_DIR to the tmpfs runtime dir instead of persistent storage.
- Run Claude Desktop with --disable-dev-shm-usage and drop the misplaced shm_size env var (Home Assistant ignores shm_size), fixing Electron renderer crashes on the default 64 MB /dev/shm.
## 1.2 (07-07-2026)
- Add baked-in git/GitHub CLI support with optional startup credential configuration.
- Fix Selkies startup by creating the s6 environment directory and XDG runtime directory before services start.
## 1.0 (07-07-2026)
- Minor bugs fixed
# Changelog
## 1.1
- Fix build failure: set HOME=/root when running rtk install script to ensure binary is installed to /root/.local/bin instead of /config/.local/bin (caused by LSIO base image overriding HOME)
## 1.0
- Fix build failure: remove separate `npm` apt package (already bundled in NodeSource nodejs)
## debianbookworm-1ae1f8ff-ls13
- Initial Claude Desktop add-on using LinuxServer Selkies, Home Assistant ingress, persistent sign-in data, runtime Claude Desktop updates, optional apt/pip additions, custom scripts, and bundled Claude Code optimization tools.

142
claude_desktop/Dockerfile Normal file
View File

@@ -0,0 +1,142 @@
#============================#
# ALEXBELGIUM'S DOCKERFILE #
#============================#
#=== Home Assistant Addon ===#
#################
# 1 Build Image #
#################
ARG BUILD_FROM
ARG BUILD_VERSION
FROM ${BUILD_FROM}
##################
# 2 Modify Image #
##################
# Set S6 wait time
ENV S6_CMD_WAIT_FOR_SERVICES=1 \
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
S6_SERVICES_GRACETIME=0
USER root
# load volume
VOLUME [ "/sys/fs/cgroup" ]
# Set shell
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Allow UID and GID setting
# hadolint ignore=SC2015,DL4006,SC2013,SC2086
RUN \
usermod --home /data/data abc && \
if [[ -d /etc/services.d ]] && ls /etc/services.d/*/run 1> /dev/null 2>&1; then sed -i "1a set +e" /etc/services.d/*/run; fi
# Global LSIO modifications
COPY ha_lsio.sh /ha_lsio.sh
ARG CONFIGLOCATION="/data/data"
RUN chmod 744 /ha_lsio.sh && if grep -qr "lsio" /etc; then /ha_lsio.sh "$CONFIGLOCATION"; fi && rm /ha_lsio.sh
##################
# 3 Install apps #
##################
# Add rootfs
COPY rootfs/ /
RUN find . -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \;
# Uses /bin for compatibility purposes
# hadolint ignore=DL4005
RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \
if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi
# Install Claude Desktop and Python tooling
RUN curl -fsSLo /usr/share/keyrings/claude-desktop-archive-keyring.asc https://downloads.claude.ai/claude-desktop/key.asc && \
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/claude-desktop-archive-keyring.asc] https://downloads.claude.ai/claude-desktop/apt/stable stable main" > /etc/apt/sources.list.d/claude-desktop.list && \
apt-get update && \
apt-get install -y --no-install-recommends \
claude-desktop \
python3-pip \
libsecret-1-0 \
dbus-x11 \
git gh && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Install token-optimization tool prerequisites and binaries at build time
RUN apt-get update && \
apt-get install -y --no-install-recommends nodejs && \
pip3 install --break-system-packages "headroom-ai[all]" && \
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh -o /tmp/rtk-install.sh && \
HOME=/root sh /tmp/rtk-install.sh && \
rm /tmp/rtk-install.sh && \
if [ -x /root/.local/bin/rtk ] && [ ! -x /usr/local/bin/rtk ]; then mv /root/.local/bin/rtk /usr/local/bin/rtk; fi && \
if [ -x /usr/local/bin/rtk ]; then chmod +x /usr/local/bin/rtk; else echo "rtk binary was not installed on PATH"; exit 1; fi && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /root/.cache
# Modules
ARG MODULES="00-banner.sh 00-global_var.sh 01-custom_script.sh 00-local_mounts.sh 00-smb_mounts.sh 90-dns_set.sh"
# Automatic modules download
COPY ha_automodules.sh /ha_automodules.sh
RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_automodules.sh
# Manual apps
ENV PACKAGES="nginx"
# Automatic apps & bashio
COPY ha_autoapps.sh /ha_autoapps.sh
RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh
################
# 4 Entrypoint #
################
# Add entrypoint
COPY ha_entrypoint.sh /ha_entrypoint.sh
RUN chmod 777 /ha_entrypoint.sh
# Install bashio
COPY bashio-standalone.sh /usr/local/lib/bashio-standalone.sh
RUN chmod 0755 /usr/local/lib/bashio-standalone.sh
ENTRYPOINT [ "/usr/bin/env" ]
CMD [ "/ha_entrypoint.sh" ]
############
# 5 Labels #
############
ARG BUILD_ARCH
ARG BUILD_DATE
ARG BUILD_DESCRIPTION
ARG BUILD_NAME
ARG BUILD_REF
ARG BUILD_REPOSITORY
ARG BUILD_VERSION
ENV BUILD_VERSION="${BUILD_VERSION}"
LABEL \
io.hass.name="${BUILD_NAME}" \
io.hass.description="${BUILD_DESCRIPTION}" \
io.hass.arch="${BUILD_ARCH}" \
io.hass.type="addon" \
io.hass.version=${BUILD_VERSION} \
maintainer="alexbelgium (https://github.com/alexbelgium)" \
org.opencontainers.image.title="${BUILD_NAME}" \
org.opencontainers.image.description="${BUILD_DESCRIPTION}" \
org.opencontainers.image.vendor="Home Assistant Add-ons" \
org.opencontainers.image.authors="alexbelgium (https://github.com/alexbelgium)" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.url="https://github.com/alexbelgium" \
org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \
org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \
org.opencontainers.image.created=${BUILD_DATE} \
org.opencontainers.image.revision=${BUILD_REF} \
org.opencontainers.image.version=${BUILD_VERSION}
####################
# 6 HealthcheckNOT #
####################

73
claude_desktop/README.md Normal file
View File

@@ -0,0 +1,73 @@
# Home assistant add-on: Claude Desktop
![Supports aarch64 Architecture][aarch64-shield]
![Supports amd64 Architecture][amd64-shield]
![Project Maintenance][maintenance-shield]
Run the Claude Desktop Linux app inside a LinuxServer.io Selkies container and stream it through Home Assistant ingress.
## Installation
1. Add this repository to the Home Assistant add-on store.
2. Install **Claude Desktop**.
3. Start the add-on and open the web UI from the sidebar.
4. Sign in with your Claude account from the Desktop app.
Claude Desktop sign-in requires a claude.ai plan that supports the Desktop app. API keys are not accepted by the Desktop application. Anthropic's Linux beta does not include Computer Use or dictation.
## Features
- Claude Desktop in single-app Selkies mode.
- Home Assistant ingress support.
- Persistent `$HOME` under `/data/data`, preserving Claude Desktop and Claude Code sign-in state across restarts.
- Optional runtime Claude Desktop updates from Anthropic's apt repository.
- Optional extra apt and pip package installation.
- Baked-in `git` and GitHub CLI (`gh`) with optional startup credential configuration.
- Custom script support through the repository standard `claude_desktop.sh` script.
- Optional bundled Claude Code optimization tools: headroom, rtk, and caveman.
- Headroom dashboard exposed on mapped port `8787` when `install_headroom` is enabled.
- Low-power defaults: GPU device mapping, `AUTO_GPU=1`, `SELKIES_FRAMERATE=30`, `/tmp` tmpfs, and `$HOME/.cache` redirected to `/tmp/cache`.
## Options
| Option | Default | Description |
| ------ | ------- | ----------- |
| `PUID` / `PGID` | `0` / `0` | User and group used for persistent data ownership. |
| `TZ` | | Optional timezone, for example `America/New_York`. |
| `KEYBOARD` | | Optional Selkies keyboard layout. |
| `PASSWORD` | | Optional password for direct Selkies ports. Set this before exposing ports `3000` or `3001`. |
| `DRINODE` | | Optional GPU device override for Selkies. |
| `DNS_server` | `8.8.8.8` | DNS server used by the standard DNS module. |
| `auto_update` | `true` | Check Anthropic's apt repository and upgrade `claude-desktop` at add-on startup. |
| `install_headroom` | `true` | Register the baked-in `headroom` MCP server in Claude Desktop, start the Headroom proxy backend, and expose the Headroom dashboard on mapped port `8787` at `/dashboard` when that port is open. This exposes the `headroom_compress`/`headroom_retrieve`/`headroom_stats` context-compression tools inside the app. (Claude Desktop overrides `ANTHROPIC_BASE_URL`, so transparent proxy compression is not possible — MCP is the supported path; see [headroom #869](https://github.com/headroomlabs-ai/headroom/issues/869).) Disabling removes the MCP entry and stops the backend/dashboard service. |
| `install_rtk` | `true` | Configure the rtk Claude Code `PreToolUse` hook in the persistent Claude Code settings. |
| `install_caveman` | `true` | Install the caveman Claude Code plugin into the persistent Claude Code home. |
| `install_github_cli` | `true` | Enable first-start checks and setup for the baked-in `git` and `gh` commands. |
| `github_token` | | Optional GitHub personal access token used to authenticate `gh` and configure Git credentials for GitHub. |
| `github_username` | | Optional global Git author name. |
| `github_email` | | Optional global Git author email. |
| `ha_smart_context` | `true` | Enable Home Assistant smart context support for Claude tooling. |
| `enable_ha_mcp` | `true` | Enable Home Assistant MCP support for Claude tooling. |
| `dangerously_skip_permissions` | `false` | Expose Claude Code's dangerous permission skip mode option. Only enable if you understand the risk. |
| `additional_apps` | | Comma-separated Debian apt packages to install at startup, for example `htop,git`. |
| `additional_pip` | | Comma-separated pip packages to install at startup. Installs use `--break-system-packages`. |
| `data_location` | `/data/data` | Persistent home directory location. Keep this persistent so Claude sign-in survives restarts. |
| `networkdisks`, `cifsusername`, `cifspassword`, `cifsdomain` | | Standard SMB mount options. |
| `localdisks` | | Standard local disk mount option. |
| `env_vars` | `[]` | Extra environment variables to export into the container. This can override `SELKIES_*` defaults. |
## Custom scripts
The add-on includes the repository standard custom-script executor. On first start, it seeds a `claude_desktop.sh` file in the add-on config directory from the shared template. Commands in that script run during startup, allowing local customization without rebuilding the image.
## Data and cache locations
Persistent state is stored in the configured `data_location`. Claude Desktop stores sign-in data below `~/.config/Claude`, and Claude Code/tool configuration is stored below `~/.claude`. Volatile cache data is redirected to `/tmp/cache` through `$XDG_CACHE_HOME` and `$HOME/.cache`.
## Headroom dashboard
When `install_headroom` is enabled, the add-on starts the local Headroom proxy backend and maps port `8787` by default. Open `http://<home-assistant-host>:8787/dashboard` to view the Headroom live savings dashboard. If you disable the `8787/tcp` port mapping, the backend binds to localhost for MCP use only and the dashboard is not externally exposed.
[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg
[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg
[maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg

118
claude_desktop/SIGN_IN.md Normal file
View File

@@ -0,0 +1,118 @@
# Claude Desktop sign-in — shipped fixes and planned work
Two related sign-in problems when Claude Desktop runs inside the LinuxServer Selkies
streamed desktop.
**Status:**
- **Shipped:** Problem B (keyring persistence) is implemented in v1.4 (Dockerfile + `rootfs/defaults/autostart`).
- **Planned only:** Problem A (in-desktop browser for OAuth) is intentionally not implemented.
The image ships no browser; complete the login with the user-side workaround below.
---
## Problem A — Cannot complete login
Symptoms:
- **"Continue with Google"** does nothing.
- **"Continue with email"** sends a link that "must be opened from the same machine".
### Root cause
Claude Desktop signs in through an **external web page** (Google OAuth or the email magic
link) and then hands the token back to the app via the **`claude://` URL scheme**
(auto-registered on first launch). Both paths need:
1. a **web browser inside the streamed desktop** to render the auth page, and
2. a **default-browser association** so the app's `xdg-open <url>` call goes somewhere.
The Selkies base image ships **no browser** and no default handler, so `xdg-open` has
nothing to dispatch to → Google "does nothing", and the email link's final `claude://…`
redirect only works on the machine running the app (the container), which currently can't
render/handle it.
### Fix (image level)
In `claude_desktop/Dockerfile`, install a browser and register defaults so the whole flow
completes inside the session:
- `apt-get install -y --no-install-recommends chromium` (Debian bookworm package name;
`firefox-esr` is an alternative).
- Set the default browser and confirm the scheme handlers, e.g. as an s6 oneshot or in the
desktop `autostart` (runs as the session user with `$DISPLAY`/dbus available):
```sh
xdg-settings set default-web-browser chromium.desktop
xdg-mime default claude-desktop.desktop x-scheme-handler/claude
```
- Verifying expectations (for reference, not a test step):
`x-scheme-handler/https` → `chromium.desktop`, `x-scheme-handler/claude` →
`claude-desktop.desktop`.
Then login is done entirely in the streamed desktop: **Continue with Google** opens
Chromium in-session and the `claude://` redirect lands back in the app; or paste the email
magic link into the in-session Chromium (not a phone).
### User-side workaround (no rebuild)
- Add-on Configuration → `additional_apps: chromium`, restart (installed by
`rootfs/etc/cont-init.d/80-configuration.sh`).
- Run the two `xdg-settings`/`xdg-mime` commands once in an in-session terminal, or add them
to the custom script `/addon_configs/db21ed7f_claude-desktop/claude-desktop.sh`.
---
## Problem B — "Your sign-in won't be saved on this device"
Symptom:
> Install and unlock a system keyring (such as GNOME Keyring), then restart the app.
### Root cause
Claude Desktop (Electron) persists its auth token with `safeStorage`, which on Linux
encrypts via the **Secret Service API** (`org.freedesktop.secrets`) provided by
gnome-keyring/kwallet. The Selkies base runs only a **system** D-Bus
(`svc-dbus` → `dbus-daemon --system`) and has **no keyring daemon**, so `safeStorage` is
unavailable and the token cannot be stored → the app warns and forgets the session on
restart.
Note: the desktop itself already runs under a **session** bus —
`root/defaults/startwm.sh` launches `dbus-launch --exit-with-session openbox-session`, so
apps started from the openbox `autostart` inherit `DBUS_SESSION_BUS_ADDRESS`. The keyring
must be started **inside that session** so it registers the secrets service on the same bus
Claude Desktop uses. No extra `dbus-launch` is needed.
### Fix (image level)
1. `claude_desktop/Dockerfile`: install
`gnome-keyring libsecret-1-0 dbus-x11` (dbus-x11 provides `dbus-launch`, already used by
startwm; libsecret-1-0 is the backend Electron dlopens).
2. `claude_desktop/rootfs/defaults/autostart`: start & unlock an empty-password keyring
before launching the app, and force the libsecret backend. Proposed content:
```sh
# Start + unlock a gnome-keyring secrets service so Claude Desktop can persist sign-in.
# Runs inside the openbox session's dbus (startwm.sh: dbus-launch --exit-with-session).
eval "$(printf '' | gnome-keyring-daemon --login)" 2>/dev/null || true
eval "$(gnome-keyring-daemon --start --components=secrets)" 2>/dev/null || true
dbus-update-activation-environment --all >/dev/null 2>&1 || true
claude-desktop --no-sandbox --disable-dev-shm-usage --password-store=gnome-libsecret
```
- `--login` reads the password from stdin (empty here), creating and unlocking the login
keyring on first run and starting the daemon in login mode; `--start --components=secrets`
then exposes the Secret Service and exports `GNOME_KEYRING_CONTROL`/`SSH_AUTH_SOCK`.
- `--password-store=gnome-libsecret` forces Electron to use the libsecret backend instead
of falling back to plaintext.
3. Persistence: the keyring DB lives in `$HOME/.local/share/keyrings/` and `HOME=/config/data`
(persistent add-on storage), so the empty-password login keyring survives restarts and is
re-unlocked automatically each boot by the same `autostart` line — the sign-in then sticks.
### User-side workaround (no rebuild)
- Add-on Configuration → `additional_apps: gnome-keyring, libsecret-1-0, dbus-x11`, restart.
- Add the keyring-start lines above to the custom script
`/addon_configs/db21ed7f_claude-desktop/claude-desktop.sh`, and relaunch Claude Desktop
with `--password-store=gnome-libsecret` (e.g. edit the in-session openbox autostart).
---
## Files this plan would touch (when implemented)
- `claude_desktop/Dockerfile` — install `chromium`, `gnome-keyring`, `libsecret-1-0`,
`dbus-x11`; set default browser + scheme handlers.
- `claude_desktop/rootfs/defaults/autostart` — keyring bootstrap + `--password-store` flag.
- `claude_desktop/CHANGELOG.md` / `config.yaml` version bump.
## Open questions before implementing
- Bundle **chromium** vs **firefox-esr** as the in-image browser?
- Empty-password keyring (fully unattended) vs. reuse the add-on `PASSWORD` option to lock
the keyring with the same password?

View File

@@ -0,0 +1,72 @@
#include <tunables/global>
profile claude_desktop_addon flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
capability chown,
capability dac_override,
capability dac_read_search,
capability fowner,
capability setgid,
capability setuid,
capability sys_chroot,
capability sys_admin,
file,
signal,
mount,
umount,
remount,
network udp,
network tcp,
network dgram,
network stream,
network inet,
network inet6,
network netlink raw,
network unix dgram,
# S6-Overlay
/init ix,
/run/{s6,s6-rc*,service}/** ix,
/package/** ix,
/command/** ix,
/run/{,**} rwk,
/dev/tty rw,
/bin/** ix,
/usr/bin/** ix,
/usr/lib/bashio/** ix,
/etc/s6/** rix,
/run/s6/** rix,
/etc/services.d/** rwix,
/etc/cont-init.d/** rwix,
/etc/cont-finish.d/** rwix,
/init rix,
/var/run/** mrwkl,
/var/run/ mrwkl,
/dev/i2c-1 mrwkl,
# Files required
/dev/fuse mrwkl,
/dev/sda1 mrwkl,
/dev/sdb1 mrwkl,
/dev/nvme0 mrwkl,
/dev/nvme0n1 mrwkl,
/dev/nvme1 mrwkl,
/dev/mmcblk0p1 mrwkl,
/dev/* mrwkl,
/udev/* mrwkl,
/tmp/** mrkwl,
/dev/fuse/** mrkwl,
/dev/** mrkwl,
/sys/firmware/** mrkwl,
# Data access
/data/** rw,
# suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
ptrace (trace,read) peer=docker-default,
# docker daemon confinement requires explict allow rule for signal
signal (receive) set=(kill,term) peer=/usr/bin/docker,
}

View File

@@ -0,0 +1,6 @@
{
"build_from": {
"aarch64": "ghcr.io/linuxserver/baseimage-selkies:arm64v8-debianbookworm",
"amd64": "ghcr.io/linuxserver/baseimage-selkies:amd64-debianbookworm"
}
}

View File

@@ -0,0 +1,94 @@
arch:
- aarch64
- amd64
audio: true
description: Claude Desktop streamed through a browser with LinuxServer Selkies
devices:
- /dev/dri
- /dev/dri/card0
- /dev/dri/card1
- /dev/dri/renderD128
- /dev/fuse
environment:
AUTO_GPU: "1"
FM_HOME: /data/data
HOME: /data/data
PGID: "0"
PUID: "0"
SELKIES_FRAMERATE: "30"
START_DOCKER: "false"
TITLE: Claude Desktop
image: ghcr.io/alexbelgium/claude_desktop-{arch}
ingress: true
init: false
hassio_api: true
hassio_role: manager
homeassistant_api: true
auth_api: true
map:
- addon_config:rw
- share:rw
- media:rw
- ssl
name: Claude Desktop
options:
env_vars: []
DNS_server: 8.8.8.8
data_location: /data/data
PGID: 0
PUID: 0
additional_apps: ""
additional_pip: ""
auto_update: true
github_email: ""
ha_smart_context: true
enable_ha_mcp: true
dangerously_skip_permissions: false
github_token: ""
github_username: ""
install_caveman: true
install_github_cli: true
install_headroom: true
install_rtk: true
panel_admin: false
panel_icon: mdi:robot-happy
ports:
3001/tcp: null
8787/tcp: 8787
ports_description:
3001/tcp: Web interface https
8787/tcp: Headroom dashboard and proxy
privileged:
- SYS_ADMIN
- DAC_READ_SEARCH
schema:
env_vars:
- name: match(^[A-Za-z0-9_]+$)
value: str?
DNS_server: str?
data_location: str?
DRINODE: list(/dev/dri/card0|/dev/dri/card1|/dev/dri/card2|/dev/dri/renderD128|/dev/dri/renderD129|)?
KEYBOARD: list(da-dk-qwerty|de-de-qwertz|en-gb-qwerty|en-us-qwerty|es-es-qwerty|fr-ch-qwertz|fr-fr-azerty|it-it-qwerty|ja-jp-qwerty|pt-br-qwerty|sv-se-qwerty|tr-tr-qwerty)?
PASSWORD: str?
PGID: int
PUID: int
TZ: match([A-Z][a-z]*./[A-Z][a-z]*.)?
additional_apps: str?
additional_pip: str?
auto_update: bool?
github_email: str?
ha_smart_context: bool?
enable_ha_mcp: bool?
dangerously_skip_permissions: bool?
github_token: password?
github_username: str?
install_caveman: bool
install_github_cli: bool
install_headroom: bool
install_rtk: bool
slug: claude_desktop
tmpfs: true
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "1.8"
video: true

BIN
claude_desktop/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
claude_desktop/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env sh
# Persist Claude Desktop sign-in across restarts.
# Claude Desktop (Electron) stores its auth token via safeStorage, which on Linux needs the
# Secret Service API (org.freedesktop.secrets). Provide it with gnome-keyring on the openbox
# session bus (startwm.sh runs `dbus-launch --exit-with-session`), unlocked with an empty
# password so it comes back automatically after a restart. The keyring DB lives under
# $HOME/.local/share/keyrings, and HOME=/data/data is persistent add-on storage, so the
# saved sign-in survives add-on restarts.
eval "$(gnome-keyring-daemon --start --components=secrets 2>/dev/null)" || true
printf '' | gnome-keyring-daemon --unlock 2>/dev/null || true
export GNOME_KEYRING_CONTROL SSH_AUTH_SOCK
dbus-update-activation-environment --all >/dev/null 2>&1 || true
# --password-store=gnome-libsecret forces Electron onto the libsecret backend instead of
# falling back to plaintext (and warning that the sign-in will not be saved).
CLAUDE_DESKTOP_COMMAND_FILE="/tmp/claude-desktop-command"
DEFAULT_CLAUDE_DESKTOP_COMMAND="claude-desktop --no-sandbox --disable-dev-shm-usage --password-store=gnome-libsecret"
if [ -s "$CLAUDE_DESKTOP_COMMAND_FILE" ]; then
CLAUDE_DESKTOP_COMMAND="$(cat "$CLAUDE_DESKTOP_COMMAND_FILE")"
else
CLAUDE_DESKTOP_COMMAND="$DEFAULT_CLAUDE_DESKTOP_COMMAND"
fi
if bashio::config.true 'install_headroom'; then
export ANTHROPIC_BASE_URL="http://127.0.0.1:8787"
fi
# Launch the configured command. If a custom/wrapped command fails to start, fall back to
# the plain Claude Desktop launch so the desktop app still comes up for the user.
if ! sh -c "$CLAUDE_DESKTOP_COMMAND"; then
if [ "$CLAUDE_DESKTOP_COMMAND" != "$DEFAULT_CLAUDE_DESKTOP_COMMAND" ]; then
echo "autostart: '$CLAUDE_DESKTOP_COMMAND' failed; falling back to default Claude Desktop launch" >&2
exec sh -c "$DEFAULT_CLAUDE_DESKTOP_COMMAND"
fi
fi

View File

@@ -0,0 +1,4 @@
# Hourly rtk + headroom token-savings report to the add-on log (heartbeat + gains).
# Seeded to /data/data/crontabs/root by init-crontab-config and run by svc-cron; edit the
# persistent copy to customize. Output goes to /proc/1/fd/1 so it shows in the add-on log.
0 * * * * /usr/local/bin/claude-gains-report.sh > /proc/1/fd/1 2>&1

View File

@@ -0,0 +1,107 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
# shellcheck disable=SC2046
set -e
# Define user
PUID=$(bashio::config "PUID")
PGID=$(bashio::config "PGID")
# Check data location
LOCATION="$(bashio::config 'data_location')"
if [[ "$LOCATION" = "null" || -z "$LOCATION" ]]; then
LOCATION="/data/data"
else
LOCATIONOK=""
for location in "/share" "/config" "/data" "/mnt"; do
if [[ "$LOCATION" == "$location"* ]]; then
LOCATIONOK=true
fi
done
if [ -z "$LOCATIONOK" ]; then
LOCATION="/data/data"
bashio::log.fatal "Your data_location value can only be set in /share, /config, /data or /mnt. It will be reset to the default location : $LOCATION"
fi
fi
bashio::log.info "Setting data location to $LOCATION"
# LinuxServer Selkies services expect the s6 envdir at /run/s6/container_environment.
# Home Assistant add-on startup can run without s6-overlay PID1, so create the envdir
# ourselves before those services start and before scripts write required variables.
S6_ENVDIR="/run/s6/container_environment"
mkdir -p "$S6_ENVDIR"
chmod 755 /run/s6 "$S6_ENVDIR"
XDG_RUNTIME_DIR="/run/user/$PUID"
mkdir -p "$XDG_RUNTIME_DIR"
chmod 700 "$XDG_RUNTIME_DIR"
for file in /etc/s6-overlay/s6-rc.d/*/run; do
if [ "$(sed -n '1{/bash/p};q' "$file")" ] && ! grep -q '^export XDG_CACHE_HOME=/tmp/cache$' "$file"; then
sed -i "1a export HOME=$LOCATION" "$file"
sed -i "1a export FM_HOME=$LOCATION" "$file"
sed -i "1a export XDG_CACHE_HOME=/tmp/cache" "$file"
fi
done
for folders in /defaults /etc/cont-init.d /etc/services.d /etc/s6-overlay/s6-rc.d; do
if [ -d "$folders" ]; then
find "$folders" -type f -exec sed -i "s|/data/data|$LOCATION|g" {} + &> /dev/null || true
fi
done
sed -i "s|^\(abc:[^:]*:[^:]*:[^:]*:[^:]*:\)[^:]*|\1$LOCATION|" /etc/passwd
printf "%s" "$LOCATION" > "$S6_ENVDIR/HOME"
printf "%s" "$LOCATION" > "$S6_ENVDIR/FM_HOME"
printf "%s" "/tmp/cache" > "$S6_ENVDIR/XDG_CACHE_HOME"
printf "%s" "$XDG_RUNTIME_DIR" > "$S6_ENVDIR/XDG_RUNTIME_DIR"
grep -qxF "export HOME=\"$LOCATION\"" ~/.bashrc 2>/dev/null || {
printf "%s\n" "export HOME=\"$LOCATION\""
printf "%s\n" "export FM_HOME=\"$LOCATION\""
printf "%s\n" "export XDG_CACHE_HOME=\"/tmp/cache\""
} >> ~/.bashrc
bashio::log.info "Creating $LOCATION"
mkdir -p "$LOCATION" /tmp/cache "$XDG_RUNTIME_DIR"
chmod 755 /tmp/cache
chmod 700 "$XDG_RUNTIME_DIR"
# Pre-create the Selkies joystick log so the base image's "chmod 777 /tmp/selkies*"
# calls (in init-selkies-config and svc-de) never fail on an empty glob.
touch /tmp/selkies_js.log
chmod 777 /tmp/selkies_js.log
if [ -e "$LOCATION/.cache" ] && [ ! -L "$LOCATION/.cache" ]; then
rm -rf "$LOCATION/.cache"
fi
ln -sfn /tmp/cache "$LOCATION/.cache"
bashio::log.info "Setting ownership to $PUID:$PGID"
chown -R "$PUID":"$PGID" "$LOCATION" /tmp/cache "$XDG_RUNTIME_DIR"
chmod -R 700 "$LOCATION"
# The base init-selkies-config script overrides XDG_RUNTIME_DIR to $HOME/.XDG, which lands
# on persistent storage and conflicts with the tmpfs runtime dir set above. Re-assert the
# tmpfs value at the end of that oneshot so the app and desktop agree on one valid dir.
SELKIES_CONFIG_RUN="/etc/s6-overlay/s6-rc.d/init-selkies-config/run"
if [ -f "$SELKIES_CONFIG_RUN" ]; then
sed -i '/^# XDG_RUNTIME_DIR override reconciled$/,+1d' "$SELKIES_CONFIG_RUN"
printf '\n# XDG_RUNTIME_DIR override reconciled\nprintf "%%s" "%s" > /run/s6/container_environment/XDG_RUNTIME_DIR\n' "$XDG_RUNTIME_DIR" >> "$SELKIES_CONFIG_RUN"
fi
# The Selkies desktop init oneshots do best-effort device/permission setup (mknod
# /dev/input/*, chmod /tmp/selkies*, /dev/dri perms) that is only partially permitted in the
# HA add-on sandbox. A non-zero exit from a oneshot fails add-on bringup and crash-loops the
# container, so make these two tolerant and always report success. Longruns (svc-*) are left
# untouched so s6 keeps supervising them with their real exit codes.
for oneshot in init-video init-selkies-config; do
run="/etc/s6-overlay/s6-rc.d/$oneshot/run"
if [ -f "$run" ] && ! grep -q '^set +e$' "$run"; then
sed -i "1a set +e" "$run"
printf '\nexit 0\n' >> "$run"
fi
done

View File

@@ -0,0 +1,54 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
# shellcheck disable=SC2015
set -e
if bashio::config.has_value 'additional_apps'; then
bashio::log.info "Installing additional apps :"
NEWAPPS=$(bashio::config 'additional_apps')
if command -v "apt-get" &> /dev/null; then
apt-get update -o Acquire::http::Timeout=10 -o Acquire::https::Timeout=10 &> /dev/null || bashio::log.warning "Unable to update apt package lists"
fi
for packagestoinstall in ${NEWAPPS//,/ }; do
bashio::log.green "... $packagestoinstall"
if command -v "apk" &> /dev/null; then
apk add --no-cache "$packagestoinstall" &> /dev/null || (bashio::log.fatal "Error : $packagestoinstall not found")
elif command -v "apt-get" &> /dev/null; then
apt-get install -yqq --no-install-recommends "$packagestoinstall" &> /dev/null || (bashio::log.fatal "Error : $packagestoinstall not found")
elif command -v "pacman" &> /dev/null; then
pacman --noconfirm -S "$packagestoinstall" &> /dev/null || (bashio::log.fatal "Error : $packagestoinstall not found")
fi
done
fi
if bashio::config.has_value 'additional_pip'; then
for p in $(bashio::config 'additional_pip' | tr ',' ' '); do
bashio::log.green "... pip: $p"
pip3 install --break-system-packages "$p" || bashio::log.fatal "Error: pip package $p failed"
done
fi
if bashio::config.has_value 'TZ'; then
TIMEZONE=$(bashio::config 'TZ')
bashio::log.info "Setting timezone to $TIMEZONE"
ln -snf /usr/share/zoneinfo/"$TIMEZONE" /etc/localtime
echo "$TIMEZONE" > /etc/timezone
fi || (bashio::log.fatal "Error : $TIMEZONE not found. Here is a list of valid timezones : https://manpages.ubuntu.com/manpages/focal/man3/DateTime::TimeZone::Catalog.3pm.html")
if bashio::config.has_value 'KEYBOARD'; then
KEYBOARD=$(bashio::config 'KEYBOARD')
bashio::log.info "Setting keyboard to $KEYBOARD"
if [ -d /var/run/s6/container_environment ]; then printf "%s" "$KEYBOARD" > /var/run/s6/container_environment/KEYBOARD; fi
grep -qxF "KEYBOARD=\"$KEYBOARD\"" ~/.bashrc 2>/dev/null || printf "%s\n" "KEYBOARD=\"$KEYBOARD\"" >> ~/.bashrc
fi || true
if bashio::config.has_value 'PASSWORD'; then
bashio::log.info "Setting password to the value defined in options"
PASSWORD=$(bashio::config 'PASSWORD')
passwd -d abc
echo -e "$PASSWORD\n$PASSWORD" | passwd abc
elif ! bashio::config.has_value 'PASSWORD' && { [[ -n "$(bashio::addon.port "3000")" ]] || [[ -n "$(bashio::addon.port "3001")" ]] }; then
bashio::log.warning "SEVERE RISK IDENTIFIED"
bashio::log.warning "You are opening an external port but your password is not defined"
bashio::log.warning "You risk being hacked ! Please disable the external ports, or use a password"
fi

View File

@@ -0,0 +1,12 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
if bashio::config.true 'auto_update'; then
bashio::log.info "Checking for Claude Desktop updates..."
if apt-get update -o Acquire::http::Timeout=10 -o Acquire::https::Timeout=10 &> /dev/null && apt-get install -y --only-upgrade claude-desktop &> /dev/null; then
bashio::log.info "Claude Desktop version: $(dpkg-query -W -f='${Version}' claude-desktop)"
else
bashio::log.warning "Update check failed (offline?), keeping current version"
fi
fi

View File

@@ -0,0 +1,172 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
set -o pipefail
mkdir -p "$HOME/.claude"
CLAUDE_DESKTOP_COMMAND_FILE="/tmp/claude-desktop-command"
DEFAULT_CLAUDE_DESKTOP_COMMAND='claude-desktop --no-sandbox --disable-dev-shm-usage --password-store=gnome-libsecret'
printf '%s\n' "$DEFAULT_CLAUDE_DESKTOP_COMMAND" > "$CLAUDE_DESKTOP_COMMAND_FILE"
# headroom's "wrap"/proxy routing works by setting ANTHROPIC_BASE_URL, which the Claude Desktop
# Electron app force-overrides to the production endpoint (headroom #869), so transparent
# compression cannot be applied to the desktop launch. The integration that does work with
# Claude Desktop is headroom's MCP server, which exposes the headroom_compress/headroom_retrieve/
# headroom_stats tools inside the app. Register it in Claude Desktop's MCP config, leaving the
# plain launch untouched. The merge is idempotent and preserves any other MCP servers.
CLAUDE_DESKTOP_CONFIG="$HOME/.config/Claude/claude_desktop_config.json"
if bashio::config.true 'install_headroom'; then
if command -v headroom &> /dev/null; then
bashio::log.info "headroom $(headroom --version 2> /dev/null || true) available; registering the headroom MCP server for Claude Desktop"
HEADROOM_BIN="$(command -v headroom)" CLAUDE_DESKTOP_CONFIG="$CLAUDE_DESKTOP_CONFIG" python3 - <<'PY' || bashio::log.warning "Unable to register the headroom MCP server automatically"
import json
import os
from pathlib import Path
path = Path(os.environ["CLAUDE_DESKTOP_CONFIG"])
try:
data = json.loads(path.read_text()) if path.exists() else {}
if not isinstance(data, dict):
data = {}
except Exception:
if path.exists():
path.rename(path.with_suffix(path.suffix + ".bak"))
data = {}
servers = data.get("mcpServers")
if not isinstance(servers, dict):
servers = {}
data["mcpServers"] = servers
servers["headroom"] = {"command": os.environ.get("HEADROOM_BIN", "headroom"), "args": ["mcp", "serve"]}
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(data, indent=2) + "\n")
PY
else
bashio::log.warning "headroom is not available"
fi
elif [ -f "$CLAUDE_DESKTOP_CONFIG" ]; then
bashio::log.info "Removing the headroom MCP server from Claude Desktop"
CLAUDE_DESKTOP_CONFIG="$CLAUDE_DESKTOP_CONFIG" python3 - <<'PY' || bashio::log.warning "Unable to remove the headroom MCP server automatically"
import json
import os
from pathlib import Path
path = Path(os.environ["CLAUDE_DESKTOP_CONFIG"])
data = json.loads(path.read_text())
if isinstance(data, dict):
servers = data.get("mcpServers")
if isinstance(servers, dict) and servers.pop("headroom", None) is not None:
if not servers:
data.pop("mcpServers", None)
path.write_text(json.dumps(data, indent=2) + "\n")
PY
fi
# Guide Claude to actually use the headroom compression tools so the MCP integration produces
# real savings (otherwise the tools sit unused and `headroom savings` stays empty). Managed,
# idempotent block appended to the user's global CLAUDE.md; removed when headroom is disabled.
CLAUDE_MD="$HOME/.claude/CLAUDE.md"
HEADROOM_GUIDE_BEGIN="<!-- BEGIN headroom (managed by claude_desktop addon) -->"
if bashio::config.true 'install_headroom'; then
mkdir -p "$(dirname "$CLAUDE_MD")"
if ! { [ -f "$CLAUDE_MD" ] && grep -qF "$HEADROOM_GUIDE_BEGIN" "$CLAUDE_MD"; }; then
bashio::log.info "Adding headroom usage guidance to CLAUDE.md"
{
[ -s "$CLAUDE_MD" ] && printf '\n'
cat <<'MD'
<!-- BEGIN headroom (managed by claude_desktop addon) -->
## Headroom context compression
A local Headroom proxy (127.0.0.1:8787) backs the `headroom` MCP tools. To save context tokens:
when you produce or read a **large, structured** blob you will keep referring to — file listings,
search results, JSON/config dumps, big command outputs, roughly >500 tokens — call
`mcp__headroom__headroom_compress` on it and keep the returned compressed text + `hash` instead of
the raw content. Call `mcp__headroom__headroom_retrieve` with that hash when you need the full
original back. Skip compression for error/stack-trace output (Headroom deliberately protects it)
and for small or one-off content. Use `mcp__headroom__headroom_stats` to check savings.
<!-- END headroom (managed by claude_desktop addon) -->
MD
} >> "$CLAUDE_MD"
fi
elif [ -f "$CLAUDE_MD" ] && grep -qF "$HEADROOM_GUIDE_BEGIN" "$CLAUDE_MD"; then
bashio::log.info "Removing headroom usage guidance from CLAUDE.md"
CLAUDE_MD="$CLAUDE_MD" python3 - <<'PY' || bashio::log.warning "Unable to remove headroom guidance automatically"
import os
import re
from pathlib import Path
path = Path(os.environ["CLAUDE_MD"])
text = path.read_text()
pattern = re.compile(
r"\n*<!-- BEGIN headroom \(managed by claude_desktop addon\) -->.*?"
r"<!-- END headroom \(managed by claude_desktop addon\) -->\n?",
re.DOTALL,
)
new = pattern.sub("", text)
if new != text:
path.write_text(new)
PY
fi
if bashio::config.true 'install_rtk'; then
if command -v rtk &> /dev/null; then
if [ -f "$HOME/.claude/settings.json" ] && grep -q 'rtk hook claude' "$HOME/.claude/settings.json"; then
bashio::log.info "rtk Claude Code hook already configured"
else
bashio::log.info "Configuring rtk Claude Code hook"
RTK_NONINTERACTIVE=1 rtk init -g || bashio::log.warning "rtk global files configuration failed"
python3 - <<'PY' || bashio::log.warning "Unable to configure rtk hook automatically"
import json
from pathlib import Path
path = Path.home() / ".claude" / "settings.json"
try:
data = json.loads(path.read_text()) if path.exists() else {}
if not isinstance(data, dict):
data = {}
except Exception:
if path.exists():
path.rename(path.with_suffix(path.suffix + ".bak"))
data = {}
hooks = data.setdefault("hooks", {})
pre = hooks.setdefault("PreToolUse", [])
rtk_entry = {"matcher": "Bash", "hooks": [{"type": "command", "command": "rtk hook claude"}]}
if not any("rtk hook claude" in json.dumps(entry) for entry in pre if isinstance(entry, dict)):
pre.append(rtk_entry)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(data, indent=2) + "\n")
PY
fi
else
bashio::log.warning "rtk is not available"
fi
elif [ -f "$HOME/.claude/settings.json" ] && grep -q 'rtk' "$HOME/.claude/settings.json"; then
bashio::log.info "Removing rtk Claude Code hook"
python3 - <<'PY' || bashio::log.warning "Unable to remove rtk hook automatically"
import json
from pathlib import Path
path = Path.home() / ".claude" / "settings.json"
data = json.loads(path.read_text())
hooks = data.get("hooks", {})
for event, entries in list(hooks.items()):
if isinstance(entries, list):
hooks[event] = [entry for entry in entries if "rtk" not in json.dumps(entry)]
if not hooks[event]:
hooks.pop(event, None)
if hooks:
data["hooks"] = hooks
else:
data.pop("hooks", None)
path.write_text(json.dumps(data, indent=2) + "\n")
PY
fi
if bashio::config.true 'install_caveman'; then
if [ -d "$HOME/.claude/plugins/caveman" ] || find "$HOME/.claude" -maxdepth 4 -iname '*caveman*' -print -quit | grep -q .; then
bashio::log.info "caveman Claude Code plugin already configured"
else
bashio::log.info "Installing caveman Claude Code plugin"
curl --connect-timeout 10 --max-time 60 -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.sh | bash >/dev/null || bashio::log.warning "caveman install failed (offline?)"
fi
else
bashio::log.info "Disabling caveman Claude Code plugin"
find "$HOME/.claude" -maxdepth 4 -iname '*caveman*' -exec rm -rf {} + 2> /dev/null || true
fi

View File

@@ -0,0 +1,41 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
set -o pipefail
if ! bashio::config.true 'install_github_cli'; then
bashio::log.info "GitHub CLI setup disabled"
exit 0
fi
if ! command -v git > /dev/null 2>&1; then
bashio::log.warning "git is not available"
fi
if ! command -v gh > /dev/null 2>&1; then
bashio::log.warning "gh is not available"
exit 0
fi
if bashio::config.has_value 'github_username'; then
git config --global user.name "$(bashio::config 'github_username')"
fi
if bashio::config.has_value 'github_email'; then
git config --global user.email "$(bashio::config 'github_email')"
fi
if bashio::config.has_value 'github_token'; then
token="$(bashio::config 'github_token')"
mkdir -p "$HOME/.config/gh"
chmod 700 "$HOME/.config/gh"
if env -u GH_TOKEN -u GITHUB_TOKEN gh auth status --hostname github.com > /dev/null 2>&1; then
bashio::log.info "GitHub CLI already authenticated for github.com"
else
bashio::log.info "Configuring GitHub CLI authentication for github.com"
printf '%s\n' "$token" | env -u GH_TOKEN -u GITHUB_TOKEN gh auth login --hostname github.com --with-token || bashio::log.warning "GitHub CLI authentication failed"
fi
env -u GH_TOKEN -u GITHUB_TOKEN gh auth setup-git --hostname github.com || bashio::log.warning "GitHub CLI git credential setup failed"
else
bashio::log.info "GitHub CLI available. Set github_token to authenticate gh and git operations."
fi

View File

@@ -0,0 +1,37 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
NGINX_CONFIG=/etc/nginx/sites-available/ingress.conf
SUBFOLDER="$(bashio::addon.ingress_entry)"
# Ensure subfolder ends with a trailing slash (except for root)
if [[ -n "${SUBFOLDER}" && "${SUBFOLDER}" != "/" ]]; then
[[ "${SUBFOLDER}" == */ ]] || SUBFOLDER="${SUBFOLDER}/"
else
SUBFOLDER="/"
fi
cp /defaults/default.conf "${NGINX_CONFIG}"
# Keep only the first (non-SSL) server block
awk -v n=2 '/^[[:space:]]*server[[:space:]]*\{/{n--} n>0' "${NGINX_CONFIG}" > tmpfile
mv tmpfile "${NGINX_CONFIG}"
# Disable IPv6 listeners for ingress proxying
sed -i '/listen \[::\]/d' "${NGINX_CONFIG}"
# Adapt ports and upstream paths for Home Assistant ingress
sed -i "s|3000|$(bashio::addon.ingress_port)|g" "${NGINX_CONFIG}"
sed -i "s|SUBFOLDER|/|g" "${NGINX_CONFIG}"
sed -i "s|CWS|8082|g" "${NGINX_CONFIG}"
sed -i "s|REPLACE_HOME|${HOME:-/root}|g" "${NGINX_CONFIG}"
sed -i "s|REPLACE_DOWNLOADS_PATH|${HOME:-/config}|g" "${NGINX_CONFIG}"
sed -i '/proxy_buffering/a proxy_set_header Accept-Encoding "";' "${NGINX_CONFIG}"
sed -i '/proxy_buffering/a sub_filter_once off;' "${NGINX_CONFIG}"
sed -i '/proxy_buffering/a sub_filter_types *;' "${NGINX_CONFIG}"
sed -i '/proxy_buffering/a sub_filter "vnc/index.html?autoconnect" "vnc/index.html?path=%%path%%/websockify?autoconnect";' "${NGINX_CONFIG}"
sed -i "s|%%path%%|${SUBFOLDER:1}|g" "${NGINX_CONFIG}"
# Avoid content encoding on proxied responses to keep Selkies happy (handled by proxy_set_header Accept-Encoding insertion above)
cp "${NGINX_CONFIG}" /etc/nginx/sites-enabled

View File

@@ -0,0 +1,96 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

View File

@@ -0,0 +1,15 @@
proxy_http_version 1.1;
proxy_ignore_client_abort off;
proxy_read_timeout 86400s;
proxy_redirect off;
proxy_send_timeout 86400s;
proxy_max_temp_file_size 0;
proxy_set_header Accept-Encoding "";
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;

View File

@@ -0,0 +1 @@
resolver 127.0.0.11 ipv6=off;

View File

@@ -0,0 +1,6 @@
root /dev/null;
server_name $hostname;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;

View File

@@ -0,0 +1,9 @@
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;

View File

@@ -0,0 +1,3 @@
upstream backend {
server 127.0.0.1:8080;
}

View File

@@ -0,0 +1,25 @@
#!/usr/bin/with-contenv bashio
# Headroom optimization proxy — runs ONLY as a local backend for the headroom MCP tools
# (headroom_compress / headroom_retrieve, registered in 82-claude_tools.sh). It is NOT a
# traffic router: no client's ANTHROPIC_BASE_URL is pointed at it, so the Claude Desktop
# Electron app (which force-overrides ANTHROPIC_BASE_URL — headroom #869) and every other
# client are unaffected. Without this backend the MCP tools cannot store/retrieve compressed
# content and no savings are ever recorded.
declare port=8787
declare host=127.0.0.1
# Expose the built-in Headroom dashboard only when the add-on option is enabled
# and Home Assistant has mapped the Headroom port. The port is mapped by default
# in config.yaml, so users can open http://<ha-host>:8787/dashboard to inspect
# live savings, while disabling install_headroom still leaves the service idle.
if [[ -n "$(bashio::addon.port "${port}")" ]]; then
host=0.0.0.0
fi
if bashio::config.true 'install_headroom' && command -v headroom >/dev/null 2>&1; then
bashio::log.info "svc-headroom: starting headroom proxy on ${host}:${port}; dashboard available at /dashboard when the port is mapped (MCP backend only; no client routing)"
exec headroom proxy --host "${host}" --port "${port}" --code-aware
fi
bashio::log.info "svc-headroom: install_headroom disabled or headroom not found; idling"
exec sleep infinity

View File

@@ -0,0 +1 @@
longrun

View File

@@ -0,0 +1,34 @@
#!/usr/bin/with-contenv bashio
# Hourly rtk + headroom token-savings snapshot for the add-on log.
# Invoked by cron (see /defaults/crontabs/root); its stdout is redirected to /proc/1/fd/1,
# so the report appears in the add-on log. Doubles as a heartbeat: if the numbers stop
# growing, the corresponding tool has stopped working.
# with-contenv supplies HOME from the s6 envdir, so this honors a custom `data_location`
# (see 20-folders.sh) instead of hardcoding /data/data; it also makes bashio::config
# available for the install_headroom gate below.
export NO_COLOR=1 # keep the add-on log free of ANSI color codes
export PATH="/lsiopy/bin:/usr/local/bin:/usr/bin:/bin:${PATH}"
have_rtk=false; command -v rtk >/dev/null 2>&1 && have_rtk=true
have_headroom=false; command -v headroom >/dev/null 2>&1 && have_headroom=true
# headroom is pip-installed unconditionally at build time, so its binary is on PATH even
# when install_headroom is off — gate on the same config svc-headroom checks, and only
# fall back to have_headroom as a secondary availability guard.
headroom_enabled=false
if bashio::config.true 'install_headroom' && $have_headroom; then
headroom_enabled=true
fi
# Nothing to report if neither tool is active — stay quiet.
if ! $have_rtk && ! $headroom_enabled; then exit 0; fi
echo "===== claude gains report $(date '+%Y-%m-%d %H:%M:%S') ====="
if $headroom_enabled; then
echo "--- headroom savings ---"
headroom savings 2>&1 || echo "[warn] headroom savings failed"
elif $have_rtk; then
echo "--- rtk gain ---"
rtk gain 2>&1 || echo "[warn] rtk gain failed"
fi
echo "===== end gains report ====="

View File

@@ -0,0 +1,7 @@
{
"source": "github",
"upstream_repo": "linuxserver/docker-baseimage-selkies",
"github_fulltag": true,
"slug": "claude_desktop",
"paused": false
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,4 +1,7 @@
## 2.63.18 (2026-07-04)
- Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases)
## 2.63.17 (2026-06-29)
- Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases)

View File

@@ -124,4 +124,4 @@ schema:
slug: filebrowser
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "2.63.17"
version: "2.63.18"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,10 +1,10 @@
{
"github_beta": "true",
"last_update": "2026-06-29",
"last_update": "2026-07-04",
"paused": false,
"repository": "alexbelgium/hassio-addons",
"slug": "filebrowser",
"source": "github",
"upstream_repo": "filebrowser/filebrowser",
"upstream_version": "2.63.17"
"upstream_version": "2.63.18"
}

Some files were not shown because too many files have changed in this diff Show More