diff --git a/FILE_MANIFEST.sha256 b/FILE_MANIFEST.sha256 index 3a78f22..51070cd 100644 --- a/FILE_MANIFEST.sha256 +++ b/FILE_MANIFEST.sha256 @@ -1,13 +1,37 @@ -d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example +80285335976065f05dd0cc3ad6006f0f5f6c8cc917dbaea9a10ad23f49df09f0 ./.env.example a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore 36a2cb85329ae82d097a335a6afd81c1ba7bc450eebdac09c600e12befe36ba2 ./Cargo.lock 5f9afa17638997837880ab244301e3921a6444972154b49797a73bc3103fbcad ./Cargo.toml 19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE -a45da91ab1d81a24ddfd561b3ae0c3b0300b57996ddb3df1f0b827ebbd488e87 ./README.md +d1418eefca2226e2bbd2a6f710f0d94bb3955ef81585b81c9681bb1dda5a7256 ./README.md bb549919b96b0413152dd905aec8640bb53301f07ec7d0b1be48ccbd37936fef ./build.rs d590a6df7ec31c274a2437075d76305cfafbe56385c800fbf39b18b54875119e ./docs/API.md 2e1e18fd8167dabfe2469c26e85cce62486c7cb6a502c63c6f6b0cd74d5885e0 ./docs/FLOW.md 3e995a5bb3ec40b26c2676818f90adbd5fa42e760b487d18a16b09a586702484 ./docs/openapi.json +e05c3dbc72aa2e2d5dfab2f9be713ee91ba6e69a6baddf9d44b9cda036d09637 ./ha-addon/.env.example +a18d2460340264d34c267fa704ee51a42e59bcc1783ee4c0dbb2a8ef42fa0f16 ./ha-addon/Dockerfile +0d7f1c5e026465b494f749ae7c4c26a0540b7b04288afa6b948ca7ad0134cb33 ./ha-addon/NETWORKING.en.md +3dcbc9ea27cd35425d7155cc3fc4705aee976f888bb157c7e1f88bfe2c8a6047 ./ha-addon/NETWORKING.md +ab7bfcde65d28e76e544ab906693e44b929332f77e10cb79f90262f0956719b4 ./ha-addon/README.md +4d55b7bb6c3aa564b2c9113fadd8878e3a39ecb7b301eadc624f16dd94107831 ./ha-addon/build.sh +62d0e3657432b727dfb993198a68a6cefba3c0f85026e9a3b01cd4e488f4e1f6 ./ha-addon/docs/README.en.md +ff4a5d68d3cd120d81f53f441cd8208af8e99c0986c28ccf7140524a91342d1a ./ha-addon/docs/README.md +79dffc634bacb0008b5d16dc6d490afb6fb80e870d424567e3dcbccf9d1083fb ./ha-addon/docs/README.pl.md +4c2f8c74bcef72883affe782283bea837d6ca7729b9789c9804a6e7b2c124bb1 ./ha-addon/docs/topologia-ha-vlan-gree-pl.png +8ee0343c78279271c0a3f580647c225be470c3679064e77d736566609c753098 ./ha-addon/docs/topology-ha-vlan-gree-en.png +7ffafcc102b997b013ce36228b39474916a87b41ade80274edabb0e6d04afcb0 ./ha-addon/render-repository.sh +7018d71adc77a53d2881672c1e452952e1c1269f5aa7d7dd78fcec0f2977efc1 ./ha-addon/repository/gree-controller/CHANGELOG.md +a9f0b5a9069de8502158f90414713be72b300b784e35c2bde37c3f597e484231 ./ha-addon/repository/gree-controller/DOCS.md +fc9039c440063e48f120f49e5166872d09625da9ef2cf3ca35589496f5a643f8 ./ha-addon/repository/gree-controller/DOCS.pl.md +e4100a2c902c50f51898150340defb84491f359ff8ee3a49fdc9327192173c5f ./ha-addon/repository/gree-controller/README.md +827dfc48c1b2a0dad1c20c9b5c3c5c55d46610901e85455ee7d4cf0c273b7d4e ./ha-addon/repository/gree-controller/config.yaml +4c2f8c74bcef72883affe782283bea837d6ca7729b9789c9804a6e7b2c124bb1 ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png +8ee0343c78279271c0a3f580647c225be470c3679064e77d736566609c753098 ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png +d13e4c2b993d209078342c0c86d852b300692089b377aa044f811308dd188a1e ./ha-addon/repository/gree-controller/translations/en.yaml +9cf50879d7ea32cd3ff7ee95a6d50c4f1eab727420bc6610282931a9117e7dde ./ha-addon/repository/gree-controller/translations/pl.yaml +ebcfb7dfb69649aa86196c19025798b3d4661691b04f4487a7c3d9f00976f2ed ./ha-addon/repository/repository.yaml +c123b190219556382f0e7f3058056fb233f6261f257a01eceb96a2c0bbfa12f3 ./ha-addon/run.sh +e5642010dd44500d23214ae26dcdcddf1a4d6537b4ce458b13a33bdf95ccebd1 ./ha_addon.md b11e581dd916676e24402112fe6896bc681abf3c94cafc85c4d3ded3f3a272a3 ./home-assistant/README.md f8e8559fe10fe523ac5bc9aac25c6e26e862f679d502e8f3c39f38a0a8e40911 ./home-assistant/custom_components/gree_controller/__init__.py 3f6ef15ef58456376ac53fde7cace1ef359d6a6f7a64c5b575ad77ea6e55ccc4 ./home-assistant/custom_components/gree_controller/api.py @@ -83,7 +107,7 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw 472db31959be29ba99247f7e9cc07a44bc090c538cd654ad8d16e094726037a3 ./scripts/smoke.sh b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh 0da4e4fcb8e77109fd56b301472d08acbab5ad9ecfe861296153a6a5eef723b3 ./src/api.rs -549941b91d9f9bb829f9f64345866a1a76eebcb323d15806d9d11545566dfe6b ./src/api/assets.rs +3dfd1e053cbfa0d684dd88cf99b244a96a36e2ff23d2a0715b8327ab62258254 ./src/api/assets.rs 05c255919f7b3d81188fc5f1410be6a0037471dfc44e93bffb009f29a1e6a3fc ./src/api/auth.rs 9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs 368cd843dcac970f1aa6800ba132a4bbc3429e8379ed7abf9878dcb0c633af40 ./src/api/configuration.rs @@ -160,7 +184,7 @@ d4df52f0202eb32290e68dc945c4b0b44f3be8aa90bd57fd116eb20285960616 ./src/protocol 4e419ba3b86ca9d154e8c04445cf005965175c54041093b2daad3666d4d852bd ./src/protocol/gree/core.rs 322bcb70016653f8bf71cc620020aa87d4f3f56b4d800ee9425a95b1bcc67d2e ./src/protocol/gree/discovery.rs 0ab094f609edb99715402d8eeaa7e38cbfd2d271036aec787dcbb3be3e005720 ./src/protocol/gree/merge.rs -a96061fbbcadac6c2df2e9708a48dcf819b951eb147f32c7d0d285ac5ae2491b ./src/protocol/gree/network.rs +6c18d870f98525e87ba821d69424e6b19a22b2c06106b7fd657f30912cd2705f ./src/protocol/gree/network.rs de37931303fc92a9399e6edc17dfabd6870a38cd2ddb27257427c76768a66ec2 ./src/protocol/gree/polling.rs c868422c02ba457df21ae92acbd00698c243855ddd1d4e95ac2166796eba9a44 ./src/protocol/gree/tests.rs 3c93e5b254a2a40a07854532cb5fc07e493ad65d9ba2cf28093cff4a4a4e451c ./src/protocol/gree/transport.rs @@ -187,7 +211,7 @@ d880ffdac6aa07cd10d79fbacf12f87eb4c69b48a80564bbfdd0248abe8884d5 ./web/js/dashb 2e822a00b5a6941b6a251eaf2c54ca95a53399b5d1056f3d8240e11f82d6065f ./web/js/flows.js cf1ebf3b9d666f309a1add070b4da86be86d990b02c8d6474f6d121c6d332746 ./web/js/forms.js dbf36223863ba882c03eccea4516ba8db7acf0c2e72c6c64490cb994f1e20f96 ./web/js/history.js -e934dbe7810da4565a4474338955b044448f3a268160e55bf98afefa4b2d97de ./web/js/main.js +0dc879c0056251618633c9efd188fc1593eeae6965ad1bc993132dea6e171b44 ./web/js/main.js 733fcc595ed9c03c9639acc6caba46f0588f29e0b95bf72891e34443e7aa9db8 ./web/js/navigation.js e0a6b5d2900f5524dba395b97288f8d75976e31b27e26810d42febbfd723085c ./web/js/realtime.js c8c82e88c3715b1bfabf155e36266a4c94f5e2fc03eb39dcdd9d08a1985a997c ./web/js/router.js diff --git a/README.md b/README.md index fd966cd..21ce682 100644 --- a/README.md +++ b/README.md @@ -170,8 +170,8 @@ The controller will use that interface for GREE UDP traffic while keeping the HT Diagnostics: ```bash -sudo ./scripts/configure-gree-network.sh -./scripts/network-debug.sh +sudo ./scripts/configure-gree-network.sh eth1 +./scripts/network-debug.sh eth1 ``` The hardened systemd unit allows `AF_NETLINK`, which is required for interface discovery on multi-NIC Linux/LXC systems. diff --git a/ha-addon/.env.example b/ha-addon/.env.example new file mode 100644 index 0000000..0413a9c --- /dev/null +++ b/ha-addon/.env.example @@ -0,0 +1,13 @@ +# OCI registry. Works with standard OCI registries, including Zot. +REGISTRY=oci.linuxiarz.pl +IMAGE_NAME=gree-controller + +# Build/runtime base: trixie or alpine. +DISTRO=trixie + +# Buildx builder name. +BUILDER=gree-controller-multiarch + +# Optional BuildKit config for a private registry with custom CA / HTTP. +# Example: /etc/buildkit/buildkitd.toml +BUILDER_CONFIG= diff --git a/ha-addon/Dockerfile b/ha-addon/Dockerfile new file mode 100644 index 0000000..e79b93e --- /dev/null +++ b/ha-addon/Dockerfile @@ -0,0 +1,64 @@ +# syntax=docker/dockerfile:1.7 +ARG RUST_VERSION=1.89 + +FROM --platform=$TARGETPLATFORM rust:${RUST_VERSION}-trixie AS builder-trixie +WORKDIR /src +RUN apt-get update \ + && apt-get install -y --no-install-recommends build-essential pkg-config \ + && rm -rf /var/lib/apt/lists/* +COPY Cargo.toml Cargo.lock build.rs ./ +COPY src ./src +COPY web ./web +COPY lang ./lang +COPY presets ./presets +COPY docs ./docs +RUN cargo build --locked --release + +FROM --platform=$TARGETPLATFORM rust:${RUST_VERSION}-alpine AS builder-alpine +WORKDIR /src +RUN apk add --no-cache build-base pkgconfig musl-dev +COPY Cargo.toml Cargo.lock build.rs ./ +COPY src ./src +COPY web ./web +COPY lang ./lang +COPY presets ./presets +COPY docs ./docs +RUN cargo build --locked --release + +FROM --platform=$TARGETPLATFORM debian:trixie-slim AS runtime-trixie +ARG BUILD_VERSION=dev +ARG TARGETARCH +LABEL io.hass.version="${BUILD_VERSION}" \ + io.hass.type="app" \ + org.opencontainers.image.base.name="debian:trixie-slim" \ + org.opencontainers.image.description="GREE Controller Home Assistant add-on" \ + org.opencontainers.image.vendor="MateuszG" \ + org.opencontainers.image.title="GREE Controller" \ + org.opencontainers.image.version="${BUILD_VERSION}" \ + org.opencontainers.image.architecture="${TARGETARCH}" +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates tzdata jq iproute2 \ + && rm -rf /var/lib/apt/lists/* +COPY --from=builder-trixie /src/target/release/gree-controller /usr/local/bin/gree-controller +COPY ha-addon/run.sh /usr/local/bin/run.sh +RUN chmod 0755 /usr/local/bin/run.sh /usr/local/bin/gree-controller +EXPOSE 8787/tcp +ENTRYPOINT ["/usr/local/bin/run.sh"] + +FROM --platform=$TARGETPLATFORM alpine:latest AS runtime-alpine +ARG BUILD_VERSION=dev +ARG TARGETARCH +LABEL io.hass.version="${BUILD_VERSION}" \ + io.hass.type="app" \ + org.opencontainers.image.base.name="alpine:latest" \ + org.opencontainers.image.description="GREE Controller Home Assistant add-on" \ + org.opencontainers.image.vendor="MateuszG" \ + org.opencontainers.image.title="GREE Controller" \ + org.opencontainers.image.version="${BUILD_VERSION}" \ + org.opencontainers.image.architecture="${TARGETARCH}" +RUN apk add --no-cache ca-certificates tzdata jq iproute2 +COPY --from=builder-alpine /src/target/release/gree-controller /usr/local/bin/gree-controller +COPY ha-addon/run.sh /usr/local/bin/run.sh +RUN chmod 0755 /usr/local/bin/run.sh /usr/local/bin/gree-controller +EXPOSE 8787/tcp +ENTRYPOINT ["/usr/local/bin/run.sh"] diff --git a/ha-addon/NETWORKING.en.md b/ha-addon/NETWORKING.en.md new file mode 100644 index 0000000..212bff0 --- /dev/null +++ b/ha-addon/NETWORKING.en.md @@ -0,0 +1,32 @@ +# Home Assistant add-on: networking, VLANs, and multiple interfaces + +This directory contains the full set of materials related to running GREE Controller as a Home Assistant add-on: + +- multi-arch build (`build.sh`) +- Dockerfile targets for `trixie` and `alpine` +- add-on repository (`https://git.linuxiarz.pl/gru/gree-controller-ha-addon/`) +- networking documentation (`https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.md`, `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.pl.md`) +- topology diagrams (`docs/topology-ha-vlan-gree-en.png`, `docs/topologia-ha-vlan-gree-pl.png`) + +## Quick conclusion + +For a Home Assistant add-on, the recommended model is: + +- `host_network: true` +- configure VLANs and interfaces on the HA OS host +- the add-on reuses the host interfaces +- do not use `macvlan` inside the add-on + +## Where the diagrams are + +- `docs/topology-ha-vlan-gree-en.png` - English version +- `docs/topologia-ha-vlan-gree-pl.png` - Polish version +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/topology-ha-vlan-gree-en.png` - repository copy EN +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/topologia-ha-vlan-gree-pl.png` - repository copy PL + +## Where the docs are + +- `docs/README.en.md` +- `docs/README.pl.md` +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.md` +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.pl.md` diff --git a/ha-addon/NETWORKING.md b/ha-addon/NETWORKING.md new file mode 100644 index 0000000..8e2f240 --- /dev/null +++ b/ha-addon/NETWORKING.md @@ -0,0 +1,32 @@ +# Home Assistant add-on: sieć, VLAN-y i wiele interfejsów + +Ten katalog zawiera komplet materiałów związanych z uruchomieniem GREE Controller jako dodatku Home Assistant: + +- build multi-arch (`build.sh`) +- Dockerfile z targetami `trixie` i `alpine` +- repozytorium dodatku (`https://git.linuxiarz.pl/gru/gree-controller-ha-addon/`) +- dokumentację sieci (`https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.pl.md`, `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.md`) +- obrazy topologii sieci (`docs/topologia-ha-vlan-gree-pl.png`, `docs/topology-ha-vlan-gree-en.png`) + +## Szybki wniosek + +Dla Home Assistant Add-on zalecany jest model: + +- `host_network: true` +- VLAN-y i interfejsy konfigurujesz na hoście HA OS +- dodatek korzysta z interfejsów hosta +- nie używamy `macvlan` w dodatku + +## Gdzie są obrazki + +- `docs/topologia-ha-vlan-gree-pl.png` - wersja polska +- `docs/topology-ha-vlan-gree-en.png` - wersja angielska +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/topologia-ha-vlan-gree-pl.png` - kopia publikacyjna PL +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/topology-ha-vlan-gree-en.png` - kopia publikacyjna EN + +## Gdzie jest opis + +- `docs/README.pl.md` +- `docs/README.en.md` +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.pl.md` +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.md` diff --git a/ha-addon/README.md b/ha-addon/README.md new file mode 100644 index 0000000..7c5426c --- /dev/null +++ b/ha-addon/README.md @@ -0,0 +1,116 @@ +# GREE Controller - Home Assistant add-on + +Everything related to the Home Assistant add-on lives in this directory: build, OCI publishing, custom repository, VLAN/multi-interface docs and diagrams. + +## Topology / topologia + +### PL + +![Topologia Home Assistant OS + GREE Controller + VLAN](./docs/topologia-ha-vlan-gree-pl.png) + +### EN + +![Home Assistant OS + GREE Controller + VLAN topology](./docs/topology-ha-vlan-gree-en.png) + +## Minimal versioning + +There is only **one manually maintained version**: `package.version` in the project root `Cargo.toml`. + +```toml +[package] +version = "X.Y.Z" +``` + +Do not set a version in `.env` and do not create architecture-specific release tags. `./build.sh` automatically: + +1. reads the version from `Cargo.toml`, +2. synchronizes the root package entry in `Cargo.lock`, +3. writes the same version to `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/config.yaml`, +4. builds `linux/amd64` and `linux/arm64`, +5. publishes one multi-arch OCI manifest: `REGISTRY/IMAGE_NAME:`. + +So a release version is changed in one place only. + +## Configure build + +```bash +cd ha-addon +cp .env.example .env +$EDITOR .env +``` + +Example: + +```dotenv +REGISTRY=zot.linuxiarz.pl +IMAGE_NAME=gree-controller +DISTRO=trixie +``` + +Supported base images: + +- `DISTRO=trixie` - Debian Trixie builder + `debian:trixie-slim` runtime +- `DISTRO=alpine` - latest Alpine builder + runtime + +Registry authentication, when required: + +```bash +docker login zot.linuxiarz.pl +``` + +## Build and publish + +Publish one multi-arch image for Home Assistant: + +```bash +./build.sh push +``` + +Result: + +```text +zot.linuxiarz.pl/gree-controller:X.Y.Z + ├─ linux/amd64 + └─ linux/arm64 +``` + +No `-amd64`, `-aarch64`, `-trixie`, `-alpine` or `latest` release tags are required. + +Local single-architecture test: + +```bash +./build.sh load amd64 +# or +./build.sh load aarch64 +``` + +The local test image is always tagged `REGISTRY/IMAGE_NAME:local`. + +Only refresh Home Assistant repository metadata: + +```bash +./build.sh render +``` + +## Home Assistant network model + +The add-on uses `host_network: true`. VLANs and physical/logical interfaces are configured on Home Assistant OS, not created as Docker `macvlan` networks inside the add-on. + +Recommended model: + +- configure NICs/VLANs on HA OS, +- let the add-on share host networking, +- set `gree_interface` to a specific host interface/IP for one GREE subnet, +- leave `gree_interface` empty for automatic per-device routing across several directly attached subnets, +- run UDP broadcast discovery separately for each VLAN/subnet. + +Detailed documentation: + +- `NETWORKING.md` - PL +- `NETWORKING.en.md` - EN +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.pl.md` - PL add-on docs +- `https://git.linuxiarz.pl/gru/gree-controller-ha-addon/gree-controller/DOCS.md` - EN add-on docs + +## Publish the custom repository + +Publish `ha-addon/repository/` as a Git repository and add its URL in the Home Assistant add-on/app store as a custom repository. The generated `config.yaml` points Home Assistant at the same OCI repository and version taken from `Cargo.toml`. diff --git a/ha-addon/build.sh b/ha-addon/build.sh new file mode 100755 index 0000000..dbb47ec --- /dev/null +++ b/ha-addon/build.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +if [[ -f "$SCRIPT_DIR/.env" ]]; then + set -a + # shellcheck disable=SC1091 + source "$SCRIPT_DIR/.env" + set +a +fi + +REGISTRY="${REGISTRY:-oci.linuxiarz.pl}" +IMAGE_NAME="${IMAGE_NAME:-gree-controller}" +DISTRO="${DISTRO:-trixie}" +BUILDER="${BUILDER:-gree-controller-multiarch}" +BUILDER_CONFIG="${BUILDER_CONFIG:-}" +ACTION="${1:-push}" + +VERSION="$(awk -F '"' '/^version = "/ {print $2; exit}' "$PROJECT_ROOT/Cargo.toml")" +[[ -n "$VERSION" ]] || { echo "Cannot determine version from Cargo.toml" >&2; exit 1; } + +case "$DISTRO" in + trixie|alpine) ;; + *) echo "DISTRO must be one of: trixie, alpine" >&2; exit 2 ;; +esac + +IMAGE_REPO="${REGISTRY%/}/${IMAGE_NAME#/}" +IMAGE_TAG="${IMAGE_REPO}:${VERSION}" + +"$SCRIPT_DIR/render-repository.sh" + +if [[ "$ACTION" == "render" ]]; then + exit 0 +fi + +command -v docker >/dev/null 2>&1 || { echo "docker is required" >&2; exit 1; } +docker buildx version >/dev/null 2>&1 || { echo "docker buildx is required" >&2; exit 1; } + +ensure_builder() { + if docker buildx inspect "$BUILDER" >/dev/null 2>&1; then + docker buildx use "$BUILDER" + else + args=(create --name "$BUILDER" --driver docker-container --use) + if [[ -n "$BUILDER_CONFIG" ]]; then + args+=(--config "$BUILDER_CONFIG") + fi + docker buildx "${args[@]}" >/dev/null + fi + docker buildx inspect --bootstrap >/dev/null +} + +ensure_builder + +case "$ACTION" in + push) + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -f "$SCRIPT_DIR/Dockerfile" \ + --target "runtime-${DISTRO}" \ + --build-arg "BUILD_VERSION=$VERSION" \ + -t "$IMAGE_TAG" \ + --push \ + "$PROJECT_ROOT" + printf 'Published %s using %s (amd64 + aarch64)\n' "$IMAGE_TAG" "$DISTRO" + ;; + load) + arch="${2:-amd64}" + case "$arch" in + amd64) platform="linux/amd64" ;; + aarch64|arm64) platform="linux/arm64" ;; + *) echo "Usage: $0 load [amd64|aarch64]" >&2; exit 2 ;; + esac + docker buildx build \ + --platform "$platform" \ + -f "$SCRIPT_DIR/Dockerfile" \ + --target "runtime-${DISTRO}" \ + --build-arg "BUILD_VERSION=$VERSION" \ + -t "${IMAGE_REPO}:local" \ + --load \ + "$PROJECT_ROOT" + printf 'Loaded %s:local for %s using %s\n' "$IMAGE_REPO" "$arch" "$DISTRO" + ;; + *) + echo "Usage: $0 [push|load [amd64|aarch64]|render]" >&2 + exit 2 + ;; +esac diff --git a/ha-addon/docs/README.en.md b/ha-addon/docs/README.en.md new file mode 100644 index 0000000..e55516a --- /dev/null +++ b/ha-addon/docs/README.en.md @@ -0,0 +1,21 @@ +# Home Assistant add-on - networking and VLAN + +This directory contains topology materials for Home Assistant OS, VLANs, and multiple network interfaces for GREE Controller. + +## EN diagram + +![Home Assistant OS + VLAN + GREE topology](./topology-ha-vlan-gree-en.png) + +## PL diagram + +![Topologia HA OS + VLAN + GREE](./topologia-ha-vlan-gree-pl.png) + +## Network model + +- VLANs and IP addresses are configured on the Home Assistant OS host. +- The add-on runs with `host_network: true`, so it uses the host interfaces directly. +- There is no need to create `macvlan` inside the add-on. +- For several directly attached subnets you can leave `gree_interface` empty so the controller picks the interface that best matches the device IP. +- UDP broadcast discovery happens per subnet/VLAN; broadcast usually does not cross a router. + +Full details: `../NETWORKING.en.md` and `../repository/gree-controller/DOCS.md`. diff --git a/ha-addon/docs/README.md b/ha-addon/docs/README.md new file mode 100644 index 0000000..e0b5714 --- /dev/null +++ b/ha-addon/docs/README.md @@ -0,0 +1,16 @@ +# Home Assistant add-on - diagrams and VLAN notes + +This directory stores the Home Assistant add-on topology diagrams in both languages. + +- [Polish notes](./README.pl.md) +- [English notes](./README.en.md) + +## Diagrams + +### Polish + +![Topologia HA OS + VLAN + GREE](./topologia-ha-vlan-gree-pl.png) + +### English + +![Home Assistant OS + VLAN + GREE topology](./topology-ha-vlan-gree-en.png) diff --git a/ha-addon/docs/README.pl.md b/ha-addon/docs/README.pl.md new file mode 100644 index 0000000..697070d --- /dev/null +++ b/ha-addon/docs/README.pl.md @@ -0,0 +1,21 @@ +# Home Assistant add-on - sieć i VLAN + +Ten katalog zawiera materiały dotyczące topologii Home Assistant OS, VLAN-ów i wielu interfejsów sieciowych dla GREE Controller. + +## Diagram PL + +![Topologia HA OS + VLAN + GREE](./topologia-ha-vlan-gree-pl.png) + +## Diagram EN + +![Home Assistant OS + VLAN + GREE topology](./topology-ha-vlan-gree-en.png) + +## Model sieciowy + +- VLAN-y oraz adresy IP są konfigurowane na hoście Home Assistant OS. +- Add-on działa z `host_network: true`, więc korzysta bezpośrednio z interfejsów hosta. +- Nie ma potrzeby tworzenia `macvlan` wewnątrz dodatku. +- Dla kilku bezpośrednio podłączonych podsieci `gree_interface` można pozostawić puste, aby kontroler dobierał interfejs do adresu urządzenia. +- Discovery UDP broadcast wykonuje się per podsieć/VLAN; broadcast zwykle nie przechodzi przez router. + +Pełny opis: `../NETWORKING.md` oraz `../repository/gree-controller/DOCS.pl.md`. diff --git a/ha-addon/docs/topologia-ha-vlan-gree-pl.png b/ha-addon/docs/topologia-ha-vlan-gree-pl.png new file mode 100644 index 0000000..99eb488 Binary files /dev/null and b/ha-addon/docs/topologia-ha-vlan-gree-pl.png differ diff --git a/ha-addon/docs/topologia-ha-vlan-gree.png b/ha-addon/docs/topologia-ha-vlan-gree.png new file mode 100644 index 0000000..95c7fd3 Binary files /dev/null and b/ha-addon/docs/topologia-ha-vlan-gree.png differ diff --git a/ha-addon/docs/topology-ha-vlan-gree-en.png b/ha-addon/docs/topology-ha-vlan-gree-en.png new file mode 100644 index 0000000..95c7fd3 Binary files /dev/null and b/ha-addon/docs/topology-ha-vlan-gree-en.png differ diff --git a/ha-addon/publish-repository.sh b/ha-addon/publish-repository.sh new file mode 100755 index 0000000..280fc68 --- /dev/null +++ b/ha-addon/publish-repository.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +if [[ -f "$SCRIPT_DIR/.env" ]]; then + set -a + # shellcheck disable=SC1091 + source "$SCRIPT_DIR/.env" + set +a +fi + +HA_REPO_PATH="${HA_REPO_PATH:-../gree-controller-ha-addon}" +HA_REPO_BRANCH="${HA_REPO_BRANCH:-master}" + +if [[ "$HA_REPO_PATH" != /* ]]; then + HA_REPO_PATH="$PROJECT_ROOT/$HA_REPO_PATH" +fi + +HA_REPO_PATH="$(realpath -m "$HA_REPO_PATH")" + +VERSION="$( + awk -F '"' '/^version = "/ {print $2; exit}' \ + "$PROJECT_ROOT/Cargo.toml" +)" + +[[ -n "$VERSION" ]] || { + echo "Cannot determine version from Cargo.toml" >&2 + exit 1 +} + +command -v git >/dev/null 2>&1 || { + echo "git is required" >&2 + exit 1 +} + +command -v rsync >/dev/null 2>&1 || { + echo "rsync is required" >&2 + exit 1 +} + +[[ -d "$HA_REPO_PATH/.git" ]] || { + echo "HA_REPO_PATH is not a Git repository: $HA_REPO_PATH" >&2 + exit 1 +} + +echo "==> Building and pushing OCI image ${VERSION}" +"$SCRIPT_DIR/build.sh" push + +echo "==> Syncing Home Assistant repository" +rsync -a --delete \ + --exclude='.git/' \ + "$SCRIPT_DIR/repository/" \ + "$HA_REPO_PATH/" + +cd "$HA_REPO_PATH" + +echo "==> Updating branch ${HA_REPO_BRANCH}" +git checkout "$HA_REPO_BRANCH" + +git add -A + +if git diff --cached --quiet; then + echo "No Home Assistant repository changes to publish." + exit 0 +fi + +echo "==> Committing Home Assistant add-on ${VERSION}" +git commit -m "GREE Controller ${VERSION}" + +echo "==> Pushing to Gitea" +git push origin "$HA_REPO_BRANCH" + +echo +echo "Published:" +echo " image version: ${VERSION}" +echo " HA repository: ${HA_REPO_PATH}" +echo " branch: ${HA_REPO_BRANCH}" \ No newline at end of file diff --git a/ha-addon/render-repository.sh b/ha-addon/render-repository.sh new file mode 100755 index 0000000..2970dc8 --- /dev/null +++ b/ha-addon/render-repository.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +CONFIG="$SCRIPT_DIR/repository/gree-controller/config.yaml" +LOCKFILE="$PROJECT_ROOT/Cargo.lock" + +REGISTRY="${REGISTRY:-oci.linuxiarz.pl}" +IMAGE_NAME="${IMAGE_NAME:-gree-controller}" +VERSION="$(awk -F '"' '/^version = "/ {print $2; exit}' "$PROJECT_ROOT/Cargo.toml")" +[[ -n "$VERSION" ]] || { echo "Cannot determine version from Cargo.toml" >&2; exit 1; } + +IMAGE_REPO="${REGISTRY%/}/${IMAGE_NAME#/}" + +python3 - "$CONFIG" "$LOCKFILE" "$VERSION" "$IMAGE_REPO" <<'PY2' +from pathlib import Path +import re +import sys + +config = Path(sys.argv[1]) +lockfile = Path(sys.argv[2]) +version = sys.argv[3] +image = sys.argv[4] + +# Home Assistant metadata is generated from the single version source: Cargo.toml. +lines = config.read_text(encoding="utf-8").splitlines() +out = [] +for line in lines: + if line.startswith("version:"): + out.append(f'version: "{version}"') + elif line.startswith("image:"): + out.append(f'image: "{image}"') + else: + out.append(line) +config.write_text("\n".join(out) + "\n", encoding="utf-8") + +# Keep Cargo.lock in sync so `cargo build --locked` remains usable after a version bump. +lock = lockfile.read_text(encoding="utf-8") +pattern = r'(\[\[package\]\]\nname = "gree-controller"\nversion = ")[^"]+("\n)' +updated, count = re.subn(pattern, rf'\g<1>{version}\g<2>', lock, count=1) +if count != 1: + raise SystemExit("Could not find gree-controller package version in Cargo.lock") +lockfile.write_text(updated, encoding="utf-8") +PY2 + +printf 'Synced version %s -> Cargo.lock + Home Assistant metadata; image %s:%s\n' \ + "$VERSION" "$IMAGE_REPO" "$VERSION" diff --git a/ha-addon/repository/gree-controller/CHANGELOG.md b/ha-addon/repository/gree-controller/CHANGELOG.md new file mode 100644 index 0000000..5540c42 --- /dev/null +++ b/ha-addon/repository/gree-controller/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +Release version is maintained only in the project root `Cargo.toml`. Home Assistant metadata is generated by `ha-addon/build.sh` / `ha-addon/render-repository.sh`. + +## Current + +- Home Assistant add-on packaging. +- amd64 and aarch64 in one OCI multi-arch manifest. +- Host-network operation for GREE UDP discovery and multi-interface hosts. +- Home Assistant ingress support. +- Polish and English topology diagrams and networking documentation. +- Debian Trixie or latest Alpine build/runtime target. diff --git a/ha-addon/repository/gree-controller/DOCS.md b/ha-addon/repository/gree-controller/DOCS.md new file mode 100644 index 0000000..a312f1a --- /dev/null +++ b/ha-addon/repository/gree-controller/DOCS.md @@ -0,0 +1,65 @@ +# GREE Controller - Home Assistant add-on + +## Installation + +Publish the `ha-addon/repository` directory as a Git repository. In Home Assistant open the app/add-on store, add that Git repository URL as a custom repository, refresh the store and install **GREE Controller**. + +The repository metadata points to a pre-built multi-architecture OCI image. The image tag must be identical to `version` in `config.yaml`. + +## Topology diagrams + +### English + +![Home Assistant OS + GREE Controller + VLAN topology](https://git.linuxiarz.pl/gru/gree-controller-ha-addon/media/branch/master/gree-controller/topology-ha-vlan-gree-en.png) + +### Polish + +![Topologia Home Assistant OS + GREE Controller + VLAN](https://git.linuxiarz.pl/gru/gree-controller-ha-addon/raw/branch/master/gree-controller/topologia-ha-vlan-gree-pl.png) + + +## Network model + +The add-on deliberately uses `host_network: true`. Do not attach Docker `macvlan` networks to the add-on. With host networking, the controller shares the Home Assistant host network namespace and sees host ethernet/VLAN interfaces directly. + +Configure physical NICs and VLANs on Home Assistant OS first. Check the current host interfaces with: + +```bash +ha network info +``` + +Example VLAN 50 on `eth0`, without adding another default gateway: + +```bash +ha network vlan eth0 50 \ + --ipv4-method static \ + --ipv4-address 192.168.50.2/24 \ + --ipv6-method disabled +``` + +Then use one of these add-on configurations: + +### One dedicated GREE interface + +Set `gree_interface` to the host interface name, for example `eth0.50`, or to its local IPv4 address, for example `192.168.50.2`. Set `discovery_broadcast` to the subnet broadcast, for example `192.168.50.255:7000`. + +### Several host interfaces / several directly attached subnets + +Leave `gree_interface` empty. For already known devices the controller chooses the local IPv4 interface whose subnet best matches the device IP. This allows normal polling and commands to devices on different directly attached networks. + +Discovery itself sends one broadcast target per scan. To discover devices on several VLANs, scan each subnet separately by supplying its broadcast address (for example `192.168.50.255:7000`, then `192.168.60.255:7000`) or add known devices manually. After discovery, normal traffic can use automatic per-target interface selection. + +Do not use `discovery_broadcast=auto` when `gree_interface` is empty; automatic broadcast calculation needs an explicitly selected interface. + +## Routed VLANs + +Unicast UDP commands can traverse routing/firewall rules if permitted. Discovery is broadcast-based and normally does not cross a router. Either give the Home Assistant host an interface in the GREE VLAN, use a suitable UDP broadcast relay, or discover/add devices from each local subnet explicitly. + +## Docker limitations + +Home Assistant add-on configuration supports host networking, but not arbitrary user-defined Docker networks/macvlan attachments. Giving an add-on Docker API/full host access just to create extra container interfaces is unnecessary here and would substantially weaken isolation. Configure the VLANs/NICs on the HA OS host and let this add-on share them through host networking. + +## Direct access and ingress + +The service listens on TCP 8787 because the same host network is needed for GREE UDP. Home Assistant ingress proxies the Web UI to that port. If TCP 8787 is reachable from untrusted networks, configure `app_token` or block that port at the network/firewall boundary. + +The standalone Home Assistant custom integration can still use the controller API exactly as before; point it at an address that Home Assistant Core can reach and use an integration token generated by GREE Controller. diff --git a/ha-addon/repository/gree-controller/DOCS.pl.md b/ha-addon/repository/gree-controller/DOCS.pl.md new file mode 100644 index 0000000..dc346a9 --- /dev/null +++ b/ha-addon/repository/gree-controller/DOCS.pl.md @@ -0,0 +1,65 @@ +# GREE Controller - dodatek Home Assistant + +## Instalacja + +Opublikuj katalog `ha-addon/repository` jako repozytorium Git. W Home Assistant otwórz sklep dodatków/aplikacji, dodaj URL tego repozytorium jako repozytorium niestandardowe, odśwież listę i zainstaluj **GREE Controller**. + +Metadane repozytorium wskazują na gotowy wieloarchitekturny obraz OCI. Tag obrazu musi być taki sam jak `version` w `config.yaml`. + +## Diagramy topologii + +### Polski + +![Topologia Home Assistant OS + GREE Controller + VLAN](https://git.linuxiarz.pl/gru/gree-controller-ha-addon/raw/branch/master/gree-controller/topologia-ha-vlan-gree-pl.png) + +### English + + +![Home Assistant OS + GREE Controller + VLAN topology](https://git.linuxiarz.pl/gru/gree-controller-ha-addon/media/branch/master/gree-controller/topology-ha-vlan-gree-en.png) + +## Model sieciowy + +Dodatek celowo używa `host_network: true`. Nie podpinaj do dodatku sieci Docker `macvlan`. Przy host networking kontroler współdzieli przestrzeń sieciową hosta Home Assistanta i widzi bezpośrednio interfejsy ethernet/VLAN hosta. + +Najpierw skonfiguruj fizyczne NIC-i i VLAN-y w Home Assistant OS. Aktualne interfejsy hosta sprawdzisz poleceniem: + +```bash +ha network info +``` + +Przykład VLAN 50 na `eth0`, bez dodawania kolejnej domyślnej bramy: + +```bash +ha network vlan eth0 50 \ + --ipv4-method static \ + --ipv4-address 192.168.50.2/24 \ + --ipv6-method disabled +``` + +Następnie użyj jednej z tych konfiguracji dodatku: + +### Jeden dedykowany interfejs GREE + +Ustaw `gree_interface` na nazwę interfejsu hosta, np. `eth0.50`, albo na jego lokalny adres IPv4, np. `192.168.50.2`. Ustaw `discovery_broadcast` na broadcast tej podsieci, np. `192.168.50.255:7000`. + +### Kilka interfejsów hosta / kilka bezpośrednio podłączonych podsieci + +Pozostaw `gree_interface` puste. Dla znanych urządzeń kontroler wybiera lokalny interfejs IPv4, którego podsieć najlepiej pasuje do adresu IP urządzenia. Dzięki temu normalne odpytywanie i sterowanie działa dla urządzeń w różnych bezpośrednio podłączonych sieciach. + +Samo discovery wysyła jeden broadcast na skan. Aby wykrywać urządzenia w kilku VLAN-ach, skanuj każdą podsieć osobno, podając jej adres broadcast (np. `192.168.50.255:7000`, potem `192.168.60.255:7000`) albo dodaj znane urządzenia ręcznie. Po discovery zwykły ruch może działać z automatycznym doborem interfejsu dla celu. + +Nie używaj `discovery_broadcast=auto`, gdy `gree_interface` jest puste; automatyczne wyliczenie broadcastu wymaga jawnie wybranego interfejsu. + +## VLAN-y routowane + +Polecenia unicast UDP mogą przechodzić przez routing/reguły firewalla, jeśli jest to dozwolone. Discovery jest oparte na broadcast i zwykle nie przechodzi przez router. Albo daj hostowi Home Assistant interfejs w VLAN-ie GREE, albo użyj odpowiedniego relaya UDP broadcast, albo wykrywaj/dodawaj urządzenia osobno z każdej lokalnej podsieci. + +## Ograniczenia Dockera + +Konfiguracja dodatków Home Assistant wspiera host networking, ale nie dowolne, własne sieci Docker/macvlan przypinane do dodatku. Nadawanie dodatkowi pełnego dostępu do hosta lub API Dockera tylko po to, aby tworzyć dodatkowe interfejsy kontenera, nie jest tu potrzebne i znacząco osłabia izolację. Skonfiguruj VLAN-y/NIC-e na hoście HA OS i pozwól temu dodatkowi współdzielić je przez host networking. + +## Bezpośredni dostęp i ingress + +Usługa nasłuchuje na TCP 8787, ponieważ ten sam host network jest potrzebny dla GREE UDP. Home Assistant ingress proxy przekazuje Web UI na ten port. Jeżeli TCP 8787 jest osiągalny z niezaufanych sieci, skonfiguruj `app_token` albo zablokuj ten port na granicy sieci/firewalla. + +Samodzielna integracja Home Assistant może nadal używać API kontrolera jak wcześniej; wskaż adres osiągalny z Home Assistant Core i użyj tokenu integracji wygenerowanego przez GREE Controller. diff --git a/ha-addon/repository/gree-controller/README.md b/ha-addon/repository/gree-controller/README.md new file mode 100644 index 0000000..b601e7c --- /dev/null +++ b/ha-addon/repository/gree-controller/README.md @@ -0,0 +1,35 @@ +# GREE Controller + +Local GREE HVAC controller packaged as a Home Assistant add-on. It uses `host_network: true` so UDP discovery and the host VLAN/ethernet interfaces are directly available to the controller. Persistent data is stored in `/data/gree-controller.db` and is included in Home Assistant backups. + +## PL - topologia HA / VLAN / GREE + +![Topologia Home Assistant OS + GREE Controller + VLAN](https://git.linuxiarz.pl/gru/gree-controller-ha-addon/raw/branch/master/gree-controller/topologia-ha-vlan-gree-pl.png) + +Najważniejsze: + +- VLAN-y konfigurujesz na hoście Home Assistant OS, +- dodatek współdzieli interfejsy hosta przez `host_network`, +- nie trzeba tworzyć Docker `macvlan`, +- przy wielu bezpośrednio podłączonych podsieciach `gree_interface` może pozostać puste, +- discovery UDP broadcast wykonuj osobno dla każdego VLAN-u/podsieci. + +Pełna dokumentacja: [DOCS.pl.md](./DOCS.pl.md). + +## EN - HA / VLAN / GREE topology + +![Home Assistant OS + GREE Controller + VLAN topology](https://git.linuxiarz.pl/gru/gree-controller-ha-addon/media/branch/master/gree-controller/topology-ha-vlan-gree-en.png) + +Key points: + +- configure VLANs on the Home Assistant OS host, +- the add-on shares host interfaces via `host_network`, +- Docker `macvlan` is not required, +- with multiple directly attached subnets `gree_interface` may be left empty, +- run UDP broadcast discovery separately for each VLAN/subnet. + +Full documentation: [DOCS.md](./DOCS.md). + +## Versioning + +The project version is maintained only in the root `Cargo.toml`. The build script copies it into the Home Assistant `config.yaml` and publishes one `amd64 + aarch64` OCI manifest under that version. No separate architecture release tags are used. diff --git a/ha-addon/repository/gree-controller/config.yaml b/ha-addon/repository/gree-controller/config.yaml new file mode 100644 index 0000000..2b11de7 --- /dev/null +++ b/ha-addon/repository/gree-controller/config.yaml @@ -0,0 +1,40 @@ +name: "GREE Controller" +version: "0.13.9" +slug: "gree_controller" +description: "Local GREE HVAC controller with Web UI and Home Assistant integration" +arch: + - amd64 + - aarch64 +image: "zot.linuxiarz.pl/gree-controller" +startup: application +boot: auto +init: false +host_network: true +ingress: true +ingress_port: 8787 +ingress_stream: true +panel_icon: mdi:air-conditioner +panel_title: GREE Controller +panel_admin: true +watchdog: "http://[HOST]:8787/api/health" +backup: cold +options: + gree_interface: "" + discovery_broadcast: "255.255.255.255:7000" + simulate: false + auto_seed: false + poll_interval_seconds: 15 + zone_interval_seconds: 5 + discovery_timeout_ms: 3000 + app_token: "" + log_level: info +schema: + gree_interface: str + discovery_broadcast: str + simulate: bool + auto_seed: bool + poll_interval_seconds: "int(2,3600)" + zone_interval_seconds: "int(2,3600)" + discovery_timeout_ms: "int(500,30000)" + app_token: password + log_level: "list(error|warn|info|debug|trace)" diff --git a/ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png b/ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png new file mode 100644 index 0000000..99eb488 Binary files /dev/null and b/ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png differ diff --git a/ha-addon/repository/gree-controller/topologia-ha-vlan-gree.png b/ha-addon/repository/gree-controller/topologia-ha-vlan-gree.png new file mode 100644 index 0000000..95c7fd3 Binary files /dev/null and b/ha-addon/repository/gree-controller/topologia-ha-vlan-gree.png differ diff --git a/ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png b/ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png new file mode 100644 index 0000000..95c7fd3 Binary files /dev/null and b/ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png differ diff --git a/ha-addon/repository/gree-controller/translations/en.yaml b/ha-addon/repository/gree-controller/translations/en.yaml new file mode 100644 index 0000000..8574c36 --- /dev/null +++ b/ha-addon/repository/gree-controller/translations/en.yaml @@ -0,0 +1,28 @@ +configuration: + gree_interface: + name: GREE network interface + description: Interface name or local IPv4 address. Leave empty for automatic routing. + discovery_broadcast: + name: Discovery broadcast + description: Broadcast target, for example 192.168.50.255:7000. Use an explicit subnet broadcast for VLANs. + simulate: + name: Simulation mode + description: Run without physical GREE units. + auto_seed: + name: Seed simulator + description: Create a sample simulated unit when the database is empty. + poll_interval_seconds: + name: Poll interval + description: Device polling interval in seconds. + zone_interval_seconds: + name: Zone interval + description: Thermostat control interval in seconds. + discovery_timeout_ms: + name: Discovery timeout + description: UDP discovery timeout in milliseconds. + app_token: + name: Application token + description: Optional token protecting direct Web UI/API access outside Home Assistant ingress. + log_level: + name: Log level + description: Controller log verbosity. diff --git a/ha-addon/repository/gree-controller/translations/pl.yaml b/ha-addon/repository/gree-controller/translations/pl.yaml new file mode 100644 index 0000000..64e7fd2 --- /dev/null +++ b/ha-addon/repository/gree-controller/translations/pl.yaml @@ -0,0 +1,28 @@ +configuration: + gree_interface: + name: Interfejs sieci GREE + description: Nazwa interfejsu lub lokalny adres IPv4. Puste pole oznacza automatyczny wybór trasy. + discovery_broadcast: + name: Broadcast discovery + description: Adres broadcast, np. 192.168.50.255:7000. Dla VLAN użyj broadcastu konkretnej podsieci. + simulate: + name: Tryb symulacji + description: Uruchomienie bez fizycznych urządzeń GREE. + auto_seed: + name: Dane testowe symulatora + description: Dodaj przykładowe urządzenie, gdy baza jest pusta. + poll_interval_seconds: + name: Interwał odpytywania + description: Odpytywanie urządzeń w sekundach. + zone_interval_seconds: + name: Interwał stref + description: Interwał sterowania termostatem w sekundach. + discovery_timeout_ms: + name: Timeout discovery + description: Timeout wykrywania UDP w milisekundach. + app_token: + name: Token aplikacji + description: Opcjonalna ochrona bezpośredniego dostępu do Web UI/API poza ingressem Home Assistanta. + log_level: + name: Poziom logowania + description: Szczegółowość logów kontrolera. diff --git a/ha-addon/repository/repository.yaml b/ha-addon/repository/repository.yaml new file mode 100644 index 0000000..a3b8119 --- /dev/null +++ b/ha-addon/repository/repository.yaml @@ -0,0 +1,2 @@ +name: GREE Controller +maintainer: linuxiarz.pl diff --git a/ha-addon/run.sh b/ha-addon/run.sh new file mode 100755 index 0000000..4054b9c --- /dev/null +++ b/ha-addon/run.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +CONFIG=/data/options.json + +read_option() { + local key="$1" default_value="${2-}" + if [[ -f "$CONFIG" ]]; then + jq -r --arg key "$key" --arg default "$default_value" \ + 'if has($key) and .[$key] != null then .[$key] else $default end' "$CONFIG" + else + printf '%s\n' "$default_value" + fi +} + +GREE_INTERFACE="$(read_option gree_interface '')" +DISCOVERY_BROADCAST="$(read_option discovery_broadcast '255.255.255.255:7000')" +SIMULATE="$(read_option simulate 'false')" +AUTO_SEED="$(read_option auto_seed 'false')" +POLL_INTERVAL="$(read_option poll_interval_seconds '15')" +ZONE_INTERVAL="$(read_option zone_interval_seconds '5')" +DISCOVERY_TIMEOUT="$(read_option discovery_timeout_ms '3000')" +APP_TOKEN="$(read_option app_token '')" +LOG_LEVEL="$(read_option log_level 'info')" + +export GREE_CONTROLLER_BIND="0.0.0.0:8787" +export GREE_CONTROLLER_DATABASE="/data/gree-controller.db" +export GREE_CONTROLLER_GREE_INTERFACE="$GREE_INTERFACE" +export GREE_CONTROLLER_DISCOVERY_BROADCAST="$DISCOVERY_BROADCAST" +export GREE_CONTROLLER_SIMULATE="$SIMULATE" +export GREE_CONTROLLER_AUTO_SEED="$AUTO_SEED" +export GREE_CONTROLLER_POLL_INTERVAL_SECONDS="$POLL_INTERVAL" +export GREE_CONTROLLER_ZONE_INTERVAL_SECONDS="$ZONE_INTERVAL" +export GREE_CONTROLLER_DISCOVERY_TIMEOUT_MS="$DISCOVERY_TIMEOUT" +export GREE_CONTROLLER_APP_TOKEN="$APP_TOKEN" +export RUST_LOG="gree_controller=${LOG_LEVEL},tower_http=${LOG_LEVEL}" + +printf 'GREE Controller: interface=%s discovery=%s database=%s\n' \ + "${GREE_INTERFACE:-auto}" "$DISCOVERY_BROADCAST" "$GREE_CONTROLLER_DATABASE" +exec /usr/local/bin/gree-controller diff --git a/ha_addon.md b/ha_addon.md new file mode 100644 index 0000000..5111775 --- /dev/null +++ b/ha_addon.md @@ -0,0 +1,1034 @@ +# GREE Controller - Home Assistant Add-on + +This file contains the installation and release procedure in **Polish** and **English**. + +--- + +# PL - Instalacja dodatku Home Assistant + +## 1. Wymagania + +Na maszynie, na której budujesz obrazy, potrzebujesz: + +- Docker +- Docker Buildx +- dostępu do registry OCI, np. `zot.example.com` albo `oci.example.com` +- repozytorium Git dostępnego z Home Assistanta + +Sprawdź: + +```bash +docker --version +docker buildx version +``` + +## 2. Wejdź do katalogu dodatku + +Z katalogu głównego projektu: + +```bash +cd ha-addon +cp .env.example .env +``` + +Edytuj `.env`: + +```bash +nano .env +``` + +Przykład dla Zot: + +```dotenv +REGISTRY=zot.example.com +IMAGE_NAME=gree-controller +DISTRO=trixie +BUILDER=gree-controller-multiarch +BUILDER_CONFIG= +``` + +Obsługiwane systemy bazowe: + +- `DISTRO=trixie` - zalecany wariant Debian Trixie +- `DISTRO=alpine` - wariant Alpine + +Na początek zalecany jest `trixie`. + +## 3. Logowanie do registry + +Jeżeli registry wymaga autoryzacji: + +```bash +docker login zot.example.com +``` + +Po poprawnym logowaniu powinno pojawić się: + +```text +Login Succeeded +``` + +## 4. Wersjonowanie + +Wersję zmieniasz tylko w jednym miejscu: + +```text +Cargo.toml +``` + +Przykład: + +```toml +[package] +version = "0.13.9" +``` + +Przy kolejnym wydaniu zmień tylko: + +```toml +version = "0.13.10" +``` + +Nie zmieniaj ręcznie wersji w: + +- `Cargo.lock` +- `ha-addon/repository/gree-controller/config.yaml` + +Skrypt `build.sh` synchronizuje te pliki automatycznie. + +## 5. Włącz build ARM64 przez QEMU + +Jeżeli host budujący jest `amd64`, zainstaluj emulację binfmt/QEMU: + +```bash +docker run --privileged --rm tonistiigi/binfmt --install all +``` + +Sprawdź dostępne platformy: + +```bash +docker buildx inspect --bootstrap +``` + +Jeżeli domyślny builder pokazuje tylko `amd64`, nie jest to problem. `build.sh` tworzy własny builder: + +```text +gree-controller-multiarch +``` + +Po jego utworzeniu możesz sprawdzić go poleceniem: + +```bash +docker buildx inspect gree-controller-multiarch --bootstrap +``` + +Powinny być dostępne co najmniej: + +```text +linux/amd64 +linux/arm64 +``` + +Home Assistant używa nazwy architektury `aarch64`, natomiast Docker używa `linux/arm64`. + +## 6. Build i push obrazu multi-arch + +Będąc w katalogu: + +```text +ha-addon/ +``` + +uruchom: + +```bash +./build.sh push +``` + +Skrypt: + +1. czyta wersję z `Cargo.toml`, +2. synchronizuje `Cargo.lock`, +3. aktualizuje `ha-addon/repository/gree-controller/config.yaml`, +4. buduje `linux/amd64`, +5. buduje `linux/arm64`, +6. publikuje jeden manifest multi-arch do registry. + +Przykładowy wynik: + +```text +zot.example.com/gree-controller:0.13.9 +``` + +Nie musisz ręcznie utrzymywać osobnych wersji dla `amd64` i `arm64`. + +## 7. Sprawdź obraz w registry + +Po poprawnym buildzie: + +```bash +docker buildx imagetools inspect \ + zot.example.com/gree-controller:0.13.9 +``` + +Powinieneś zobaczyć: + +```text +linux/amd64 +linux/arm64 +``` + +Możesz też sprawdzić pull dla bieżącej architektury: + +```bash +docker pull zot.example.com/gree-controller:0.13.9 +``` + +## 8. Jeżeli build kończy się błędem `docs/openapi.json` + +Projekt korzysta podczas kompilacji z: + +```text +src/api/openapi.rs -> ../../docs/openapi.json +``` + +Dockerfile musi więc kopiować katalog `docs` do etapu buildera: + +```dockerfile +COPY docs ./docs +``` + +W tej wersji projektu jest to już poprawione dla `trixie` i `alpine`. + +## 9. Przygotowanie repozytorium Home Assistant + +Do Git publikujesz zawartość katalogu: + +```text +ha-addon/repository/ +``` + +Root repozytorium Git powinien wyglądać tak: + +```text +repository.yaml +gree-controller/ +├── config.yaml +├── README.md +├── DOCS.md +├── DOCS.pl.md +├── CHANGELOG.md +├── translations/ +├── topology-ha-vlan-gree-en.png +└── topologia-ha-vlan-gree-pl.png +``` + +Przykład: + +```bash +cd ha-addon/repository + +git init -b main +git add . +git commit -m "Initial GREE Controller Home Assistant add-on" +``` + +Dodaj swoje repo Git: + +```bash +git remote add origin \ + https://git.example.pl/mateusz/gree-controller-ha-addon.git + +git push -u origin main +``` + +Home Assistant musi mieć dostęp do tego repozytorium Git. + +## 10. Prywatne registry + +Jeżeli obraz OCI nie jest publiczny, Home Assistant musi mieć możliwość zalogowania się do registry. + +Jeżeli używasz prywatnego registry, dodaj dane dostępowe do registry w Home Assistant/Supervisor zgodnie z konfiguracją swojej instalacji. + +Najprostszy wariant do pierwszego uruchomienia: + +- Git repo może być publiczne lub dostępne dla HA, +- obraz może mieć anonymous pull, +- registry powinno działać po HTTPS z poprawnym certyfikatem. + +## 11. Dodanie custom repository w Home Assistant + +W Home Assistant otwórz sklep Apps/Add-ons i dodaj URL repozytorium Git jako custom repository. + +Podajesz URL Git, np.: + +```text +https://git.example.pl/mateusz/gree-controller-ha-addon.git +``` + +Nie podajesz: + +- ZIP-a projektu, +- adresu obrazu OCI, +- adresu `zot.example.com/gree-controller`. + +Podział jest następujący: + +```text +Git repository + -> opis dodatku i config.yaml + +OCI/Zot registry + -> gotowy obraz kontenera +``` + +## 12. Instalacja dodatku + +Po dodaniu custom repository odśwież sklep dodatków. + +Powinien pojawić się: + +```text +GREE Controller +``` + +Kliknij: + +```text +Install +``` + +Home Assistant odczyta np.: + +```yaml +version: "0.13.9" +image: "zot.example.com/gree-controller" +``` + +i pobierze: + +```text +zot.example.com/gree-controller:0.13.9 +``` + +Manifest OCI automatycznie wybierze odpowiednią architekturę. + +## 13. Konfiguracja jednej sieci/VLAN GREE + +Przykład: + +```text +eth0.50 +192.168.50.2/24 + +GREE devices: +192.168.50.x +``` + +Konfiguracja dodatku: + +```yaml +gree_interface: "eth0.50" +discovery_broadcast: "192.168.50.255:7000" +simulate: false +auto_seed: false +poll_interval_seconds: 15 +zone_interval_seconds: 5 +discovery_timeout_ms: 3000 +app_token: "" +log_level: info +``` + +Zamiast nazwy interfejsu można podać lokalny IPv4: + +```yaml +gree_interface: "192.168.50.2" +``` + +## 14. Kilka VLAN-ów / kilka interfejsów + +Przykład: + +```text +eth0 +├── eth0.50 -> 192.168.50.2/24 +└── eth0.60 -> 192.168.60.2/24 +``` + +Dla automatycznego wyboru interfejsu pozostaw: + +```yaml +gree_interface: "" +``` + +Dla znanych urządzeń kontroler dobiera interfejs do adresu docelowego. + +Discovery UDP broadcast wykonuj osobno dla każdej podsieci. + +VLAN 50: + +```yaml +discovery_broadcast: "192.168.50.255:7000" +``` + +VLAN 60: + +```yaml +discovery_broadcast: "192.168.60.255:7000" +``` + +Broadcast zwykle nie przechodzi przez router, dlatego discovery należy wykonywać per VLAN. + +## 15. VLAN-y na Home Assistant OS + +Sprawdź sieć: + +```bash +ha network info +``` + +Przykład VLAN 50: + +```bash +ha network vlan eth0 50 \ + --ipv4-method static \ + --ipv4-address 192.168.50.2/24 \ + --ipv6-method disabled +``` + +Przykład VLAN 60: + +```bash +ha network vlan eth0 60 \ + --ipv4-method static \ + --ipv4-address 192.168.60.2/24 \ + --ipv6-method disabled +``` + +Następnie: + +```bash +ha network info +``` + +Powinny być widoczne interfejsy podobne do: + +```text +eth0 +eth0.50 +eth0.60 +``` + +Jeżeli główny interfejs HA ma już default gateway, nie dodawaj bez potrzeby kolejnych bram domyślnych na VLAN-ach GREE. + +## 16. Dlaczego `host_network` + +Dodatek ma ustawione: + +```yaml +host_network: true +``` + +Dzięki temu kontener korzysta z przestrzeni sieciowej hosta Home Assistant OS i widzi jego interfejsy/VLAN-y. + +Nie trzeba tworzyć `macvlan` wewnątrz dodatku. + +Schemat: + +```text +Home Assistant OS +├── eth0 +├── eth0.50 +├── eth0.60 +└── GREE Controller add-on + └── host_network: true + ├── VLAN 50 -> GREE A/B + └── VLAN 60 -> GREE C/D +``` + +Diagramy znajdują się w: + +```text +ha-addon/docs/topologia-ha-vlan-gree-pl.png +ha-addon/docs/topology-ha-vlan-gree-en.png +``` + +## 17. Uruchomienie i Web UI + +Po instalacji uruchom dodatek i sprawdź logi. + +Przykład: + +```text +GREE Controller: interface=auto discovery=192.168.50.255:7000 database=/data/gree-controller.db +``` + +Web UI jest dostępne przez Home Assistant Ingress. + +## 18. Aktualizacja dodatku + +Przy nowym wydaniu: + +### Krok 1 - zmień wersję tylko w `Cargo.toml` + +```toml +version = "0.13.10" +``` + +### Krok 2 - build i push + +```bash +cd ha-addon +./build.sh push +``` + +### Krok 3 - sprawdź manifest + +```bash +docker buildx imagetools inspect \ + zot.example.com/gree-controller:0.13.10 +``` + +### Krok 4 - wypchnij metadata repozytorium HA + +```bash +cd repository +git add . +git commit -m "Release 0.13.10" +git push +``` + +### Krok 5 - aktualizacja w Home Assistant + +Odśwież informacje o dodatkach i zainstaluj dostępną aktualizację. + +Zalecana kolejność publikacji: + +```text +1. zmiana Cargo.toml +2. ./build.sh push +3. sprawdzenie obrazu OCI +4. git commit/push ha-addon/repository +5. update w Home Assistant +``` + +Dzięki temu Home Assistant nie zobaczy nowej wersji zanim obraz będzie dostępny w registry. + +--- + +# EN - Home Assistant Add-on installation + +## 1. Requirements + +The build machine needs: + +- Docker +- Docker Buildx +- access to an OCI registry such as `zot.example.com` or `oci.example.com` +- a Git repository reachable by Home Assistant + +Check: + +```bash +docker --version +docker buildx version +``` + +## 2. Enter the add-on directory + +From the project root: + +```bash +cd ha-addon +cp .env.example .env +``` + +Edit `.env`: + +```bash +nano .env +``` + +Example for Zot: + +```dotenv +REGISTRY=zot.example.com +IMAGE_NAME=gree-controller +DISTRO=trixie +BUILDER=gree-controller-multiarch +BUILDER_CONFIG= +``` + +Supported base distributions: + +- `DISTRO=trixie` - recommended Debian Trixie variant +- `DISTRO=alpine` - Alpine variant + +Start with `trixie` unless you specifically need Alpine. + +## 3. Log in to the registry + +If authentication is required: + +```bash +docker login zot.example.com +``` + +Expected result: + +```text +Login Succeeded +``` + +## 4. Versioning + +Change the application version in one place only: + +```text +Cargo.toml +``` + +Example: + +```toml +[package] +version = "0.13.9" +``` + +For the next release change only: + +```toml +version = "0.13.10" +``` + +Do not manually maintain the version in: + +- `Cargo.lock` +- `ha-addon/repository/gree-controller/config.yaml` + +`build.sh` synchronizes these files automatically. + +## 5. Enable ARM64 builds with QEMU + +When building on an `amd64` host, install binfmt/QEMU support: + +```bash +docker run --privileged --rm tonistiigi/binfmt --install all +``` + +Check platforms: + +```bash +docker buildx inspect --bootstrap +``` + +If the default builder still lists only `amd64`, that is not a problem. `build.sh` creates its own builder named: + +```text +gree-controller-multiarch +``` + +After it has been created, inspect it with: + +```bash +docker buildx inspect gree-controller-multiarch --bootstrap +``` + +You should have at least: + +```text +linux/amd64 +linux/arm64 +``` + +Home Assistant calls the ARM architecture `aarch64`, while Docker uses `linux/arm64`. + +## 6. Build and push the multi-arch image + +From: + +```text +ha-addon/ +``` + +run: + +```bash +./build.sh push +``` + +The script: + +1. reads the version from `Cargo.toml`, +2. synchronizes `Cargo.lock`, +3. updates `ha-addon/repository/gree-controller/config.yaml`, +4. builds `linux/amd64`, +5. builds `linux/arm64`, +6. pushes one multi-architecture OCI manifest. + +Example result: + +```text +zot.example.com/gree-controller:0.13.9 +``` + +There is no manual per-architecture version maintenance. + +## 7. Verify the published image + +After a successful build: + +```bash +docker buildx imagetools inspect \ + zot.example.com/gree-controller:0.13.9 +``` + +Expected platforms: + +```text +linux/amd64 +linux/arm64 +``` + +You can also test a pull for the current machine architecture: + +```bash +docker pull zot.example.com/gree-controller:0.13.9 +``` + +## 8. If the build fails on `docs/openapi.json` + +The application embeds this file during compilation: + +```text +src/api/openapi.rs -> ../../docs/openapi.json +``` + +Therefore each Docker builder stage must include: + +```dockerfile +COPY docs ./docs +``` + +This project version already contains that fix for both `trixie` and `alpine`. + +## 9. Prepare the Home Assistant repository + +Publish the contents of: + +```text +ha-addon/repository/ +``` + +The Git repository root should contain: + +```text +repository.yaml +gree-controller/ +├── config.yaml +├── README.md +├── DOCS.md +├── DOCS.pl.md +├── CHANGELOG.md +├── translations/ +├── topology-ha-vlan-gree-en.png +└── topologia-ha-vlan-gree-pl.png +``` + +Example: + +```bash +cd ha-addon/repository + +git init -b main +git add . +git commit -m "Initial GREE Controller Home Assistant add-on" +``` + +Add your Git remote: + +```bash +git remote add origin \ + https://git.example.pl/mateusz/gree-controller-ha-addon.git + +git push -u origin main +``` + +Home Assistant must be able to reach this Git repository. + +## 10. Private registry + +If the OCI image is private, Home Assistant/Supervisor must have valid credentials for the registry. + +For the simplest first installation use: + +- a Git repository reachable by Home Assistant, +- anonymous pull for the OCI image if possible, +- HTTPS with a trusted certificate on the registry. + +## 11. Add the custom repository to Home Assistant + +Open the Apps/Add-ons store in Home Assistant and add the Git repository URL as a custom repository. + +Example: + +```text +https://git.example.pl/mateusz/gree-controller-ha-addon.git +``` + +Do not use: + +- the project ZIP, +- the OCI image URL, +- `zot.example.com/gree-controller` as the repository URL. + +The roles are: + +```text +Git repository + -> add-on metadata and config.yaml + +OCI/Zot registry + -> pre-built container image +``` + +## 12. Install the add-on + +Refresh the Apps/Add-ons store after adding the repository. + +You should see: + +```text +GREE Controller +``` + +Select it and click: + +```text +Install +``` + +Home Assistant reads values such as: + +```yaml +version: "0.13.9" +image: "zot.example.com/gree-controller" +``` + +and pulls: + +```text +zot.example.com/gree-controller:0.13.9 +``` + +The OCI manifest automatically selects the correct architecture. + +## 13. Single GREE network/VLAN + +Example: + +```text +eth0.50 +192.168.50.2/24 + +GREE devices: +192.168.50.x +``` + +Add-on configuration: + +```yaml +gree_interface: "eth0.50" +discovery_broadcast: "192.168.50.255:7000" +simulate: false +auto_seed: false +poll_interval_seconds: 15 +zone_interval_seconds: 5 +discovery_timeout_ms: 3000 +app_token: "" +log_level: info +``` + +A local IPv4 address can be used instead of the interface name: + +```yaml +gree_interface: "192.168.50.2" +``` + +## 14. Multiple VLANs / multiple interfaces + +Example: + +```text +eth0 +├── eth0.50 -> 192.168.50.2/24 +└── eth0.60 -> 192.168.60.2/24 +``` + +For automatic interface selection leave: + +```yaml +gree_interface: "" +``` + +For known devices the controller selects an interface according to the destination address. + +Run UDP broadcast discovery separately for each subnet. + +VLAN 50: + +```yaml +discovery_broadcast: "192.168.50.255:7000" +``` + +VLAN 60: + +```yaml +discovery_broadcast: "192.168.60.255:7000" +``` + +Broadcast normally does not cross routers, therefore discovery should be performed per VLAN. + +## 15. VLANs on Home Assistant OS + +Check the current network configuration: + +```bash +ha network info +``` + +Example VLAN 50: + +```bash +ha network vlan eth0 50 \ + --ipv4-method static \ + --ipv4-address 192.168.50.2/24 \ + --ipv6-method disabled +``` + +Example VLAN 60: + +```bash +ha network vlan eth0 60 \ + --ipv4-method static \ + --ipv4-address 192.168.60.2/24 \ + --ipv6-method disabled +``` + +Verify again: + +```bash +ha network info +``` + +Expected interfaces are similar to: + +```text +eth0 +eth0.50 +eth0.60 +``` + +If the main Home Assistant interface already has the default gateway, normally do not add additional default gateways to GREE VLAN interfaces. + +## 16. Why `host_network` + +The add-on uses: + +```yaml +host_network: true +``` + +This makes the container use the Home Assistant OS host network namespace and allows it to see the host interfaces and VLANs. + +There is no need to create Docker `macvlan` networks inside the add-on. + +Topology: + +```text +Home Assistant OS +├── eth0 +├── eth0.50 +├── eth0.60 +└── GREE Controller add-on + └── host_network: true + ├── VLAN 50 -> GREE A/B + └── VLAN 60 -> GREE C/D +``` + +Diagrams are stored in: + +```text +ha-addon/docs/topologia-ha-vlan-gree-pl.png +ha-addon/docs/topology-ha-vlan-gree-en.png +``` + +## 17. Start the add-on and open the Web UI + +After installation start the add-on and check its logs. + +Example: + +```text +GREE Controller: interface=auto discovery=192.168.50.255:7000 database=/data/gree-controller.db +``` + +The Web UI is available through Home Assistant Ingress. + +## 18. Updating the add-on + +For a new release: + +### Step 1 - change only `Cargo.toml` + +```toml +version = "0.13.10" +``` + +### Step 2 - build and push + +```bash +cd ha-addon +./build.sh push +``` + +### Step 3 - verify the OCI manifest + +```bash +docker buildx imagetools inspect \ + zot.example.com/gree-controller:0.13.10 +``` + +### Step 4 - publish updated Home Assistant metadata + +```bash +cd repository +git add . +git commit -m "Release 0.13.10" +git push +``` + +### Step 5 - update in Home Assistant + +Refresh the add-on information and install the available update. + +Recommended release order: + +```text +1. change Cargo.toml +2. ./build.sh push +3. verify the OCI image +4. git commit/push ha-addon/repository +5. update in Home Assistant +``` + +This prevents Home Assistant from seeing a new version before its image is available in the registry. + +--- + +# References + +- Home Assistant app repositories: https://developers.home-assistant.io/docs/apps/repository/ +- Home Assistant app publishing: https://developers.home-assistant.io/docs/apps/publishing/ +- Home Assistant app configuration: https://developers.home-assistant.io/docs/apps/configuration/ +- Docker multi-platform builds: https://docs.docker.com/build/building/multi-platform/ diff --git a/src/api/assets.rs b/src/api/assets.rs index 3e9d00a..441d23f 100644 --- a/src/api/assets.rs +++ b/src/api/assets.rs @@ -67,23 +67,26 @@ fn request_base_path(state: &AppState, headers: &HeaderMap) -> String { } fn forwarded_prefix(headers: &HeaderMap) -> Option { - let raw = headers - .get("x-forwarded-prefix")? - .to_str() - .ok()? - .split(',') - .next()? - .trim(); - if raw.is_empty() || raw == "/" { - return Some(String::new()); + // Home Assistant ingress uses X-Ingress-Path. Generic reverse proxies + // commonly use X-Forwarded-Prefix, so support both with HA taking + // precedence when both are present. + for header in ["x-ingress-path", "x-forwarded-prefix"] { + let Some(raw) = headers.get(header).and_then(|value| value.to_str().ok()) else { + continue; + }; + let raw = raw.split(',').next().unwrap_or_default().trim(); + if raw.is_empty() || raw == "/" { + return Some(String::new()); + } + if raw.contains('?') + || raw.contains('#') + || raw.split('/').any(|part| matches!(part, "." | "..")) + { + continue; + } + return Some(format!("/{}", raw.trim_matches('/'))); } - if raw.contains('?') - || raw.contains('#') - || raw.split('/').any(|part| matches!(part, "." | "..")) - { - return None; - } - Some(format!("/{}", raw.trim_matches('/'))) + None } async fn app_js() -> Response { static_response( diff --git a/src/protocol/gree/network.rs b/src/protocol/gree/network.rs index 9ff4503..f68e74e 100644 --- a/src/protocol/gree/network.rs +++ b/src/protocol/gree/network.rs @@ -51,8 +51,9 @@ fn local_ipv4_config_for_target(target: Ipv4Addr) -> Result Result> { Ok(None) } #[cfg(target_os = "linux")] -fn interface_ipv4_config(interface: &str) -> Result<(Ipv4Addr, Ipv4Addr)> { +fn interface_ipv4_config(selector: &str) -> Result<(Ipv4Addr, Ipv4Addr)> { use std::{ffi::CStr, ptr}; + let requested_ip = selector.parse::().ok(); unsafe { let mut addrs: *mut libc::ifaddrs = ptr::null_mut(); if libc::getifaddrs(&mut addrs) != 0 { return Err(std::io::Error::last_os_error()).context("getifaddrs failed"); } @@ -60,11 +61,14 @@ fn interface_ipv4_config(interface: &str) -> Result<(Ipv4Addr, Ipv4Addr)> { let mut found = None; while !current.is_null() { let ifa = &*current; - if !ifa.ifa_name.is_null() && !ifa.ifa_addr.is_null() { + if !ifa.ifa_name.is_null() + && !ifa.ifa_addr.is_null() + && (*ifa.ifa_addr).sa_family as i32 == libc::AF_INET + { let name = CStr::from_ptr(ifa.ifa_name).to_string_lossy(); - if name == interface && (*ifa.ifa_addr).sa_family as i32 == libc::AF_INET { - let addr = &*(ifa.ifa_addr as *const libc::sockaddr_in); - let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()); + let addr = &*(ifa.ifa_addr as *const libc::sockaddr_in); + let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()); + if name == selector || requested_ip == Some(ip) { let broadcast = if !ifa.ifa_netmask.is_null() { let mask_addr = &*(ifa.ifa_netmask as *const libc::sockaddr_in); let mask = Ipv4Addr::from(mask_addr.sin_addr.s_addr.to_ne_bytes()); @@ -77,7 +81,7 @@ fn interface_ipv4_config(interface: &str) -> Result<(Ipv4Addr, Ipv4Addr)> { current = ifa.ifa_next; } libc::freeifaddrs(addrs); - found.ok_or_else(|| anyhow!("interface {interface} has no IPv4 address")) + found.ok_or_else(|| anyhow!("interface or local IPv4 address {selector} was not found")) } } diff --git a/web/js/main.js b/web/js/main.js index 06fdafd..02fbca1 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -1,7 +1,7 @@ let historyResizeTimer; window.addEventListener('resize', () => { if (app.currentView === 'history') { clearTimeout(historyResizeTimer); historyResizeTimer = setTimeout(drawCurrentChartIfVisible, 120); } }); window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', () => { if (app.theme === 'system') applyTheme(); }); -if ('serviceWorker' in navigator) window.addEventListener('load', () => navigator.serviceWorker.register(withBase('/sw.js')).catch(() => { })); +if ('serviceWorker' in navigator && !APP_BASE.startsWith('/api/hassio_ingress/')) window.addEventListener('load', () => navigator.serviceWorker.register(withBase('/sw.js')).catch(() => { })); async function startApplication() { try {