From 828efe06e9243e559f75a611fb1859184dbaf874 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:05:54 +0100 Subject: [PATCH 1/3] feat!: move edge & proxy to new builder --- .devcontainer.json | 33 +++++ .github/{dependabot.yml => dependabot.yaml} | 4 +- .github/workflows/builder.yaml | 121 ++++++++++++++++++ .github/workflows/bump-version.yaml | 73 +++++++++++ .github/workflows/lint.yaml | 44 +++++++ .github/workflows/manual-builder.yaml | 69 ++++++++++ .gitignore | 4 - .vscode/tasks.json | 62 +++++++++ repository.json | 5 - repository.yaml | 4 + zigbee2mqtt-edge/CHANGELOG.md | 6 +- zigbee2mqtt-edge/DOCS.md | 12 ++ zigbee2mqtt-edge/Dockerfile | 37 ++++++ zigbee2mqtt-edge/README.md | 5 +- zigbee2mqtt-edge/build.yaml | 9 ++ zigbee2mqtt-edge/config.json | 87 ------------- zigbee2mqtt-edge/config.yaml | 50 ++++++++ .../s6-rc.d/user/contents.d/zigbee2mqtt | 0 .../contents.d/zigbee2mqtt-migrate-config | 0 .../dependencies.d/base | 0 .../s6-rc.d/zigbee2mqtt-migrate-config/type | 1 + .../s6-rc.d/zigbee2mqtt-migrate-config/up | 1 + .../dependencies.d/zigbee2mqtt-migrate-config | 0 .../etc/s6-overlay/s6-rc.d/zigbee2mqtt/finish | 28 ++++ .../etc/s6-overlay/s6-rc.d/zigbee2mqtt/run | 93 ++++++++++++++ .../etc/s6-overlay/s6-rc.d/zigbee2mqtt/type | 1 + .../scripts/zigbee2mqtt-migrate-config.sh | 94 ++++++++++++++ zigbee2mqtt-edge/translations/en.yaml | 44 +++++++ zigbee2mqtt-proxy/CHANGELOG.md | 5 + zigbee2mqtt-proxy/DOCS.md | 5 + zigbee2mqtt-proxy/Dockerfile | 26 +--- zigbee2mqtt-proxy/README.md | 12 +- zigbee2mqtt-proxy/build.yaml | 9 ++ zigbee2mqtt-proxy/config.json | 35 ----- zigbee2mqtt-proxy/config.yaml | 26 ++++ zigbee2mqtt-proxy/entrypoint.sh | 6 - zigbee2mqtt-proxy/rootfs/etc/nginx/mime.types | 99 ++++++++++++++ .../etc/nginx/nginx.gtpl} | 53 ++++---- .../s6-rc.d/nginx/dependencies.d/base | 0 .../etc/s6-overlay/s6-rc.d/nginx/finish | 28 ++++ .../rootfs/etc/s6-overlay/s6-rc.d/nginx/run | 21 +++ .../rootfs/etc/s6-overlay/s6-rc.d/nginx/type | 1 + .../s6-overlay/s6-rc.d/user/contents.d/nginx | 0 zigbee2mqtt-proxy/translations/en.yaml | 8 ++ 44 files changed, 1028 insertions(+), 193 deletions(-) create mode 100644 .devcontainer.json rename .github/{dependabot.yml => dependabot.yaml} (66%) create mode 100644 .github/workflows/builder.yaml create mode 100644 .github/workflows/bump-version.yaml create mode 100644 .github/workflows/lint.yaml create mode 100644 .github/workflows/manual-builder.yaml create mode 100644 .vscode/tasks.json delete mode 100644 repository.json create mode 100644 repository.yaml create mode 100644 zigbee2mqtt-edge/DOCS.md create mode 100644 zigbee2mqtt-edge/Dockerfile create mode 100644 zigbee2mqtt-edge/build.yaml delete mode 100644 zigbee2mqtt-edge/config.json create mode 100644 zigbee2mqtt-edge/config.yaml create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/zigbee2mqtt create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/zigbee2mqtt-migrate-config create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/dependencies.d/base create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/type create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/up create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/dependencies.d/zigbee2mqtt-migrate-config create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/finish create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/run create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/type create mode 100644 zigbee2mqtt-edge/rootfs/etc/s6-overlay/scripts/zigbee2mqtt-migrate-config.sh create mode 100644 zigbee2mqtt-edge/translations/en.yaml create mode 100644 zigbee2mqtt-proxy/CHANGELOG.md create mode 100644 zigbee2mqtt-proxy/DOCS.md create mode 100644 zigbee2mqtt-proxy/build.yaml delete mode 100644 zigbee2mqtt-proxy/config.json create mode 100644 zigbee2mqtt-proxy/config.yaml delete mode 100755 zigbee2mqtt-proxy/entrypoint.sh create mode 100644 zigbee2mqtt-proxy/rootfs/etc/nginx/mime.types rename zigbee2mqtt-proxy/{nginx.conf.gtpl => rootfs/etc/nginx/nginx.gtpl} (67%) create mode 100644 zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/dependencies.d/base create mode 100644 zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish create mode 100644 zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/run create mode 100644 zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/type create mode 100644 zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/nginx create mode 100644 zigbee2mqtt-proxy/translations/en.yaml diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100644 index 000000000..8aaa69656 --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,33 @@ +{ + "name": "Example devcontainer for add-on repositories", + "image": "ghcr.io/home-assistant/devcontainer:2-addons", + "appPort": ["7123:8123", "7357:4357"], + "postStartCommand": "bash devcontainer_bootstrap", + "runArgs": ["-e", "GIT_EDITOR=code --wait", "--privileged"], + "containerEnv": { + "WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}" + }, + "workspaceFolder": "/mnt/supervisor/addons/local/${localWorkspaceFolderBasename}", + "workspaceMount": "source=${localWorkspaceFolder},target=${containerWorkspaceFolder},type=bind,consistency=cached", + "customizations": { + "vscode": { + "extensions": ["timonwong.shellcheck", "esbenp.prettier-vscode"], + "settings": { + "terminal.integrated.profiles.linux": { + "zsh": { + "path": "/usr/bin/zsh" + } + }, + "terminal.integrated.defaultProfile.linux": "zsh", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true + } + } + }, + "mounts": [ + "type=volume,target=/var/lib/docker", + "type=volume,target=/mnt/supervisor" + ] +} diff --git a/.github/dependabot.yml b/.github/dependabot.yaml similarity index 66% rename from .github/dependabot.yml rename to .github/dependabot.yaml index ff55ddc99..83b26ea5b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yaml @@ -3,5 +3,5 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" - target-branch: dev + interval: weekly + time: "06:00" diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml new file mode 100644 index 000000000..47058c07e --- /dev/null +++ b/.github/workflows/builder.yaml @@ -0,0 +1,121 @@ +name: Builder + +env: + BUILD_ARGS: "--test" + MONITORED_FILES: "build.yaml config.yaml Dockerfile rootfs" + REGISTRY: ghcr.io + +on: + push: + branches: + - master + pull_request: + branches: + - master + +permissions: + contents: read + +jobs: + init: + runs-on: ubuntu-latest + name: Initialize builds + outputs: + changed_addons: ${{ steps.changed_addons.outputs.addons }} + changed: ${{ steps.changed_addons.outputs.changed }} + steps: + - name: Check out the repository + uses: actions/checkout@v6 + + - name: Get changed files + id: changed_files + uses: tj-actions/changed-files@v47 + + - name: Find add-on directories + id: addons + uses: home-assistant/actions/helpers/find-addons@master + + - name: Get changed add-ons + id: changed_addons + run: | + declare -a changed_addons + for addon in ${{ steps.addons.outputs.addons }}; do + if [[ "${{ steps.changed_files.outputs.all_changed_files }}" =~ $addon ]]; then + for file in ${{ env.MONITORED_FILES }}; do + if [[ "${{ steps.changed_files.outputs.all_changed_files }}" =~ $addon/$file ]]; then + if [[ ! "${changed_addons[@]}" =~ $addon ]]; then + changed_addons+=("\"${addon}\","); + fi + fi + done + fi + done + + changed=$(echo ${changed_addons[@]} | rev | cut -c 2- | rev) + + if [[ -n ${changed} ]]; then + echo "Changed add-ons: $changed"; + echo "changed=true" >> $GITHUB_OUTPUT; + echo "addons=[$changed]" >> $GITHUB_OUTPUT; + else + echo "No add-on had any monitored files changed (${{ env.MONITORED_FILES }})"; + fi + + build: + needs: init + runs-on: ubuntu-latest + if: needs.init.outputs.changed == 'true' + name: Build ${{ matrix.arch }} ${{ matrix.addon }} add-on + strategy: + matrix: + addon: ${{ fromJson(needs.init.outputs.changed_addons) }} + arch: ["aarch64", "amd64"] + permissions: + contents: read + packages: write + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Get information + id: info + uses: home-assistant/actions/helpers/info@master + with: + path: "./${{ matrix.addon }}" + + - name: Check if add-on should be built + id: check + run: | + if [[ "${{ steps.info.outputs.image }}" == "null" ]]; then + echo "Image property is not defined, skipping build" + echo "build_arch=false" >> $GITHUB_OUTPUT; + elif [[ "${{ steps.info.outputs.architectures }}" =~ ${{ matrix.arch }} ]]; then + echo "build_arch=true" >> $GITHUB_OUTPUT; + echo "image=$(echo ${{ steps.info.outputs.image }} | cut -d'/' -f3)" >> $GITHUB_OUTPUT; + if [[ -z "${{ github.head_ref }}" ]] && [[ "${{ github.event_name }}" == "push" ]]; then + echo "BUILD_ARGS=" >> $GITHUB_ENV; + fi + else + echo "${{ matrix.arch }} is not a valid arch for ${{ matrix.addon }}, skipping build"; + echo "build_arch=false" >> $GITHUB_OUTPUT; + fi + + - name: Login to GitHub Container Registry + if: env.BUILD_ARGS != '--test' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build ${{ matrix.addon }} add-on + if: steps.check.outputs.build_arch == 'true' + uses: home-assistant/builder@2025.09.0 + with: + args: | + ${{ env.BUILD_ARGS }} \ + --${{ matrix.arch }} \ + --target /data/${{ matrix.addon }} \ + --image "${{ steps.check.outputs.image }}" \ + --docker-hub "${{ env.REGISTRY }}/${{ github.repository_owner }}" \ + --addon diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml new file mode 100644 index 000000000..f13af30af --- /dev/null +++ b/.github/workflows/bump-version.yaml @@ -0,0 +1,73 @@ +name: Bump version + +on: + workflow_dispatch: + inputs: + addon_folder: + description: "The folder of the add-on to version bump" + required: true + default: "zigbee2mqtt" + type: choice + options: ["zigbee2mqtt"] + arg_name: + description: "The name of the version to bump (without '_VERSION')" + required: true + default: "ZIGBEE2MQTT" + type: string + version: + description: "The new version" + required: true + type: string + changelog: + description: "The changelog to write if needed" + type: string + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + name: Bump ${{ inputs.addon_folder }} add-on ${{ inputs.arg_name }} to ${{ inputs.version }} + permissions: + contents: write + pull-requests: write + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Bump version & write changelog + run: | + sed -i -E 's/^ ${{ inputs.arg_name }}_VERSION: .*$/ ${{ inputs.arg_name }}_VERSION: "${{ inputs.version }}"/g' ./${{ inputs.addon_folder }}/build.yaml + + addonFolderLower=$(echo "${{ inputs.addon_folder }}" | tr '[:upper:]' '[:lower:]') + argNameLower=$(echo "${{ inputs.arg_name }}" | tr '[:upper:]' '[:lower:]') + + if [[ "$addonFolderLower" == "$argNameLower" ]]; then + sed -i -E 's/^version: .*$/version: "${{ inputs.version }}"/g' ./${{ inputs.addon_folder }}/config.yaml + fi + + if [[ -z "${{ inputs.changelog }}" ]]; then + echo "No changelog to write" + else + echo -e "${{ inputs.changelog }}\n\n$(cat ./${{ inputs.addon_folder }}/CHANGELOG.md)" > ./${{ inputs.addon_folder }}/CHANGELOG.md + fi + + branch="${{ inputs.addon_folder }}-${{ inputs.arg_name }}-${{ inputs.version }}" + title="fix: bump ${{ inputs.addon_folder }} add-on ${{ inputs.arg_name }} to ${{ inputs.version }}" + + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git checkout -b "$branch" + git add . + git commit -m "$title" || echo 'Nothing to commit' + git push origin "$branch" + + echo -e "${{ inputs.changelog }}" | gh pr create \ + --fill \ + --base master \ + --head $branch \ + --title "$title" \ + --body-file - + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 000000000..cee91b33a --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,44 @@ +name: Lint + +on: + push: + branches: + - master + pull_request: + branches: + - master + schedule: + - cron: "0 0 * * *" + +permissions: + contents: read + +jobs: + find: + name: Find add-ons + runs-on: ubuntu-latest + outputs: + addons: ${{ steps.addons.outputs.addons_list }} + steps: + - name: ⤵️ Check out code from GitHub + uses: actions/checkout@v6 + + - name: 🔍 Find add-on directories + id: addons + uses: home-assistant/actions/helpers/find-addons@master + + lint: + name: Lint add-on ${{ matrix.path }} + runs-on: ubuntu-latest + needs: find + strategy: + matrix: + path: ${{ fromJson(needs.find.outputs.addons) }} + steps: + - name: ⤵️ Check out code from GitHub + uses: actions/checkout@v6 + + - name: 🚀 Run Home Assistant Add-on Lint + uses: frenck/action-addon-linter@v2 + with: + path: "./${{ matrix.path }}" diff --git a/.github/workflows/manual-builder.yaml b/.github/workflows/manual-builder.yaml new file mode 100644 index 000000000..7b06891e6 --- /dev/null +++ b/.github/workflows/manual-builder.yaml @@ -0,0 +1,69 @@ +name: Manual Builder + +env: + REGISTRY: ghcr.io + +on: + workflow_dispatch: + inputs: + addon_folder: + description: "The folder of the add-on to build" + required: true + default: "zigbee2mqtt-edge" + type: choice + options: ["zigbee2mqtt", "zigbee2mqtt-edge", "zigbee2mqtt-proxy"] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + name: Build ${{ matrix.arch }} ${{ inputs.addon_folder }} add-on + strategy: + matrix: + arch: ["aarch64", "amd64"] + permissions: + packages: write + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Get information + id: info + uses: home-assistant/actions/helpers/info@master + with: + path: "./${{ inputs.addon_folder }}" + + - name: Check if add-on should be built + id: check + run: | + if [[ "${{ steps.info.outputs.image }}" == "null" ]]; then + echo "Image property is not defined, skipping build" + echo "build_arch=false" >> $GITHUB_OUTPUT; + elif [[ "${{ steps.info.outputs.architectures }}" =~ ${{ matrix.arch }} ]]; then + echo "build_arch=true" >> $GITHUB_OUTPUT; + echo "image=$(echo ${{ steps.info.outputs.image }} | cut -d'/' -f3)" >> $GITHUB_OUTPUT; + else + echo "${{ matrix.arch }} is not a valid arch for ${{ inputs.addon_folder }}, skipping build"; + echo "build_arch=false" >> $GITHUB_OUTPUT; + fi + + - name: Login to GitHub Container Registry + if: steps.check.outputs.build_arch == 'true' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build ${{ inputs.addon_folder }} add-on + if: steps.check.outputs.build_arch == 'true' + uses: home-assistant/builder@2025.09.0 + with: + args: | + --${{ matrix.arch }} \ + --target /data/${{ inputs.addon_folder }} \ + --image "${{ steps.check.outputs.image }}" \ + --docker-hub "${{ env.REGISTRY }}/${{ github.repository_owner }}" \ + --addon diff --git a/.gitignore b/.gitignore index 2b8952585..85689612e 100644 --- a/.gitignore +++ b/.gitignore @@ -111,7 +111,3 @@ secrets.json zigbee2mqtt/rootfs zigbee2mqtt/Dockerfile zigbee2mqtt/build.yaml -zigbee2mqtt-edge/rootfs -zigbee2mqtt-edge/Dockerfile -zigbee2mqtt-edge/build.yaml -zigbee2mqtt-local/* \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..2d50d07b5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,62 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Home Assistant", + "type": "shell", + "command": "supervisor_run", + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Start Addon", + "type": "shell", + "command": "ha addons stop \"local_${input:addonName}\"; ha addons start \"local_${input:addonName}\"; docker logs --follow \"addon_local_${input:addonName}\"", + "group": { + "kind": "test", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [], + "runOptions": { + "reevaluateOnRerun": false + } + }, + { + "label": "Rebuild and Start Addon", + "type": "shell", + "command": "ha addons rebuild \"local_${input:addonName}\"; ha addons start \"local_${input:addonName}\"; docker logs --follow \"addon_local_${input:addonName}\"", + "group": { + "kind": "test", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + } + ], + "inputs": [ + { + "id": "addonName", + "type": "pickString", + "description": "Name of addon (to add your addon to this list, please edit .vscode/tasks.json)", + "options": [ + "zigbee2mqtt", + "zigbee2mqtt-edge", + "zigbee2mqtt-proxy", + ] + } + ] +} diff --git a/repository.json b/repository.json deleted file mode 100644 index 2b3c50c97..000000000 --- a/repository.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Home Assistant Add-on: Zigbee2MQTT", - "url": "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt", - "maintainer": "Koen Kanters " -} diff --git a/repository.yaml b/repository.yaml new file mode 100644 index 000000000..a821d21b9 --- /dev/null +++ b/repository.yaml @@ -0,0 +1,4 @@ +# https://developers.home-assistant.io/docs/add-ons/repository#repository-configuration +name: "Home Assistant Add-on: Zigbee2MQTT" +url: "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt" +maintainer: "Koen Kanters " diff --git a/zigbee2mqtt-edge/CHANGELOG.md b/zigbee2mqtt-edge/CHANGELOG.md index b9bcbc5a5..f65f77390 100644 --- a/zigbee2mqtt-edge/CHANGELOG.md +++ b/zigbee2mqtt-edge/CHANGELOG.md @@ -1 +1,5 @@ -Tracks latest Zigbee2MQTT [`dev branch`](https://github.com/Koenkk/zigbee2mqtt/commits/dev) \ No newline at end of file + + +## Edge + +- [Zigbee2MQTT current dev changelog](https://gist.github.com/Koenkk/bfd4c3d1725a2cccacc11d6ba51008ba) diff --git a/zigbee2mqtt-edge/DOCS.md b/zigbee2mqtt-edge/DOCS.md new file mode 100644 index 000000000..e6a4f45c3 --- /dev/null +++ b/zigbee2mqtt-edge/DOCS.md @@ -0,0 +1,12 @@ +# Home Assistant Add-on: Zigbee2MQTT Edge + +## How to use + +[Onboarding](https://www.zigbee2mqtt.io/guide/getting-started/#onboarding) allows you to setup Zigbee2MQTT without having to manually enter the details in the add-on configuration page. When starting the add-on with a brand new install (no configuration present), the frontend will show a quick setup page, allowing you to select various settings for Zigbee2MQTT to be able to start. + +> [!NOTE] +> Successful detection of adapters, to select from, may vary based on your setup/network. You may have to enter these [details manually](https://www.zigbee2mqtt.io/guide/configuration/adapter-settings.html#basic-configuration) on the page instead. + +# Adding Support for New Devices + +If you are interested in adding support for new devices to Zigbee2MQTT see [How to support new devices](https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html). diff --git a/zigbee2mqtt-edge/Dockerfile b/zigbee2mqtt-edge/Dockerfile new file mode 100644 index 000000000..2ef7e5ea0 --- /dev/null +++ b/zigbee2mqtt-edge/Dockerfile @@ -0,0 +1,37 @@ +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-dockerfile +ARG BUILD_FROM +FROM $BUILD_FROM AS base + +ENV LANG=C.UTF-8 + +RUN apk --no-cache add socat tini nodejs eudev tzdata + +FROM base AS deps_and_build + +# ignore cache for below layers when latest commit changes +ADD "https://api.github.com/repos/Koenkk/zigbee2mqtt/branches/dev" latest_commit +RUN \ + apk add --no-cache --virtual .buildtools npm make gcc g++ linux-headers udev git python3 \ + && git clone -b dev --single-branch --depth 1 https://github.com/Koenkk/zigbee2mqtt.git /app \ + && mkdir -p /app/dist \ + && cd /app \ + && echo $(git rev-parse --short=8 HEAD) > /app/dist/.hash \ + && npm i -g $(jq -r '.packageManager' package.json) \ + && pnpm i --frozen-lockfile --no-optional \ + && pnpm run build \ + && rm -rf node_modules \ + && pnpm i --frozen-lockfile --no-optional --prod \ + # force serialport to rebuild on current platform instead of using prebuilds + && rm -rf `find ./node_modules/.pnpm/ -wholename "*/@serialport/bindings-cpp/prebuilds" -type d` \ + && pnpm rebuild @serialport/bindings-cpp + +FROM base AS release + +COPY rootfs / + +WORKDIR /app +ENV NODE_ENV=production + +COPY --from=deps_and_build /app/node_modules ./node_modules +COPY --from=deps_and_build /app/dist ./dist +COPY --from=deps_and_build /app/package.json /app/LICENSE /app/index.js ./ diff --git a/zigbee2mqtt-edge/README.md b/zigbee2mqtt-edge/README.md index e149449f9..795ad4e37 100644 --- a/zigbee2mqtt-edge/README.md +++ b/zigbee2mqtt-edge/README.md @@ -1,6 +1,9 @@ # Home Assistant Add-on: Zigbee2MQTT Edge -[![Docker Pulls](https://img.shields.io/docker/pulls/zigbee2mqtt/zigbee2mqtt-edge-amd64.svg?style=flat-square&logo=docker)](https://cloud.docker.com/u/zigbee2mqtt/repository/docker/dwelch2101/zigbee2mqtt-edge-amd64) +![Supports aarch64 Architecture](https://img.shields.io/badge/aarch64-yes-green.svg) +[![Docker aarch64 Pulls](https://img.shields.io/docker/pulls/zigbee2mqtt/zigbee2mqtt-edge-aarch64.svg?style=flat-square&logo=docker)](https://cloud.docker.com/u/zigbee2mqtt/repository/docker/dwelch2101/zigbee2mqtt-edge-aarch64) +![Supports amd64 Architecture](https://img.shields.io/badge/amd64-yes-green.svg) +[![Docker amd64 Pulls](https://img.shields.io/docker/pulls/zigbee2mqtt/zigbee2mqtt-edge-amd64.svg?style=flat-square&logo=docker)](https://cloud.docker.com/u/zigbee2mqtt/repository/docker/dwelch2101/zigbee2mqtt-edge-amd64) ⚠️ This is the Edge version (follows the Zigbee2MQTT development branch) ⚠️ diff --git a/zigbee2mqtt-edge/build.yaml b/zigbee2mqtt-edge/build.yaml new file mode 100644 index 000000000..49cdd117c --- /dev/null +++ b/zigbee2mqtt-edge/build.yaml @@ -0,0 +1,9 @@ +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-dockerfile +build_from: + aarch64: "ghcr.io/home-assistant/aarch64-base:3.22" + amd64: "ghcr.io/home-assistant/amd64-base:3.22" +labels: + org.opencontainers.image.title: "Home Assistant Add-on: Zigbee2MQTT Edge" + org.opencontainers.image.description: "Development version of the Zigbee2MQTT add-on" + org.opencontainers.image.source: "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt" + org.opencontainers.image.licenses: "Apache License 2.0" diff --git a/zigbee2mqtt-edge/config.json b/zigbee2mqtt-edge/config.json deleted file mode 100644 index a0f7fe340..000000000 --- a/zigbee2mqtt-edge/config.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "name": "Zigbee2MQTT Edge", - "version": "edge", - "slug": "zigbee2mqtt_edge", - "description": "Development build of the Zigbee2MQTT add-on", - "uart": true, - "udev": true, - "url": "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/tree/master/zigbee2mqtt-edge", - "startup": "application", - "services": [ - "mqtt:need" - ], - "hassio_api": true, - "arch": [ - "aarch64", - "amd64" - ], - "boot": "auto", - "init": false, - "ingress": true, - "timeout": 30, - "panel_icon": "mdi:zigbee", - "map": [ - { - "type": "share", - "read_only": false - }, - { - "type": "homeassistant_config", - "read_only": false, - "path": "/config" - }, - { - "type": "addon_config", - "read_only": false, - "path": "/addon_config" - } - ], - "ports": { - "8485/tcp": 8485, - "8099/tcp": null - }, - "ports_description": { - "8485/tcp": "Socat tcp-listen port", - "8099/tcp": "Frontend tcp-listen port" - }, - "options": { - "data_path": "/config/zigbee2mqtt", - "socat": { - "enabled": false, - "master": "pty,raw,echo=0,link=/tmp/ttyZ2M,mode=777", - "slave": "tcp-listen:8485,keepalive,nodelay,reuseaddr,keepidle=1,keepintvl=1,keepcnt=5", - "options": "-d -d", - "log": false - }, - "mqtt": {}, - "serial": {} - }, - "schema": { - "disable_tuya_default_response": "bool?", - "data_path": "str", - "socat": { - "enabled": "bool?", - "master": "str?", - "slave": "str?", - "options": "str?", - "log": "bool?" - }, - "mqtt": { - "server": "str?", - "ca": "str?", - "key": "str?", - "cert": "str?", - "user": "str?", - "password": "str?" - }, - "serial": { - "port": "str?", - "adapter": "match(zstack|deconz|zigate|ezsp|ember|zboss)?", - "baudrate": "int?", - "rtscts": "bool?" - }, - "watchdog": "str?", - "force_onboarding": "bool?" - }, - "image": "ghcr.io/zigbee2mqtt/zigbee2mqtt-edge-{arch}" -} \ No newline at end of file diff --git a/zigbee2mqtt-edge/config.yaml b/zigbee2mqtt-edge/config.yaml new file mode 100644 index 000000000..0c2272642 --- /dev/null +++ b/zigbee2mqtt-edge/config.yaml @@ -0,0 +1,50 @@ +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config +name: Zigbee2MQTT Edge +version: "edge-2" +breaking_versions: + - "edge-2" +slug: zigbee2mqtt_edge +description: Development version of the Zigbee2MQTT add-on +url: "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/tree/master/zigbee2mqtt-edge" +panel_icon: "mdi:zigbee" +arch: + - aarch64 + - amd64 +image: "ghcr.io/zigbee2mqtt/zigbee2mqtt-edge-{arch}" +init: false +ingress: true +uart: true +udev: true +hassio_api: true +timeout: 30 +services: + - mqtt:need +map: + # @deprecated for migration purpose only + - type: homeassistant_config + read_only: false + path: /config + - type: addon_config + read_only: false + path: /addon_config +ports: + "8099/tcp": null + "8485/tcp": 8485 +options: + watchdog: default + force_onboarding: false + socat_enabled: false + socat_master: "pty,raw,echo=0,link=/tmp/ttyZ2M,mode=777" + socat_slave: "tcp-listen:8485,keepalive,nodelay,reuseaddr,keepidle=1,keepintvl=1,keepcnt=5" + socat_options: "-d -d" + socat_log: false +schema: + # @deprecated for migration purpose only + data_path: "str?" + watchdog: "str?" + force_onboarding: bool + socat_enabled: "bool?" + socat_master: "str?" + socat_slave: "str?" + socat_options: "str?" + socat_log: "bool?" diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/zigbee2mqtt b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/zigbee2mqtt new file mode 100644 index 000000000..e69de29bb diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/zigbee2mqtt-migrate-config b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/zigbee2mqtt-migrate-config new file mode 100644 index 000000000..e69de29bb diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/dependencies.d/base b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/dependencies.d/base new file mode 100644 index 000000000..e69de29bb diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/type b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/type new file mode 100644 index 000000000..3d92b15f2 --- /dev/null +++ b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/type @@ -0,0 +1 @@ +oneshot \ No newline at end of file diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/up b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/up new file mode 100644 index 000000000..30335a8e8 --- /dev/null +++ b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt-migrate-config/up @@ -0,0 +1 @@ +/etc/s6-overlay/scripts/zigbee2mqtt-migrate-config.sh \ No newline at end of file diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/dependencies.d/zigbee2mqtt-migrate-config b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/dependencies.d/zigbee2mqtt-migrate-config new file mode 100644 index 000000000..e69de29bb diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/finish b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/finish new file mode 100644 index 000000000..fd8ef0ba5 --- /dev/null +++ b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/finish @@ -0,0 +1,28 @@ +#!/command/with-contenv bashio +# vim: ft=bash +# shellcheck shell=bash +# ============================================================================== +# Take down the S6 supervision tree when daemon fails +# ============================================================================== +# shellcheck disable=SC2155 + +readonly exit_code_container=$( /run/s6-linux-init-container-results/exitcode + fi + [[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt +elif [[ "${exit_code_service}" -ne 0 ]]; then + if [[ "${exit_code_container}" -eq 0 ]]; then + echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode + fi + exec /run/s6/basedir/bin/halt +fi diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/run b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/run new file mode 100644 index 000000000..6bce57fd8 --- /dev/null +++ b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/run @@ -0,0 +1,93 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +# ============================================================================== +# Start the nginx service +# s6-overlay docs: https://github.com/just-containers/s6-overlay +# ============================================================================== +# shellcheck disable=SC2155 + +set -e + +bashio::log.info "Configuring Zigbee2MQTT Edge..." + +export ZIGBEE2MQTT_DATA="/addon_config" + +mkdir -p "$ZIGBEE2MQTT_DATA" || bashio::exit.nok "Could not create $ZIGBEE2MQTT_DATA" + +if bashio::var.has_value "$(bashio::services 'mqtt')"; then + if bashio::var.true "$(bashio::services 'mqtt' 'ssl')"; then + export ZIGBEE2MQTT_CONFIG_MQTT_SERVER="mqtts://$(bashio::services 'mqtt' 'host'):$(bashio::services 'mqtt' 'port')" + else + export ZIGBEE2MQTT_CONFIG_MQTT_SERVER="mqtt://$(bashio::services 'mqtt' 'host'):$(bashio::services 'mqtt' 'port')" + fi + export ZIGBEE2MQTT_CONFIG_MQTT_USER="$(bashio::services 'mqtt' 'username')" + export ZIGBEE2MQTT_CONFIG_MQTT_PASSWORD="$(bashio::services 'mqtt' 'password')" +else + bashio::log.warning "MQTT service configuration unavailable" +fi + +# Socat +if bashio::config.true 'socat_enabled'; then + bashio::log.info "Socat enabled" + SOCAT_MASTER=$(bashio::config 'socat_master') + SOCAT_SLAVE=$(bashio::config 'socat_slave') + + # Validate input + if bashio::var.is_empty "$SOCAT_MASTER"; then + bashio::exit.nok "Socat is enabled but not started because no master address specified" + fi + + if bashio::var.is_empty "$SOCAT_SLAVE"; then + bashio::exit.nok "Socat is enabled but not started because no slave address specified" + fi + + bashio::log.info "Starting socat" + + SOCAT_OPTIONS=$(bashio::config 'socat_options') + + # Socat start configuration + bashio::log.blue "Socat startup parameters:" + bashio::log.blue "Options: $SOCAT_OPTIONS" + bashio::log.blue "Master: $SOCAT_MASTER" + bashio::log.blue "Slave: $SOCAT_SLAVE" + + bashio::log.info "Starting socat process ..." + exec socat "$SOCAT_OPTIONS" "$SOCAT_MASTER" "$SOCAT_SLAVE" & + + bashio::log.debug "Modifying process for logging if required" + + if bashio::config.true 'socat_log'; then + bashio::log.debug "Socat loggin enabled, setting file path to $ZIGBEE2MQTT_DATA/socat.log" + exec &>"$ZIGBEE2MQTT_DATA/socat.log" 2>&1 + else + bashio::log.debug "No logging required" + fi +else + bashio::log.info "Socat not enabled" +fi + +if bashio::config.has_value 'watchdog'; then + export Z2M_WATCHDOG="$(bashio::config 'watchdog')" + bashio::log.info "Enabled Zigbee2MQTT watchdog with value '$Z2M_WATCHDOG'" +fi + +if bashio::config.true 'force_onboarding'; then + export Z2M_ONBOARD_FORCE_RUN="1" + bashio::log.info "Forcing onboarding to run" +fi + +export TZ="$(bashio::supervisor.timezone)" +export ZIGBEE2MQTT_CONFIG_FRONTEND_ENABLED='true' +export ZIGBEE2MQTT_CONFIG_FRONTEND_PORT='8099' +export ZIGBEE2MQTT_CONFIG_HOMEASSISTANT_ENABLED='true' +export Z2M_ONBOARD_URL='http://0.0.0.0:8099' + +cd /app + +if [ -d "$ZIGBEE2MQTT_DATA/app_overrides" ]; then + bashio::log.info "Applying app overrides..." + cp -R "$ZIGBEE2MQTT_DATA/app_overrides/." ./ || bashio::exit.nok "Could not apply app overrides" +fi + +bashio::log.info "Starting Zigbee2MQTT Edge..." +exec node index.js \ No newline at end of file diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/type b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/s6-rc.d/zigbee2mqtt/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/zigbee2mqtt-edge/rootfs/etc/s6-overlay/scripts/zigbee2mqtt-migrate-config.sh b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/scripts/zigbee2mqtt-migrate-config.sh new file mode 100644 index 000000000..d8adcf9c1 --- /dev/null +++ b/zigbee2mqtt-edge/rootfs/etc/s6-overlay/scripts/zigbee2mqtt-migrate-config.sh @@ -0,0 +1,94 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +# ============================================================================== +# Start the nginx service +# s6-overlay docs: https://github.com/just-containers/s6-overlay +# ============================================================================== +# shellcheck disable=SC2155 + +set -e + +z2m_config="$(bashio::config 'data_path')" + +if bashio::var.is_empty "$z2m_config"; then + bashio::log.info "data_path is unspecified. No migration necessary." + + bashio::exit.ok +fi + +if bashio::var.equals "$z2m_config" "/addon_config" || bashio::var.equals "$z2m_config" "/addon_config/";then + bashio::log.info "data_path is already '/addon_config'. No migration necessary." + + bashio::exit.ok +fi + +if ! bashio::fs.directory_exists "$z2m_config"; then + bashio::log.info "data_path '$z2m_config' does not exist. No migration necessary." + + bashio::exit.ok +fi + +if [[ "$(ls -A "$z2m_config" | wc -l)" -eq 0 ]]; then + bashio::log.info "data_path '$z2m_config' is empty. No migration necessary." + + rm -rfv "$z2m_config" + bashio::exit.ok +fi + +function archive_src() { + tar -czvf "/addon_config/src-archive.tar.gz" "$z2m_config" + rm -rfv "$z2m_config" + bashio::log.info "Done archiving src." +} + +bashio::log.info "Starting Zigbee2MQTT config migration..." + +# don't want the symlinks if they exist, Z2M will re-create as needed so can safely delete them +rm -fv "$z2m_config/external_converters/node_modules" "$z2m_config/external_extensions/node_modules" +rm -fv "/addon_config/external_converters/node_modules" "/addon_config/external_extensions/node_modules" + +if [[ $z2m_config == /addon_config/* ]]; then + bashio::log.info "data_path is nested under addon_config folder. Skipping dst archiving..." +else + if [[ "$(ls -A "/addon_config" | wc -l)" -ne 0 ]]; then + bashio::log.info "New addon_config folder is not empty, archiving it..." + + # if certain files exists in addon_config, check mtime + # check db first since it is always written on shutdown + if bashio::fs.file_exists "/addon_config/database.db"; then + if [[ "/addon_config/database.db" -nt "$z2m_config/database.db" ]]; then + bashio::log.info "/addon_config/database.db is newer. Skipping migration, only archiving src..." + + archive_src + bashio::exit.ok + fi + else + # in case add-on onboarding was started but never finished, database.db does not exist + if bashio::fs.file_exists "/addon_config/configuration.yaml" \ + && [[ "/addon_config/configuration.yaml" -nt "$z2m_config/configuration.yaml" ]] + then + bashio::log.info "/addon_config/configuration.yaml is newer. Skipping migration, only archiving src..." + + archive_src + bashio::exit.ok + fi + fi + + tar -czvf "/addon_config.dst-archive.tar.gz" "/addon_config" + + # do not use `rm -rfv /addon_config/*` to remove content as that forces prompt with zsh + for item in /addon_config/*; do + rm -rfv "$item" + done + + mv "/addon_config.dst-archive.tar.gz" "/addon_config/dst-archive.tar.gz" + bashio::log.info "Done archiving dst." + fi +fi + +cp -R -d "$z2m_config/." "/addon_config" +bashio::log.info "Done copying src to dst." + +archive_src + +bashio::log.info "Successfully migrated to addon_config." diff --git a/zigbee2mqtt-edge/translations/en.yaml b/zigbee2mqtt-edge/translations/en.yaml new file mode 100644 index 000000000..851592ce1 --- /dev/null +++ b/zigbee2mqtt-edge/translations/en.yaml @@ -0,0 +1,44 @@ +--- +configuration: + # @deprecated for migration purpose only + data_path: + name: Data path (deprecated) + description: Used to migrate from previous version's folder to new backup-supporting addon_config folder, after which, this is no longer needed. + watchdog: + name: Watchdog + description: >- + Automatically restart Zigbee2MQTT in case of a soft failure (like 'adapter disconnected'). + Using `default` will use the retry delays 1min, 5min, 15min, 30min, 60min. + Custom delays are also supported, e.g. `5,10,30` will start Zigbee2MQTT with the watchdog's retry delays set to 5min, 10min, 30min. + https://www.zigbee2mqtt.io/guide/installation/15_watchdog.html + force_onboarding: + name: Force onboarding + description: >- + Force the onboarding to run even if it was previously done. + Can be used for e.g. when changing adapter to update the serial port. + Make sure to disable once done. + socat_enabled: + name: Enable Socat + description: >- + Socat can be used to forward a serial device over TCP to Zigbee2MQTT. + The defaults values will make sure that socat listens on port `8485` and redirects its output to `/dev/ttyZ2M`. + https://linux.die.net/man/1/socat + socat_master: + name: Socat Master + description: >- + Master or first address used in socat command line (mandatory if socat enabled). + Example: `pty,raw,echo=0,link=/tmp/ttyZ2M,mode=777` + socat_slave: + name: Socat Slave + description: >- + Slave or second address used in socat command line (mandatory if socat enabled). + Example: `tcp-listen:8485,keepalive,nodelay,reuseaddr,keepidle=1,keepintvl=1,keepcnt=5` + socat_options: + name: Socat Options + description: >- + Extra options added to the socat command line. + Example: `-d -d` + socat_log: + name: Socat Enable Logging + description: >- + Log the socat stdout/stderr to /config/socat.log diff --git a/zigbee2mqtt-proxy/CHANGELOG.md b/zigbee2mqtt-proxy/CHANGELOG.md new file mode 100644 index 000000000..a0d61b9d3 --- /dev/null +++ b/zigbee2mqtt-proxy/CHANGELOG.md @@ -0,0 +1,5 @@ + + +## 1.0.0 + +- Initial release diff --git a/zigbee2mqtt-proxy/DOCS.md b/zigbee2mqtt-proxy/DOCS.md new file mode 100644 index 000000000..1f07a4e84 --- /dev/null +++ b/zigbee2mqtt-proxy/DOCS.md @@ -0,0 +1,5 @@ +# Home Assistant Add-on: Zigbee2MQTT Proxy + +## How to use + +Configure `server` and optionally `auth_token` then start the add-on. \ No newline at end of file diff --git a/zigbee2mqtt-proxy/Dockerfile b/zigbee2mqtt-proxy/Dockerfile index 635337d15..4b4af9406 100644 --- a/zigbee2mqtt-proxy/Dockerfile +++ b/zigbee2mqtt-proxy/Dockerfile @@ -1,24 +1,10 @@ +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-dockerfile ARG BUILD_FROM FROM $BUILD_FROM -RUN apk add --no-cache --update nginx -# Copy root filesystem -ADD nginx.conf.gtpl / -ADD entrypoint.sh / +RUN \ + apk --no-cache add nginx tzdata \ + && mkdir -p /run/nginx \ + && rm -rf /tmp/* /etc/nginx -ENTRYPOINT ["/bin/sh", "-c", "/entrypoint.sh"] -# Build arguments -ARG BUILD_ARCH -ARG BUILD_DESCRIPTION -ARG BUILD_NAME -ARG BUILD_VERSION - -# Labels -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} - -ENTRYPOINT ["/entrypoint.sh"] +COPY rootfs / diff --git a/zigbee2mqtt-proxy/README.md b/zigbee2mqtt-proxy/README.md index e3620ce0f..c671896e0 100644 --- a/zigbee2mqtt-proxy/README.md +++ b/zigbee2mqtt-proxy/README.md @@ -1,13 +1,11 @@ # Home Assistant Add-on: Zigbee2MQTT Proxy -[![Docker Pulls](https://img.shields.io/docker/pulls/zigbee2mqtt/zigbee2mqtt-proxy-amd64.svg?style=flat-square&logo=docker)](https://cloud.docker.com/u/zigbee2mqtt/repository/docker/dwelch2101/zigbee2mqtt-proxy-amd64) +![Supports aarch64 Architecture](https://img.shields.io/badge/aarch64-yes-green.svg) +[![Docker aarch64 Pulls](https://img.shields.io/docker/pulls/zigbee2mqtt/zigbee2mqtt-proxy-aarch64.svg?style=flat-square&logo=docker)](https://cloud.docker.com/u/zigbee2mqtt/repository/docker/dwelch2101/zigbee2mqtt-proxy-aarch64) +![Supports amd64 Architecture](https://img.shields.io/badge/amd64-yes-green.svg) +[![Docker amd64 Pulls](https://img.shields.io/docker/pulls/zigbee2mqtt/zigbee2mqtt-proxy-amd64.svg?style=flat-square&logo=docker)](https://cloud.docker.com/u/zigbee2mqtt/repository/docker/dwelch2101/zigbee2mqtt-proxy-amd64) ⚠️ This add-on does not contain Zigbee2MQTT ⚠️ -This add-on acts as a proxy to an external running Zigbee2MQTT instance. +This add-on acts as a proxy to an external running Zigbee2MQTT instance. The sole purpose of this add-on is to add a Zigbee2MQTT icon to the sidebar of Home Assistant which will open the frontend of an external running Zigbee2MQTT instance. - -## Options - -- `server` (required): this should be the local URL on which the Zigbee2MQTT frontend is running, e.g. `http://192.168.2.43:8080`. Make sure there is no trailing slash! -- `auth_token` (optional): only use when you have an `auth_token` set for the frontend in the Zigbee2MQTT configuration. \ No newline at end of file diff --git a/zigbee2mqtt-proxy/build.yaml b/zigbee2mqtt-proxy/build.yaml new file mode 100644 index 000000000..90cd7873d --- /dev/null +++ b/zigbee2mqtt-proxy/build.yaml @@ -0,0 +1,9 @@ +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-dockerfile +build_from: + aarch64: "ghcr.io/home-assistant/aarch64-base:3.22" + amd64: "ghcr.io/home-assistant/amd64-base:3.22" +labels: + org.opencontainers.image.title: "Home Assistant Add-on: Zigbee2MQTT Proxy" + org.opencontainers.image.description: "Proxy for externally running Zigbee2MQTT" + org.opencontainers.image.source: "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt" + org.opencontainers.image.licenses: "Apache License 2.0" diff --git a/zigbee2mqtt-proxy/config.json b/zigbee2mqtt-proxy/config.json deleted file mode 100644 index 92b63a3ca..000000000 --- a/zigbee2mqtt-proxy/config.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Zigbee2MQTT Proxy", - "version": "0.3.0", - "slug": "zigbee2mqtt_proxy", - "description": "Proxy for externally running Zigbee2MQTT", - "url": "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/tree/master/zigbee2mqtt-proxy", - "arch": [ - "aarch64", - "amd64" - ], - "boot": "auto", - "init": true, - "true": true, - "ingress": true, - "timeout": 30, - "panel_icon": "mdi:zigbee", - "map": [ - "config:ro" - ], - "ports": { - "8099/tcp": null - }, - "ports_description": { - "8099/tcp": "Frontend tcp-listen port" - }, - "options": { - "server": null, - "auth_token": "" - }, - "schema": { - "server": "url", - "auth_token": "str?" - }, - "image": "ghcr.io/zigbee2mqtt/zigbee2mqtt-proxy-{arch}" -} diff --git a/zigbee2mqtt-proxy/config.yaml b/zigbee2mqtt-proxy/config.yaml new file mode 100644 index 000000000..b6af248e9 --- /dev/null +++ b/zigbee2mqtt-proxy/config.yaml @@ -0,0 +1,26 @@ +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config +name: Zigbee2MQTT Proxy +version: "1.0.0" +breaking_versions: + - "1.0.0" +slug: zigbee2mqtt_proxy +description: Proxy for externally running Zigbee2MQTT +url: "https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/tree/master/zigbee2mqtt-proxy" +panel_icon: "mdi:zigbee" +arch: + - aarch64 + - amd64 +image: "ghcr.io/zigbee2mqtt/zigbee2mqtt-proxy-{arch}" +init: false +ingress: true +timeout: 30 +map: + - addon_config:rw +ports: + "8099/tcp": null +options: + server: "" + auth_token: "" +schema: + server: url + auth_token: "str?" diff --git a/zigbee2mqtt-proxy/entrypoint.sh b/zigbee2mqtt-proxy/entrypoint.sh deleted file mode 100755 index b823c4c12..000000000 --- a/zigbee2mqtt-proxy/entrypoint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -tempio -conf /data/options.json -template /nginx.conf.gtpl -out /tmp/nginx.conf -nginx -t -c /tmp/nginx.conf - -exec nginx -c /tmp/nginx.conf \ No newline at end of file diff --git a/zigbee2mqtt-proxy/rootfs/etc/nginx/mime.types b/zigbee2mqtt-proxy/rootfs/etc/nginx/mime.types new file mode 100644 index 000000000..b4828d669 --- /dev/null +++ b/zigbee2mqtt-proxy/rootfs/etc/nginx/mime.types @@ -0,0 +1,99 @@ + +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/avif avif; + 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/wasm wasm; + 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; +} \ No newline at end of file diff --git a/zigbee2mqtt-proxy/nginx.conf.gtpl b/zigbee2mqtt-proxy/rootfs/etc/nginx/nginx.gtpl similarity index 67% rename from zigbee2mqtt-proxy/nginx.conf.gtpl rename to zigbee2mqtt-proxy/rootfs/etc/nginx/nginx.gtpl index 6bbc5e20e..8555fac30 100644 --- a/zigbee2mqtt-proxy/nginx.conf.gtpl +++ b/zigbee2mqtt-proxy/rootfs/etc/nginx/nginx.gtpl @@ -1,10 +1,8 @@ - - # Run nginx in foreground. daemon off; # This is run inside Docker. -user nginx; +user root; # Pid storage location. pid /var/run/nginx.pid; @@ -20,39 +18,44 @@ error_log /proc/1/fd/1 error; # Max num of simultaneous connections by a worker process. events { - worker_connections 512; + worker_connections 1024; } http { - access_log off; - client_max_body_size 4G; - default_type application/octet-stream; - keepalive_timeout 65; - sendfile off; - server_tokens off; - tcp_nodelay on; - tcp_nopush on; + include /etc/nginx/mime.types; + + access_log off; + 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 $http_upgrade $connection_upgrade { default upgrade; '' close; } + resolver 127.0.0.11 ipv6=off; server { listen 8099 default_server; - root /dev/null; - server_name _; + allow 172.30.32.2; + deny all; - location / { - allow 172.30.32.2; - deny all; + server_name _; - set $target "{{ .server }}"; - set $token "{{ .auth_token }}"; + root /dev/null; + location / { + set $target "{{ .server }}"; + set $token "{{ .auth_token }}"; set $args $args&token=$token; + proxy_pass $target; proxy_http_version 1.1; proxy_ignore_client_abort off; @@ -64,9 +67,12 @@ http { proxy_no_cache 1; proxy_cache_bypass 1; - add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; - add_header Pragma "no-cache"; - add_header Expires 0; + add_header Last-Modified $date_gmt; + add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + if_modified_since off; + expires off; + etag off; proxy_set_header Accept-Encoding ""; proxy_set_header Connection $connection_upgrade; @@ -76,9 +82,6 @@ http { proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-NginX-Proxy true; proxy_set_header X-Real-IP $remote_addr; - } } } - - diff --git a/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/dependencies.d/base b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/dependencies.d/base new file mode 100644 index 000000000..e69de29bb diff --git a/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish new file mode 100644 index 000000000..4634dd768 --- /dev/null +++ b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/finish @@ -0,0 +1,28 @@ +#!/command/with-contenv bashio +# vim: ft=bash +# shellcheck shell=bash +# ============================================================================== +# Take down the S6 supervision tree when daemon fails +# ============================================================================== +# shellcheck disable=SC2155 + +readonly exit_code_container=$( /run/s6-linux-init-container-results/exitcode + fi + [[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt +elif [[ "${exit_code_service}" -ne 0 ]]; then + if [[ "${exit_code_container}" -eq 0 ]]; then + echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode + fi + exec /run/s6/basedir/bin/halt +fi diff --git a/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/run b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/run new file mode 100644 index 000000000..60598fb22 --- /dev/null +++ b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/run @@ -0,0 +1,21 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +# ============================================================================== +# Start the nginx service +# s6-overlay docs: https://github.com/just-containers/s6-overlay +# ============================================================================== +# shellcheck disable=SC2155 + +set -e + +bashio::config.require 'server' + +bashio::log.info "Configuring nginx..." + +tempio \ + -conf /data/options.json \ + -template /etc/nginx/nginx.gtpl \ + -out /etc/nginx/nginx.conf + +bashio::log.info "Starting nginx..." +exec nginx -c /etc/nginx/nginx.conf < /dev/null \ No newline at end of file diff --git a/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/type b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/nginx/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/nginx b/zigbee2mqtt-proxy/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/nginx new file mode 100644 index 000000000..e69de29bb diff --git a/zigbee2mqtt-proxy/translations/en.yaml b/zigbee2mqtt-proxy/translations/en.yaml new file mode 100644 index 000000000..48c031930 --- /dev/null +++ b/zigbee2mqtt-proxy/translations/en.yaml @@ -0,0 +1,8 @@ +--- +configuration: + server: + name: Server + description: Should be the local URL on which the Zigbee2MQTT frontend is running, e.g. `http://192.168.2.43:8080`. Make sure there is no trailing slash! + auth_token: + name: Auth Token + description: Only use when you have an `auth_token` set for the frontend in the Zigbee2MQTT configuration. \ No newline at end of file From 7589e9eab69e05c490de6c9a00d6802ce528ba1e Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:48:02 +0100 Subject: [PATCH 2/3] fix: remove old ci actions for migrated add-ons --- .github/workflows/ci.yml | 120 ++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 933839970..2d5e3c8ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,8 +6,8 @@ on: workflow_dispatch: inputs: type: - description: 'Type (edge/proxy)' - default: 'edge' + description: "Type (edge/proxy)" + default: "edge" required: true jobs: @@ -18,34 +18,24 @@ jobs: ADDON_LIST: ${{ env.ADDON_LIST }} BUILD_ARGS: ${{ env.BUILD_ARGS }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: "ARGS: default" - run: | - echo "ADDON_LIST=['zigbee2mqtt', 'zigbee2mqtt-edge']" >> $GITHUB_ENV - echo "BUILD_ARGS=--no-latest --test" >> $GITHUB_ENV - - name: "ARGS: zigbee2mqtt-proxy" # Build of addon proxy version - if: github.ref == 'refs/heads/master' && (github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'proxy') - run: | - echo "ADDON_LIST=['zigbee2mqtt-proxy']" >> $GITHUB_ENV - echo "BUILD_ARGS=--no-cache" >> $GITHUB_ENV - - name: "ARGS: zigbee2mqtt-edge" # Build of addon edge version - if: github.ref == 'refs/heads/master' && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'edge')) - run: | - echo "ADDON_LIST=['zigbee2mqtt-edge']" >> $GITHUB_ENV - echo "BUILD_ARGS=--no-cache" >> $GITHUB_ENV - - name: "ARGS: zigbee2mqtt" # Build of addon release version - if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push' - run: | - echo "ADDON_LIST=['zigbee2mqtt']" >> $GITHUB_ENV - echo "BUILD_ARGS=--no-cache --docker-hub-check" >> $GITHUB_ENV + - name: "ARGS: default" + run: | + echo "ADDON_LIST=['zigbee2mqtt']" >> $GITHUB_ENV + echo "BUILD_ARGS=--no-latest --test" >> $GITHUB_ENV + - name: "ARGS: zigbee2mqtt" # Build of addon release version + if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push' + run: | + echo "ADDON_LIST=['zigbee2mqtt']" >> $GITHUB_ENV + echo "BUILD_ARGS=--no-cache --docker-hub-check" >> $GITHUB_ENV - - name: Determine arch - id: determine_arch - run: | - ARCH_LIST=$(jq -r -c '.arch' ./${{ fromJSON(env.ADDON_LIST)[0] }}/config.json) - echo "Found the following arches: $ARCH_LIST" - echo "ARCH_LIST=$ARCH_LIST" >> $GITHUB_ENV + - name: Determine arch + id: determine_arch + run: | + ARCH_LIST=$(jq -r -c '.arch' ./${{ fromJSON(env.ADDON_LIST)[0] }}/config.json) + echo "Found the following arches: $ARCH_LIST" + echo "ARCH_LIST=$ARCH_LIST" >> $GITHUB_ENV build: runs-on: ubuntu-latest @@ -57,54 +47,52 @@ jobs: arch: ${{fromJSON(needs.variables.outputs.ARCH_LIST)}} addon: ${{fromJSON(needs.variables.outputs.ADDON_LIST)}} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Log in to docker.io - if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') - uses: docker/login-action@v3 - with: + - name: Log in to docker.io + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + uses: docker/login-action@v3 + with: username: koenkk password: ${{ secrets.DOCKER_KEY }} - - name: Log in to ghcr.io - if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') - uses: docker/login-action@v3 - with: + - name: Log in to ghcr.io + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + uses: docker/login-action@v3 + with: registry: ghcr.io username: koenkk password: ${{ secrets.GH_TOKEN }} - - name: Prepare data folder - run: | - if [ "${{ matrix.addon }}" != "zigbee2mqtt-proxy" ]; then + - name: Prepare data folder + run: | cp -R common/rootfs ${{ matrix.addon }} cp common/Dockerfile ${{ matrix.addon }} - fi - cp common/build.yaml ${{ matrix.addon }} + cp common/build.yaml ${{ matrix.addon }} - - name: Build - uses: home-assistant/builder@master - # Note: if running without `--test`, image is pushed to docker.io - with: - args: | - --${{ matrix.arch }} \ - --target ${{ matrix.addon }} \ - ${{ env.BUILD_ARGS }} + - name: Build + uses: home-assistant/builder@master + # Note: if running without `--test`, image is pushed to docker.io + with: + args: | + --${{ matrix.arch }} \ + --target ${{ matrix.addon }} \ + ${{ env.BUILD_ARGS }} - # Keep pushing to docker.io as it is unclear how - # HA handles config.json updates (especially for the unversioned edge) - - name: Push to docker.io - if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') - run: | - CONFIG_IMAGE=$(cat ${{ matrix.addon }}/config.json | jq -r '.image | sub("{arch}"; "${{ matrix.arch }}")') - CONFIG_VERSION=$(cat ${{ matrix.addon }}/config.json | jq -r .version) + # Keep pushing to docker.io as it is unclear how + # HA handles config.json updates (especially for the unversioned edge) + - name: Push to docker.io + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + run: | + CONFIG_IMAGE=$(cat ${{ matrix.addon }}/config.json | jq -r '.image | sub("{arch}"; "${{ matrix.arch }}")') + CONFIG_VERSION=$(cat ${{ matrix.addon }}/config.json | jq -r .version) - VERSIONS=("latest" "$CONFIG_VERSION") - for VERSION in "${VERSIONS[@]}"; do - GHCR_IMAGE="$CONFIG_IMAGE:$CONFIG_VERSION" - DOCKER_IO_IMAGE=$(echo "$GHCR_IMAGE" | sed 's|ghcr\.io\/||') - echo "Push: $GHCR_IMAGE -> $DOCKER_IO_IMAGE" - docker pull $GHCR_IMAGE - docker tag $GHCR_IMAGE $DOCKER_IO_IMAGE - docker push $DOCKER_IO_IMAGE - done + VERSIONS=("latest" "$CONFIG_VERSION") + for VERSION in "${VERSIONS[@]}"; do + GHCR_IMAGE="$CONFIG_IMAGE:$CONFIG_VERSION" + DOCKER_IO_IMAGE=$(echo "$GHCR_IMAGE" | sed 's|ghcr\.io\/||') + echo "Push: $GHCR_IMAGE -> $DOCKER_IO_IMAGE" + docker pull $GHCR_IMAGE + docker tag $GHCR_IMAGE $DOCKER_IO_IMAGE + docker push $DOCKER_IO_IMAGE + done From c48e6832b93ba4c1c52c6ba23459e602e0384114 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:19:52 +0100 Subject: [PATCH 3/3] fix: update ha builder version --- .github/workflows/builder.yaml | 2 +- .github/workflows/manual-builder.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index 47058c07e..1be8ae567 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -110,7 +110,7 @@ jobs: - name: Build ${{ matrix.addon }} add-on if: steps.check.outputs.build_arch == 'true' - uses: home-assistant/builder@2025.09.0 + uses: home-assistant/builder@2025.11.0 with: args: | ${{ env.BUILD_ARGS }} \ diff --git a/.github/workflows/manual-builder.yaml b/.github/workflows/manual-builder.yaml index 7b06891e6..99fca6cef 100644 --- a/.github/workflows/manual-builder.yaml +++ b/.github/workflows/manual-builder.yaml @@ -59,7 +59,7 @@ jobs: - name: Build ${{ inputs.addon_folder }} add-on if: steps.check.outputs.build_arch == 'true' - uses: home-assistant/builder@2025.09.0 + uses: home-assistant/builder@2025.11.0 with: args: | --${{ matrix.arch }} \