mirror of
https://github.com/lildude/ha-addon-teslamate.git
synced 2026-10-13 21:19:16 -04:00
Compare commits
6
Commits
4f844af772
...
e5e379a383
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5e379a383 | ||
|
|
ff4f4bcbf5 | ||
|
|
64033b692e | ||
|
|
a7bc113eb0 | ||
|
|
b03e782e8f | ||
|
|
6a20a1e31d |
@@ -506,11 +506,16 @@ configure_and_start_teslamate() {
|
|||||||
if [ "$state" = "started" ]; then
|
if [ "$state" = "started" ]; then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
if [ "$state" = "error" ]; then
|
||||||
|
dump_diagnostics 50
|
||||||
|
err "TeslaMate addon failed to start (state: ${state})"
|
||||||
|
fi
|
||||||
debug "Attempt $((attempt + 1))/${max_attempts}: TeslaMate state=${state}"
|
debug "Attempt $((attempt + 1))/${max_attempts}: TeslaMate state=${state}"
|
||||||
sleep 5
|
sleep 5
|
||||||
attempt=$((attempt + 1))
|
attempt=$((attempt + 1))
|
||||||
done
|
done
|
||||||
if [ "$state" != "started" ]; then
|
if [ "$state" != "started" ]; then
|
||||||
|
dump_diagnostics 50
|
||||||
err "TeslaMate addon did not start in time (state: ${state})"
|
err "TeslaMate addon did not start in time (state: ${state})"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -518,23 +523,52 @@ configure_and_start_teslamate() {
|
|||||||
ok "TeslaMate addon started and ready"
|
ok "TeslaMate addon started and ready"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# dump_diagnostics() prints PostgreSQL and TeslaMate add-on logs (full lines) so
|
||||||
|
# a startup failure shows its real cause. PostgreSQL is reported first because
|
||||||
|
# TeslaMate halts if it cannot reach the database, so a stopped/crashed postgres
|
||||||
|
# addon is the most common root cause. Closes any open GitHub Actions log group
|
||||||
|
# first so the output is never hidden inside a collapsed fold.
|
||||||
|
dump_diagnostics() {
|
||||||
|
local lines="${1:-50}"
|
||||||
|
group_end
|
||||||
|
|
||||||
|
local pg_state
|
||||||
|
pg_state=$(supervisor_api_jq "/addons/${POSTGRES_SLUG}/info" '.data.state' 2>/dev/null || true)
|
||||||
|
warn "PostgreSQL addon state: ${pg_state:-unknown}"
|
||||||
|
warn "Last ${lines} lines of PostgreSQL add-on logs:"
|
||||||
|
supervisor_api GET "/addons/${POSTGRES_SLUG}/logs" 2>/dev/null | tail -n "${lines}" >&2 || true
|
||||||
|
|
||||||
|
warn "Last ${lines} lines of TeslaMate add-on logs:"
|
||||||
|
supervisor_api GET "/addons/${TESLAMATE_SLUG}/logs" 2>/dev/null | tail -n "${lines}" >&2 || true
|
||||||
|
}
|
||||||
|
|
||||||
wait_for_teslamate_ready() {
|
wait_for_teslamate_ready() {
|
||||||
log "Waiting for TeslaMate to be ready (listening on port ${TESLAMATE_PORT})..."
|
log "Waiting for TeslaMate to be ready (listening on port ${TESLAMATE_PORT})..."
|
||||||
local max_attempts=60
|
local max_attempts=60
|
||||||
local attempt=0
|
local attempt=0
|
||||||
|
local logs state
|
||||||
while [ "$attempt" -lt "$max_attempts" ]; do
|
while [ "$attempt" -lt "$max_attempts" ]; do
|
||||||
local logs
|
|
||||||
logs=$(supervisor_api GET "/addons/${TESLAMATE_SLUG}/logs" 2>/dev/null || true)
|
logs=$(supervisor_api GET "/addons/${TESLAMATE_SLUG}/logs" 2>/dev/null || true)
|
||||||
if echo "$logs" | grep -q "TeslaMateWeb.Endpoint"; then
|
if echo "$logs" | grep -q "TeslaMateWeb.Endpoint"; then
|
||||||
ok "TeslaMate is ready"
|
ok "TeslaMate is ready"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if [ "$DEBUG" = "1" ]; then
|
|
||||||
debug "Attempt $((attempt + 1))/${max_attempts}: endpoint not up yet; last log: $(echo "$logs" | tail -n 1)"
|
# TeslaMate's s6 finish script halts the whole container if the service
|
||||||
|
# exits non-zero, so a crash during startup shows up as the add-on leaving
|
||||||
|
# the "started" state. Detect that and fail immediately with the real logs
|
||||||
|
# instead of polling uselessly until the timeout.
|
||||||
|
state=$(supervisor_api_jq "/addons/${TESLAMATE_SLUG}/info" '.data.state')
|
||||||
|
if [ "$state" = "error" ] || [ "$state" = "stopped" ]; then
|
||||||
|
dump_diagnostics 50
|
||||||
|
err "TeslaMate crashed during startup (add-on state: ${state})"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
debug "Attempt $((attempt + 1))/${max_attempts}: endpoint not up yet (state=${state})"
|
||||||
sleep 5
|
sleep 5
|
||||||
attempt=$((attempt + 1))
|
attempt=$((attempt + 1))
|
||||||
done
|
done
|
||||||
|
dump_diagnostics 50
|
||||||
err "TeslaMate did not become ready in time (never saw 'TeslaMateWeb.Endpoint' in logs)"
|
err "TeslaMate did not become ready in time (never saw 'TeslaMateWeb.Endpoint' in logs)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,10 +48,20 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
echo "::endgroup::" >&2
|
echo "::endgroup::" >&2
|
||||||
echo "::group::Start Supervisor" >&2
|
echo "::group::Start Supervisor" >&2
|
||||||
|
# AppArmor profile enforcement on add-on containers makes the
|
||||||
|
# PostgreSQL add-on's first-boot initdb file I/O so slow it blows
|
||||||
|
# past that add-on's hard 2-minute startup timeout on GitHub Actions
|
||||||
|
# runners. SUPERVISOR_UNCONFINED only unconfines the Supervisor
|
||||||
|
# itself, not the add-ons it launches, so instead stub apparmor_parser
|
||||||
|
# to a no-op and mount it into the Supervisor's privileged container.
|
||||||
|
# Add-on profiles are then never loaded and add-ons run effectively
|
||||||
|
# unconfined (matches the proven ha-addon-ghostfolio CI setup).
|
||||||
|
printf '#!/bin/sh\nexit 0\n' | sudo tee /usr/sbin/apparmor_parser > /dev/null && sudo chmod +x /usr/sbin/apparmor_parser
|
||||||
|
sudo sed -i 's|docker run --rm --privileged|docker run --rm --privileged -v /usr/sbin/apparmor_parser:/usr/sbin/apparmor_parser:ro|' /usr/bin/supervisor_run
|
||||||
if [ -n "${DEBUG:-}" ]; then
|
if [ -n "${DEBUG:-}" ]; then
|
||||||
SUPERVISOR_UNCONFINED=1 supervisor_run &
|
supervisor_run &
|
||||||
else
|
else
|
||||||
SUPERVISOR_UNCONFINED=1 supervisor_run > /dev/null 2>&1 &
|
supervisor_run > /dev/null 2>&1 &
|
||||||
fi
|
fi
|
||||||
echo "::endgroup::" >&2
|
echo "::endgroup::" >&2
|
||||||
bash .devcontainer/setup-ha.sh
|
bash .devcontainer/setup-ha.sh
|
||||||
|
|||||||
@@ -52,46 +52,67 @@ for env_var in $(bashio::config 'env_vars|keys'); do
|
|||||||
export "${name}=${value}"
|
export "${name}=${value}"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Create the PostgreSQL database if it doesn't exist
|
# Wait for PostgreSQL to be ready before continuing. The add-on can start
|
||||||
bashio::log.info "Checking for database '$DATABASE_NAME' on $DATABASE_HOST"
|
# before the database is accepting connections (e.g. on a Home Assistant reboot
|
||||||
if pg_isready -h "$DATABASE_HOST" -p "$DATABASE_PORT" > /dev/null 2>&1; then
|
# when both add-ons start together, or under load in CI), so retry instead of
|
||||||
# Check the version of PostgreSQL database is v16.7 or later or v17.3 or later
|
# failing immediately - TeslaMate has no built-in retry for this pre-check and
|
||||||
pg_version=$(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -Atqc "SELECT regexp_replace(version(), 'PostgreSQL ([^ ]+) .*', '\\1') AS version")
|
# the s6 finish script would otherwise halt the whole add-on.
|
||||||
major_version=$(echo "$pg_version" | cut -d '.' -f 1)
|
bashio::log.info "Waiting for PostgreSQL at '$DATABASE_HOST:$DATABASE_PORT'..."
|
||||||
minor_version=$(echo "$pg_version" | cut -d '.' -f 2)
|
pg_wait_attempt=0
|
||||||
compat=true
|
pg_wait_max=30
|
||||||
case "$major_version" in
|
until pg_isready -h "$DATABASE_HOST" -p "$DATABASE_PORT" > /dev/null 2>&1; do
|
||||||
16)
|
pg_wait_attempt=$((pg_wait_attempt + 1))
|
||||||
[[ "$minor_version" -lt 7 ]] && compat=false
|
if [[ "$pg_wait_attempt" -ge "$pg_wait_max" ]]; then
|
||||||
;;
|
bashio::log.error "PostgreSQL at '$DATABASE_HOST' is not ready or unreachable after $((pg_wait_max * 2))s"
|
||||||
17)
|
# Distinguish a DNS/networking problem from a database that is simply down,
|
||||||
[[ "$minor_version" -lt 3 ]] && compat=false
|
# so the failure is actionable: resolve the host and report what we find.
|
||||||
;;
|
if getent hosts "$DATABASE_HOST" > /dev/null 2>&1; then
|
||||||
*)
|
bashio::log.error "Host '$DATABASE_HOST' resolves to $(getent hosts "$DATABASE_HOST" | awk '{print $1}' | tr '\n' ' ')but is not accepting connections on port $DATABASE_PORT - is the PostgreSQL add-on running and healthy?"
|
||||||
[[ "$major_version" -lt 16 ]] && compat=false
|
else
|
||||||
;;
|
bashio::log.error "Host '$DATABASE_HOST' does not resolve - the PostgreSQL add-on may be stopped or on a different network"
|
||||||
esac
|
fi
|
||||||
|
|
||||||
if [ $compat = false ]; then
|
|
||||||
bashio::log.error "PostgreSQL version $pg_version is not supported. Please upgrade to v16.7 or later or v17.3 or later."
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
bashio::log.info "PostgreSQL not ready yet, retrying in 2s (${pg_wait_attempt}/${pg_wait_max})..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
bashio::log.info "PostgreSQL is ready"
|
||||||
|
|
||||||
if [[ -n $(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -Atqc "SELECT datname FROM pg_database WHERE datname = '$DATABASE_NAME'") ]]; then
|
# Create the PostgreSQL database if it doesn't exist
|
||||||
bashio::log.info "Database $DATABASE_NAME already exists"
|
bashio::log.info "Checking for database '$DATABASE_NAME' on $DATABASE_HOST"
|
||||||
else
|
# Check the version of PostgreSQL database is v16.7 or later or v17.3 or later
|
||||||
if PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -qc "CREATE DATABASE \"$DATABASE_NAME\""; then
|
pg_version=$(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -Atqc "SELECT regexp_replace(version(), 'PostgreSQL ([^ ]+) .*', '\\1') AS version")
|
||||||
bashio::log.info "Database $DATABASE_NAME created"
|
major_version=$(echo "$pg_version" | cut -d '.' -f 1)
|
||||||
else
|
minor_version=$(echo "$pg_version" | cut -d '.' -f 2)
|
||||||
bashio::log.error "Failed to create database $DATABASE_NAME"
|
compat=true
|
||||||
exit 1
|
case "$major_version" in
|
||||||
fi
|
16)
|
||||||
fi
|
[[ "$minor_version" -lt 7 ]] && compat=false
|
||||||
else
|
;;
|
||||||
bashio::log.error "PostgreSQL at '$DATABASE_HOST' is not ready or unreachable"
|
17)
|
||||||
|
[[ "$minor_version" -lt 3 ]] && compat=false
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
[[ "$major_version" -lt 16 ]] && compat=false
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ $compat = false ]; then
|
||||||
|
bashio::log.error "PostgreSQL version $pg_version is not supported. Please upgrade to v16.7 or later or v17.3 or later."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -n $(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -Atqc "SELECT datname FROM pg_database WHERE datname = '$DATABASE_NAME'") ]]; then
|
||||||
|
bashio::log.info "Database $DATABASE_NAME already exists"
|
||||||
|
else
|
||||||
|
if PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -qc "CREATE DATABASE \"$DATABASE_NAME\""; then
|
||||||
|
bashio::log.info "Database $DATABASE_NAME created"
|
||||||
|
else
|
||||||
|
bashio::log.error "Failed to create database $DATABASE_NAME"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Import dashboards
|
# Import dashboards
|
||||||
if bashio::config.true 'grafana_import_dashboards'; then
|
if bashio::config.true 'grafana_import_dashboards'; then
|
||||||
bashio::log.info "Importing Grafana dashboards..."
|
bashio::log.info "Importing Grafana dashboards..."
|
||||||
|
|||||||
Reference in New Issue
Block a user