v0.14.15-fix
This commit is contained in:
+2
-2
@@ -37,7 +37,7 @@ LABEL io.hass.version="${BUILD_VERSION}" \
|
||||
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 \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates tzdata jq iproute2 curl \
|
||||
&& 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
|
||||
@@ -56,7 +56,7 @@ LABEL io.hass.version="${BUILD_VERSION}" \
|
||||
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
|
||||
RUN apk add --no-cache ca-certificates tzdata jq iproute2 curl
|
||||
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
|
||||
|
||||
@@ -42,6 +42,8 @@ INTEGRATION_DOMAIN=gree_controller
|
||||
|
||||
The project version comes only from root `Cargo.toml`. `render` synchronizes it to `Cargo.lock`, add-on `config.yaml` and the custom integration `manifest.json`, and sets the OCI image from `.env`.
|
||||
|
||||
For the Home Assistant package, TCP `8787` is an intentional fixed host-network/ingress port contract. `build.sh render` verifies `config.yaml`, `run.sh`, the watchdog URL and Docker image metadata stay consistent. The runtime also compares the Supervisor-reported ingress port before starting. Standalone installations remain free to use any `GREE_CONTROLLER_BIND` port.
|
||||
|
||||
## Home Assistant repository
|
||||
|
||||
```bash
|
||||
|
||||
@@ -35,6 +35,8 @@ CONFIG="$SCRIPT_DIR/repository/$ADDON_DIR/config.yaml"
|
||||
LOCKFILE="$PROJECT_ROOT/Cargo.lock"
|
||||
INTEGRATION_MANIFEST="$SCRIPT_DIR/home-assistant/custom_components/$INTEGRATION_DOMAIN/manifest.json"
|
||||
REPOSITORY_CONFIG="$SCRIPT_DIR/repository/repository.yaml"
|
||||
RUN_SCRIPT="$SCRIPT_DIR/run.sh"
|
||||
DOCKERFILE="$SCRIPT_DIR/Dockerfile"
|
||||
|
||||
[[ -f "$CONFIG" ]] || { echo "Missing add-on config: $CONFIG" >&2; exit 1; }
|
||||
[[ -f "$INTEGRATION_MANIFEST" ]] || { echo "Missing integration manifest: $INTEGRATION_MANIFEST" >&2; exit 1; }
|
||||
@@ -87,6 +89,30 @@ PY
|
||||
printf 'Synced version %s; image %s\n' "$VERSION" "$IMAGE_REPO"
|
||||
}
|
||||
|
||||
validate_addon_port_contract() {
|
||||
local ingress_port
|
||||
ingress_port="$(awk '/^ingress_port:/ {print $2; exit}' "$CONFIG")"
|
||||
[[ "$ingress_port" =~ ^[0-9]+$ ]] || { echo "Invalid ingress_port in $CONFIG" >&2; exit 1; }
|
||||
grep -Fqx "readonly HA_HTTP_PORT=${ingress_port}" "$RUN_SCRIPT" || {
|
||||
echo "HA port contract mismatch: ingress_port=${ingress_port}, run.sh differs" >&2
|
||||
exit 1
|
||||
}
|
||||
grep -Fq "watchdog: \"http://[HOST]:[PORT:${ingress_port}]/api/health\"" "$CONFIG" || {
|
||||
echo "HA port contract mismatch: watchdog must use [PORT:${ingress_port}]" >&2
|
||||
exit 1
|
||||
}
|
||||
grep -Fqx "hassio_api: true" "$CONFIG" || {
|
||||
echo "HA add-on contract mismatch: hassio_api must be enabled for Supervisor runtime discovery" >&2
|
||||
exit 1
|
||||
}
|
||||
local expose_count
|
||||
expose_count="$(grep -Ec "^EXPOSE ${ingress_port}/tcp$" "$DOCKERFILE" || true)"
|
||||
(( expose_count >= 1 )) || {
|
||||
echo "HA port contract mismatch: Dockerfile must expose ${ingress_port}/tcp" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
ensure_builder() {
|
||||
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; }
|
||||
@@ -136,6 +162,7 @@ build_load() {
|
||||
}
|
||||
|
||||
render_metadata
|
||||
validate_addon_port_contract
|
||||
|
||||
case "$ACTION" in
|
||||
render) ;;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "gree_controller",
|
||||
"name": "GREE Controller",
|
||||
"version": "0.14.14",
|
||||
"version": "0.14.15",
|
||||
"config_flow": true,
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 0.14.15
|
||||
|
||||
- Treats TCP `8787` as the fixed internal Home Assistant add-on application/ingress port and validates the Supervisor-reported ingress port at startup.
|
||||
- Makes the add-on build fail when `run.sh`, `ingress_port` and watchdog port metadata diverge.
|
||||
- Uses the Home Assistant Supervisor network API to resolve the primary host IPv4 for generated chart-only share links instead of depending on the browser/ingress hostname.
|
||||
- Adds optional `public_chart_base_url` for reverse proxies, alternate hostnames or non-standard external routing while keeping the public surface limited to generated Custom Chart shares.
|
||||
- Uses Home Assistant's `[PORT:8787]` watchdog placeholder so Supervisor resolves the effective watchdog port from the add-on port contract.
|
||||
- Keeps standalone installations fully port-configurable through `GREE_CONTROLLER_BIND`.
|
||||
|
||||
## 0.14.14
|
||||
|
||||
- Changes History → Custom Charts links to open a standalone chart-only page instead of the full dashboard.
|
||||
|
||||
@@ -56,6 +56,7 @@ Broadcast zwykle nie przechodzi przez router. Sterowanie unicast może działać
|
||||
| `zone_interval_seconds` | interwał sterowania strefami/termostatem |
|
||||
| `discovery_timeout_ms` | timeout discovery UDP |
|
||||
| `app_token` | token bezpośredniego Web UI/API; w trybie Supervisor pusty = direct access zablokowany |
|
||||
| `public_chart_base_url` | opcjonalny bazowy URL publicznych linków Custom Chart; puste = automatyczne główne IPv4 hosta HA + `:8787` |
|
||||
| `log_level` | `error`, `warn`, `info`, `debug`, `trace` |
|
||||
|
||||
### Home Assistant API
|
||||
@@ -64,7 +65,7 @@ Dodatek korzysta automatycznie z wewnętrznego proxy Home Assistant Core (`http:
|
||||
|
||||
### Dostęp, dane i bezpieczeństwo
|
||||
|
||||
Usługa słucha na TCP `8787`; ingress Home Assistant przekazuje Web UI na ten port. Gdy wykryty jest `SUPERVISOR_TOKEN`, bezpośredni dashboard/API na `:8787` wymaga `app_token`; przy pustym `app_token` bezpośredni dashboard jest zablokowany. Z sieci bez poświadczeń dostępny jest tylko publiczny widok i endpoint danych pojedynczego Custom Chart (`/charts/custom/<share-token>`, `/api/public/charts/custom/<share-token>`). Token udostępnienia jest losowy, jego skrót jest zapisywany w SQLite, a sam URL nie zawiera nazw urządzeń ani listy metryk. Link kopiowany z History → Custom chart omija HA ingress i wskazuje bezpośrednio host/IP dodatku na jego porcie HTTP (domyślnie `8787`). `/api/health` bez tokenu jest akceptowane tylko od peera Supervisora na potrzeby watchdoga; bezpośrednie żądanie sieciowe wymaga `app_token`. Baza jest zapisywana w `/data/gree-controller.db`; konfiguracja używa `backup: cold`, więc dane są objęte backupem dodatku.
|
||||
Usługa używa stałego wewnętrznego portu TCP `8787`; ingress Home Assistant przekazuje Web UI na ten port. Port nie jest opcją użytkownika w zakładce Network. Przy starcie add-on porównuje port raportowany przez Supervisor z kontraktem `8787` i odmawia startu, jeśli ręcznie zmodyfikowana paczka jest niespójna. Gdy wykryty jest `SUPERVISOR_TOKEN`, bezpośredni dashboard/API na `:8787` wymaga `app_token`; przy pustym `app_token` bezpośredni dashboard jest zablokowany. Z sieci bez poświadczeń dostępny jest tylko publiczny widok i endpoint danych pojedynczego Custom Chart (`/charts/custom/<share-token>`, `/api/public/charts/custom/<share-token>`). Token udostępnienia jest losowy, jego skrót jest zapisywany w SQLite, a sam URL nie zawiera nazw urządzeń ani listy metryk. Link kopiowany z History → Custom chart omija HA ingress; domyślnie add-on pobiera główne IPv4 hosta z Supervisor API i tworzy `http://<IP-HA>:8787/charts/custom/...`. `public_chart_base_url` pozwala jawnie wskazać reverse proxy, inną nazwę hosta lub alternatywną trasę. `/api/health` bez tokenu jest akceptowane tylko od peera Supervisora na potrzeby watchdoga; bezpośrednie żądanie sieciowe wymaga `app_token`. Baza jest zapisywana w `/data/gree-controller.db`; konfiguracja używa `backup: cold`, więc dane są objęte backupem dodatku.
|
||||
|
||||
Watchdog sprawdza `/api/health`. Do diagnostyki sieci najpierw sprawdź `ha network info`, poprawność `gree_interface`, broadcast konkretnej podsieci i reguły UDP/firewalla.
|
||||
|
||||
@@ -121,6 +122,7 @@ Broadcast normally does not cross routers. Unicast control may work through rout
|
||||
| `zone_interval_seconds` | thermostat/zone control interval |
|
||||
| `discovery_timeout_ms` | UDP discovery timeout |
|
||||
| `app_token` | token for direct Web UI/API access; in Supervisor mode empty = direct access disabled |
|
||||
| `public_chart_base_url` | optional base URL for public Custom Chart links; empty = primary HA host IPv4 + `:8787` automatically |
|
||||
| `log_level` | `error`, `warn`, `info`, `debug`, `trace` |
|
||||
|
||||
### Home Assistant API
|
||||
@@ -129,6 +131,6 @@ The add-on automatically uses the internal Home Assistant Core proxy (`http://su
|
||||
|
||||
### Access, data and security
|
||||
|
||||
The service listens on TCP `8787`; Home Assistant ingress proxies the Web UI to that port. When `SUPERVISOR_TOKEN` is detected, direct dashboard/API access on `:8787` requires `app_token`; with an empty `app_token`, direct dashboard access is disabled. From the network, the only unauthenticated application data is the single public Custom Chart view/data endpoint (`/charts/custom/<share-token>`, `/api/public/charts/custom/<share-token>`). The share token is random, only its hash is stored in SQLite, and the URL does not expose device names or metric selectors. Links copied from History → Custom chart bypass HA ingress and point directly to the add-on host/IP on its HTTP port (default `8787`). `/api/health` is accepted without a token only from the Supervisor peer for the add-on watchdog; direct network requests require `app_token`. The database is stored in `/data/gree-controller.db`; `backup: cold` keeps it in the add-on backup.
|
||||
The service uses fixed internal TCP port `8787`; Home Assistant ingress proxies the Web UI to that port. The port is not a user-facing Network option. On startup the add-on compares the Supervisor-reported ingress port with the `8787` contract and refuses to start if a manually modified package is inconsistent. When `SUPERVISOR_TOKEN` is detected, direct dashboard/API access on `:8787` requires `app_token`; with an empty `app_token`, direct dashboard access is disabled. From the network, the only unauthenticated application data is the single public Custom Chart view/data endpoint (`/charts/custom/<share-token>`, `/api/public/charts/custom/<share-token>`). The share token is random, only its hash is stored in SQLite, and the URL does not expose device names or metric selectors. Links copied from History → Custom chart bypass HA ingress; by default the add-on obtains the primary host IPv4 from the Supervisor API and builds `http://<HA-IP>:8787/charts/custom/...`. `public_chart_base_url` can explicitly select a reverse proxy, alternate hostname or routing path. `/api/health` is accepted without a token only from the Supervisor peer for the add-on watchdog; direct network requests require `app_token`. The database is stored in `/data/gree-controller.db`; `backup: cold` keeps it in the add-on backup.
|
||||
|
||||
The watchdog checks `/api/health`. For network troubleshooting, verify `ha network info`, `gree_interface`, the selected subnet broadcast, and UDP/firewall rules first.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "GREE Controller"
|
||||
version: "0.14.14"
|
||||
version: "0.14.15"
|
||||
slug: "gree_controller"
|
||||
description: "Local GREE HVAC controller with Web UI and Home Assistant integration"
|
||||
url: "https://git.linuxiarz.pl/gru/gree-controller-ha-addon/"
|
||||
@@ -12,13 +12,14 @@ boot: auto
|
||||
init: false
|
||||
host_network: true
|
||||
homeassistant_api: true
|
||||
hassio_api: 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"
|
||||
watchdog: "http://[HOST]:[PORT:8787]/api/health"
|
||||
backup: cold
|
||||
options:
|
||||
gree_interface: ""
|
||||
@@ -29,6 +30,7 @@ options:
|
||||
zone_interval_seconds: 5
|
||||
discovery_timeout_ms: 3000
|
||||
app_token: ""
|
||||
public_chart_base_url: ""
|
||||
log_level: info
|
||||
schema:
|
||||
gree_interface: str
|
||||
@@ -39,4 +41,5 @@ schema:
|
||||
zone_interval_seconds: "int(2,3600)"
|
||||
discovery_timeout_ms: "int(500,30000)"
|
||||
app_token: password
|
||||
public_chart_base_url: str
|
||||
log_level: "list(error|warn|info|debug|trace)"
|
||||
|
||||
@@ -23,6 +23,9 @@ configuration:
|
||||
app_token:
|
||||
name: Application token
|
||||
description: Token required for direct dashboard/API access on port 8787. With SUPERVISOR_TOKEN active, an empty token disables direct access; only generated Custom Chart share links remain public.
|
||||
public_chart_base_url:
|
||||
name: Public chart base URL
|
||||
description: Optional override for generated Custom Chart links, for example http://192.168.1.20:8787 or a reverse-proxy URL. Leave empty to use the primary Home Assistant host IPv4 and the fixed add-on port 8787 automatically.
|
||||
log_level:
|
||||
name: Log level
|
||||
description: Controller log verbosity.
|
||||
|
||||
@@ -23,6 +23,9 @@ configuration:
|
||||
app_token:
|
||||
name: Token aplikacji
|
||||
description: Token wymagany do bezpośredniego dostępu do dashboardu/API na porcie 8787. Przy aktywnym SUPERVISOR_TOKEN pusty token blokuje direct access; publiczne pozostają wyłącznie wygenerowane linki Custom Chart.
|
||||
public_chart_base_url:
|
||||
name: Bazowy URL publicznych wykresów
|
||||
description: Opcjonalne nadpisanie adresu generowanych linków Custom Chart, np. http://192.168.1.20:8787 albo adres reverse proxy. Pozostaw puste, aby automatycznie użyć głównego IPv4 hosta Home Assistant i stałego portu add-onu 8787.
|
||||
log_level:
|
||||
name: Poziom logowania
|
||||
description: Szczegółowość logów kontrolera.
|
||||
|
||||
+56
-3
@@ -2,6 +2,8 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
CONFIG=/data/options.json
|
||||
readonly HA_HTTP_PORT=8787
|
||||
SUPERVISOR_API="${SUPERVISOR:-http://supervisor}"
|
||||
|
||||
read_option() {
|
||||
local key="$1" default_value="${2-}"
|
||||
@@ -13,6 +15,44 @@ read_option() {
|
||||
fi
|
||||
}
|
||||
|
||||
supervisor_get() {
|
||||
local path="$1"
|
||||
[[ -n "${SUPERVISOR_TOKEN:-}" ]] || return 1
|
||||
curl --fail --silent --show-error --max-time 5 \
|
||||
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
|
||||
"${SUPERVISOR_API%/}${path}"
|
||||
}
|
||||
|
||||
validate_ingress_port_contract() {
|
||||
local payload ingress_port
|
||||
payload="$(supervisor_get /addons/self/info 2>/dev/null || true)"
|
||||
[[ -n "$payload" ]] || return 0
|
||||
ingress_port="$(jq -r '.data.ingress_port // empty' <<<"$payload" 2>/dev/null || true)"
|
||||
if [[ "$ingress_port" =~ ^[0-9]+$ ]] && (( ingress_port != HA_HTTP_PORT )); then
|
||||
printf 'GREE Controller: refusing to start: HA ingress_port=%s but the add-on HTTP port contract is %s. Reinstall/update the official add-on package instead of changing ingress_port manually.\n' \
|
||||
"$ingress_port" "$HA_HTTP_PORT" >&2
|
||||
exit 78
|
||||
fi
|
||||
}
|
||||
|
||||
detect_primary_host_ipv4() {
|
||||
local payload address
|
||||
payload="$(supervisor_get /network/info 2>/dev/null || true)"
|
||||
[[ -n "$payload" ]] || return 1
|
||||
address="$(jq -r '
|
||||
(.data.interfaces // [])
|
||||
| (if type == "object" then [to_entries[] | (.value + {interface: (.value.interface // .key)})] else . end)
|
||||
| map(select((.enabled // true) == true and (.connected // true) == true))
|
||||
| map(. + {candidate_ipv4: (.ipv4.ip_address // .ip_address // (try .ipv4.address[0] catch empty) // empty)})
|
||||
| map(select(.candidate_ipv4 != ""))
|
||||
| ((map(select((.primary // false) == true)) | .[0]) // .[0] // {})
|
||||
| (.candidate_ipv4 // empty)
|
||||
' <<<"$payload" 2>/dev/null || true)"
|
||||
address="${address%%/*}"
|
||||
[[ "$address" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1
|
||||
printf '%s\n' "$address"
|
||||
}
|
||||
|
||||
GREE_INTERFACE="$(read_option gree_interface '')"
|
||||
DISCOVERY_BROADCAST="$(read_option discovery_broadcast '255.255.255.255:7000')"
|
||||
SIMULATE="$(read_option simulate 'false')"
|
||||
@@ -21,9 +61,20 @@ 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 '')"
|
||||
PUBLIC_CHART_BASE_URL="$(read_option public_chart_base_url '')"
|
||||
LOG_LEVEL="$(read_option log_level 'info')"
|
||||
|
||||
export GREE_CONTROLLER_BIND="0.0.0.0:8787"
|
||||
validate_ingress_port_contract
|
||||
PUBLIC_CHART_BASE_SOURCE="configured"
|
||||
if [[ -z "$PUBLIC_CHART_BASE_URL" ]]; then
|
||||
PUBLIC_CHART_BASE_SOURCE="browser-fallback"
|
||||
if PRIMARY_HOST_IPV4="$(detect_primary_host_ipv4)"; then
|
||||
PUBLIC_CHART_BASE_URL="http://${PRIMARY_HOST_IPV4}:${HA_HTTP_PORT}"
|
||||
PUBLIC_CHART_BASE_SOURCE="supervisor-primary-ipv4"
|
||||
fi
|
||||
fi
|
||||
|
||||
export GREE_CONTROLLER_BIND="0.0.0.0:${HA_HTTP_PORT}"
|
||||
export GREE_CONTROLLER_DATABASE="/data/gree-controller.db"
|
||||
export GREE_CONTROLLER_GREE_INTERFACE="$GREE_INTERFACE"
|
||||
export GREE_CONTROLLER_DISCOVERY_BROADCAST="$DISCOVERY_BROADCAST"
|
||||
@@ -33,9 +84,11 @@ 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 GREE_CONTROLLER_PUBLIC_CHART_BASE_URL="$PUBLIC_CHART_BASE_URL"
|
||||
export GREE_CONTROLLER_HA_AUTH="supervisor"
|
||||
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"
|
||||
printf 'GREE Controller: interface=%s discovery=%s database=%s http_port=%s public_chart_base_source=%s\n' \
|
||||
"${GREE_INTERFACE:-auto}" "$DISCOVERY_BROADCAST" "$GREE_CONTROLLER_DATABASE" "$HA_HTTP_PORT" \
|
||||
"$PUBLIC_CHART_BASE_SOURCE"
|
||||
exec /usr/local/bin/gree-controller
|
||||
|
||||
Reference in New Issue
Block a user