fix: auto-fix linting issues

This commit is contained in:
alexbelgium
2025-08-17 00:34:15 +00:00
committed by github-actions[bot]
parent 205a7e9b84
commit 0d3c7619b4
28 changed files with 375 additions and 352 deletions

View File

@@ -149,8 +149,8 @@ if bashio::config.has_value 'networkdisks'; then
fi
diskname="$disk"
diskname="${diskname//\\//}" # replace \ with /
diskname="${diskname##*/}" # Get only last part of the name
diskname="${diskname//\\//}" # replace \ with /
diskname="${diskname##*/}" # Get only last part of the name
MOUNTED=false
# Start

498
.templates/bashio-standalone.sh Normal file → Executable file
View File

@@ -9,47 +9,50 @@
# Whether to emit ANSI colors (disabled if not a TTY)
if [ -t 1 ]; then
_BASHIO_COLOR=1
_BASHIO_COLOR=1
else
_BASHIO_COLOR=0
_BASHIO_COLOR=0
fi
_bashio_color() {
# $1=name; returns ANSI sequence or empty
if [ "$_BASHIO_COLOR" != "1" ]; then return 0; fi
case "$1" in
blue) printf '\033[34m' ;;
green) printf '\033[32m' ;;
yellow) printf '\033[33m' ;;
red) printf '\033[31m' ;;
magenta) printf '\033[35m' ;;
reset) printf '\033[0m' ;;
esac
# $1=name; returns ANSI sequence or empty
if [ "$_BASHIO_COLOR" != "1" ]; then return 0; fi
case "$1" in
blue) printf '\033[34m' ;;
green) printf '\033[32m' ;;
yellow) printf '\033[33m' ;;
red) printf '\033[31m' ;;
magenta) printf '\033[35m' ;;
reset) printf '\033[0m' ;;
esac
}
_bashio_log() {
# $1=color name, $2...=msg
local c="$1"; shift
local pre; pre="$(_bashio_color "$c")"
local rst; rst="$(_bashio_color reset)"
printf '%s%s%s\n' "$pre" "$*" "$rst"
# $1=color name, $2...=msg
local c="$1"
shift
local pre
pre="$(_bashio_color "$c")"
local rst
rst="$(_bashio_color reset)"
printf '%s%s%s\n' "$pre" "$*" "$rst"
}
# Optional JSON options source (single flat object or nested).
# Set STANDALONE_OPTIONS_JSON to a path (e.g., /data/options.json).
# If jq is present, keys can be fetched as .key or .nested.key
_bashio_json_get() {
# $1=key (dot.notation). echoes value or empty; returns 0 always
local key="${1:-}"
local file="${STANDALONE_OPTIONS_JSON:-}"
if [ -z "$file" ] || [ ! -f "$file" ] || ! command -v jq >/dev/null 2>&1; then
return 0
fi
# jq -r returns "null" for missing; convert to empty
local val
val="$(jq -er --arg k "$key" '. as $r | getpath(($k|split("."))) // empty' "$file" 2>/dev/null || true)"
[ "$val" = "null" ] && val=""
printf '%s' "$val"
# $1=key (dot.notation). echoes value or empty; returns 0 always
local key="${1:-}"
local file="${STANDALONE_OPTIONS_JSON:-}"
if [ -z "$file" ] || [ ! -f "$file" ] || ! command -v jq > /dev/null 2>&1; then
return 0
fi
# jq -r returns "null" for missing; convert to empty
local val
val="$(jq -er --arg k "$key" '. as $r | getpath(($k|split("."))) // empty' "$file" 2> /dev/null || true)"
[ "$val" = "null" ] && val=""
printf '%s' "$val"
}
# Map a bashio "key" to an env var name.
@@ -59,127 +62,128 @@ _bashio_json_get() {
# 3) dot->underscore, dash->underscore (upper & lower)
# 4) with prefixes: CFG_, CONFIG_, ADDON_, OPTION_, OPT_
_bashio_env_get() {
# $1=key
local key="${1:-}"
[ -z "$key" ] && return 0
# $1=key
local key="${1:-}"
[ -z "$key" ] && return 0
local variants=()
variants+=("$key")
variants+=("$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')")
variants+=("$(printf '%s' "$key" | tr '.' '_' | tr '-' '_' )")
variants+=("$(printf '%s' "$key" | tr '.' '_' | tr '-' '_' | tr '[:lower:]' '[:upper:]')")
local variants=()
variants+=("$key")
variants+=("$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')")
variants+=("$(printf '%s' "$key" | tr '.' '_' | tr '-' '_')")
variants+=("$(printf '%s' "$key" | tr '.' '_' | tr '-' '_' | tr '[:lower:]' '[:upper:]')")
local prefixes=(""
"CFG_" "CONFIG_" "ADDON_" "OPTION_" "OPT_")
local prefixes=(""
"CFG_" "CONFIG_" "ADDON_" "OPTION_" "OPT_")
local v p name
for v in "${variants[@]}"; do
for p in "${prefixes[@]}"; do
name="${p}${v}"
if [ -n "${!name+x}" ]; then
printf '%s' "${!name}"
return 0
fi
local v p name
for v in "${variants[@]}"; do
for p in "${prefixes[@]}"; do
name="${p}${v}"
if [ -n "${!name+x}" ]; then
printf '%s' "${!name}"
return 0
fi
done
done
done
}
# Helper: true/false parsing
_bashio_is_true() {
# $1=value
case "${1:-}" in
1|true|TRUE|yes|YES|on|On) return 0 ;;
*) return 1 ;;
esac
# $1=value
case "${1:-}" in
1 | true | TRUE | yes | YES | on | On) return 0 ;;
*) return 1 ;;
esac
}
# Net wait using /dev/tcp (POSIX bash) with a timeout
_bashio_tcp_wait() {
# $1=host $2=port $3=timeout(s, default 30)
local host="$1" port="$2" to="${3:-30}"
local start now
start="$(date +%s)"
while :; do
if exec 3<>"/dev/tcp/${host}/${port}" 2>/dev/null; then
exec 3>&- 3<&-
return 0
fi
now="$(date +%s)"
if [ $((now - start)) -ge "$to" ]; then
return 1
fi
sleep 1
done
# $1=host $2=port $3=timeout(s, default 30)
local host="$1" port="$2" to="${3:-30}"
local start now
start="$(date +%s)"
while :; do
if exec 3<> "/dev/tcp/${host}/${port}" 2> /dev/null; then
exec 3>&- 3<&-
return 0
fi
now="$(date +%s)"
if [ $((now - start)) -ge "$to" ]; then
return 1
fi
sleep 1
done
}
# -------- logs ---------------------------------------------------------------
bashio::log.blue() { _bashio_log blue "$*"; }
bashio::log.green() { _bashio_log green "$*"; }
bashio::log.yellow() { _bashio_log yellow "$*"; }
bashio::log.red() { _bashio_log red "$*"; }
bashio::log.blue() { _bashio_log blue "$*"; }
bashio::log.green() { _bashio_log green "$*"; }
bashio::log.yellow() { _bashio_log yellow "$*"; }
bashio::log.red() { _bashio_log red "$*"; }
bashio::log.magenta() { _bashio_log magenta "$*"; }
# compatibility aliases often used
bashio::log.info() { bashio::log.blue "$@"; }
bashio::log.warning() { bashio::log.yellow "$@"; }
bashio::log.error() { bashio::log.red "$@"; }
bashio::log.debug() { printf '%s\n' "$*"; }
bashio::log.info() { bashio::log.blue "$@"; }
bashio::log.warning() { bashio::log.yellow "$@"; }
bashio::log.error() { bashio::log.red "$@"; }
bashio::log.debug() { printf '%s\n' "$*"; }
# -------- supervisor & addon meta -------------------------------------------
# In standalone, "ping" always fails unless forced
bashio::supervisor.ping() {
if _bashio_is_true "${STANDALONE_FORCE_SUPERVISOR_PING:-}"; then
return 0
fi
return 1
if _bashio_is_true "${STANDALONE_FORCE_SUPERVISOR_PING:-}"; then
return 0
fi
return 1
}
# Add-on metadata (use env or sensible defaults)
bashio::addon.name() { printf '%s' "${ADDON_NAME:-Standalone container}"; }
bashio::addon.description() { printf '%s' "${ADDON_DESCRIPTION:-Running without Home Assistant Supervisor}"; }
bashio::addon.version() { printf '%s' "${BUILD_VERSION:-1.0}"; }
bashio::addon.version_latest(){ printf '%s' "${ADDON_VERSION_LATEST:-${BUILD_VERSION:-1.0}}"; }
bashio::addon.name() { printf '%s' "${ADDON_NAME:-Standalone container}"; }
bashio::addon.description() { printf '%s' "${ADDON_DESCRIPTION:-Running without Home Assistant Supervisor}"; }
bashio::addon.version() { printf '%s' "${BUILD_VERSION:-1.0}"; }
bashio::addon.version_latest() { printf '%s' "${ADDON_VERSION_LATEST:-${BUILD_VERSION:-1.0}}"; }
bashio::addon.update_available() {
if [ "${ADDON_VERSION_LATEST:-}" != "" ] && [ "${ADDON_VERSION_LATEST:-}" != "${BUILD_VERSION:-}" ]; then
printf '%s' "true"; return 0
fi
printf '%s' "false"
if [ "${ADDON_VERSION_LATEST:-}" != "" ] && [ "${ADDON_VERSION_LATEST:-}" != "${BUILD_VERSION:-}" ]; then
printf '%s' "true"
return 0
fi
printf '%s' "false"
}
bashio::addon.ingress_port() { printf '%s' "${ADDON_INGRESS_PORT:-}"; }
bashio::addon.ingress_port() { printf '%s' "${ADDON_INGRESS_PORT:-}"; }
bashio::addon.ingress_entry() { printf '%s' "${ADDON_INGRESS_ENTRY:-}"; }
bashio::addon.ip_address() { printf '%s' "${ADDON_IP_ADDRESS:-}"; }
bashio::addon.ip_address() { printf '%s' "${ADDON_IP_ADDRESS:-}"; }
# Ports:
# - numeric arg "8080" -> env PORT_8080 or ADDON_PORT_8080, falling back to the number
# - non-numeric "WEB_PORT" -> resolve as config/env key
bashio::addon.port() {
local arg="${1:-}"
if [[ "$arg" =~ ^[0-9]+$ ]]; then
local v
v="$(_bashio_env_get "PORT_${arg}")"
[ -z "$v" ] && v="$(_bashio_env_get "ADDON_PORT_${arg}")"
printf '%s' "${v:-$arg}"
else
printf '%s' "$(_bashio_env_get "$arg")"
fi
local arg="${1:-}"
if [[ "$arg" =~ ^[0-9]+$ ]]; then
local v
v="$(_bashio_env_get "PORT_${arg}")"
[ -z "$v" ] && v="$(_bashio_env_get "ADDON_PORT_${arg}")"
printf '%s' "${v:-$arg}"
else
printf '%s' "$(_bashio_env_get "$arg")"
fi
}
# -------- system info --------------------------------------------------------
bashio::info.operating_system() {
if [ -r /etc/os-release ]; then
. /etc/os-release
printf '%s' "${PRETTY_NAME:-${NAME:-Linux}}"
else
printf '%s' "Linux"
fi
if [ -r /etc/os-release ]; then
. /etc/os-release
printf '%s' "${PRETTY_NAME:-${NAME:-Linux}}"
else
printf '%s' "Linux"
fi
}
bashio::info.arch() { uname -m; }
bashio::info.machine() { uname -m; }
bashio::info.homeassistant(){ printf '%s' "standalone"; }
bashio::info.supervisor() { printf '%s' "standalone"; }
bashio::info.arch() { uname -m; }
bashio::info.machine() { uname -m; }
bashio::info.homeassistant() { printf '%s' "standalone"; }
bashio::info.supervisor() { printf '%s' "standalone"; }
# -------- config -------------------------------------------------------------
@@ -187,24 +191,30 @@ bashio::info.supervisor() { printf '%s' "standalone"; }
# 1) ENV (several name variants/prefixes)
# 2) JSON file via STANDALONE_OPTIONS_JSON (jq required)
bashio::config() {
local key="${1:-}"
local v
v="$(_bashio_env_get "$key")"
if [ -z "$v" ]; then
v="$(_bashio_json_get "$key")"
fi
printf '%s' "${v:-}"
local key="${1:-}"
local v
v="$(_bashio_env_get "$key")"
if [ -z "$v" ]; then
v="$(_bashio_json_get "$key")"
fi
printf '%s' "${v:-}"
}
bashio::config.has_value() { local k="$1"; [ -n "$(bashio::config "$k")" ]; }
bashio::config.true() { local k="$1"; _bashio_is_true "$(bashio::config "$k")"; }
bashio::config.has_value() {
local k="$1"
[ -n "$(bashio::config "$k")" ]
}
bashio::config.true() {
local k="$1"
_bashio_is_true "$(bashio::config "$k")"
}
# Some add-ons call "require.ssl" (noop by default)
bashio::config.require.ssl(){ printf '%s' "${REQUIRE_SSL:-true}"; }
bashio::config.require.ssl() { printf '%s' "${REQUIRE_SSL:-true}"; }
# -------- variables & fs helpers --------------------------------------------
bashio::var.true() { _bashio_is_true "${1:-}"; }
bashio::var.true() { _bashio_is_true "${1:-}"; }
bashio::var.has_value() { [ -n "${1:-}" ]; }
bashio::fs.directory_exists() { [ -d "$1" ]; }
@@ -213,8 +223,8 @@ bashio::fs.directory_exists() { [ -d "$1" ]; }
# Wait for TCP service: bashio::net.wait_for host port [timeout]
bashio::net.wait_for() {
local host="$1" port="$2" to="${3:-30}"
_bashio_tcp_wait "$host" "$port" "$to"
local host="$1" port="$2" to="${3:-30}"
_bashio_tcp_wait "$host" "$port" "$to"
}
# Discovery stubs; map to common env names, or JSON:
@@ -222,33 +232,33 @@ bashio::net.wait_for() {
# bashio::services "mqtt" "host"
# bashio::services "mysql" "port"
bashio::services() {
local svc="${1:-}" key="${2:-}"
[ -z "$svc" ] && return 0
local upper svc_upper var v
upper="$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')"
svc_upper="$(printf '%s' "$svc" | tr '[:lower:]' '[:upper:]')"
local svc="${1:-}" key="${2:-}"
[ -z "$svc" ] && return 0
local upper svc_upper var v
upper="$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')"
svc_upper="$(printf '%s' "$svc" | tr '[:lower:]' '[:upper:]')"
# Common mappings
case "$svc_upper:$upper" in
MQTT:HOST) var="MQTT_HOST" ;;
MQTT:PORT) var="MQTT_PORT" ;;
MQTT:USERNAME) var="MQTT_USER" ;;
MQTT:PASSWORD) var="MQTT_PASSWORD" ;;
MQTT:TLS) var="MQTT_TLS" ;;
MYSQL:HOST|MARIADB:HOST) var="DB_HOST" ;;
MYSQL:PORT|MARIADB:PORT) var="DB_PORT" ;;
MYSQL:USERNAME|MARIADB:USERNAME) var="DB_USER" ;;
MYSQL:PASSWORD|MARIADB:PASSWORD) var="DB_PASSWORD" ;;
MYSQL:DATABASE|MARIADB:DATABASE) var="DB_NAME" ;;
*) var="${svc_upper}_${upper}" ;;
esac
# Common mappings
case "$svc_upper:$upper" in
MQTT:HOST) var="MQTT_HOST" ;;
MQTT:PORT) var="MQTT_PORT" ;;
MQTT:USERNAME) var="MQTT_USER" ;;
MQTT:PASSWORD) var="MQTT_PASSWORD" ;;
MQTT:TLS) var="MQTT_TLS" ;;
MYSQL:HOST | MARIADB:HOST) var="DB_HOST" ;;
MYSQL:PORT | MARIADB:PORT) var="DB_PORT" ;;
MYSQL:USERNAME | MARIADB:USERNAME) var="DB_USER" ;;
MYSQL:PASSWORD | MARIADB:PASSWORD) var="DB_PASSWORD" ;;
MYSQL:DATABASE | MARIADB:DATABASE) var="DB_NAME" ;;
*) var="${svc_upper}_${upper}" ;;
esac
v="$(_bashio_env_get "$var")"
if [ -z "$v" ] && [ -n "${STANDALONE_OPTIONS_JSON:-}" ]; then
v="$(_bashio_json_get "services.${svc}.${key}")"
[ -z "$v" ] && v="$(_bashio_json_get "${svc}.${key}")"
fi
printf '%s' "${v:-}"
v="$(_bashio_env_get "$var")"
if [ -z "$v" ] && [ -n "${STANDALONE_OPTIONS_JSON:-}" ]; then
v="$(_bashio_json_get "services.${svc}.${key}")"
[ -z "$v" ] && v="$(_bashio_json_get "${svc}.${key}")"
fi
printf '%s' "${v:-}"
}
# ----- extras for broader compatibility --------------------------------------
@@ -258,165 +268,177 @@ _BASHIO_CACHE_DIR="${BASHIO_CACHE_DIR:-/tmp/.bashio}"
mkdir -p "$_BASHIO_CACHE_DIR"
bashio::cache.exists() { [ -f "$_BASHIO_CACHE_DIR/${1}.cache" ]; }
bashio::cache.get() { [ -f "$_BASHIO_CACHE_DIR/${1}.cache" ] && cat "$_BASHIO_CACHE_DIR/${1}.cache"; }
bashio::cache.set() { mkdir -p "$_BASHIO_CACHE_DIR"; printf '%s' "${2:-}" > "$_BASHIO_CACHE_DIR/${1}.cache"; }
bashio::cache.get() { [ -f "$_BASHIO_CACHE_DIR/${1}.cache" ] && cat "$_BASHIO_CACHE_DIR/${1}.cache"; }
bashio::cache.set() {
mkdir -p "$_BASHIO_CACHE_DIR"
printf '%s' "${2:-}" > "$_BASHIO_CACHE_DIR/${1}.cache"
}
# Filesystem helpers frequently used
bashio::fs.file_exists() { [ -f "$1" ]; }
bashio::fs.directory_exists() { [ -d "$1" ]; } # already defined earlier; keep if present
bashio::fs.file_contains() { local f="$1" p="$2"; [ -f "$f" ] && grep -q -- "$p" "$f"; }
bashio::fs.file_exists() { [ -f "$1" ]; }
bashio::fs.directory_exists() { [ -d "$1" ]; } # already defined earlier; keep if present
bashio::fs.file_contains() {
local f="$1" p="$2"
[ -f "$f" ] && grep -q -- "$p" "$f"
}
# jq wrapper (some add-ons call bashio::jq)
bashio::jq() { command -v jq >/dev/null 2>&1 && jq "$@"; }
bashio::jq() { command -v jq > /dev/null 2>&1 && jq "$@"; }
# env presence (even if empty) used by config.exists
_bashio_env_has() {
local key="$1" p v name
[ -z "$key" ] && return 1
local variants=(
"$key"
"$(printf '%s' "$key" | tr '.' '_' )"
"$(printf '%s' "$key" | tr '.' '_' | tr '[:lower:]' '[:upper:]')"
"$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')"
)
for v in "${variants[@]}"; do
for p in "" "CFG_" "CONFIG_" "ADDON_" "OPTION_" "OPT_"; do
name="${p}${v}"
if [ -n "${!name+x}" ]; then # defined, even if empty
printf '%s' "$name"
return 0
fi
local key="$1" p v name
[ -z "$key" ] && return 1
local variants=(
"$key"
"$(printf '%s' "$key" | tr '.' '_')"
"$(printf '%s' "$key" | tr '.' '_' | tr '[:lower:]' '[:upper:]')"
"$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')"
)
for v in "${variants[@]}"; do
for p in "" "CFG_" "CONFIG_" "ADDON_" "OPTION_" "OPT_"; do
name="${p}${v}"
if [ -n "${!name+x}" ]; then # defined, even if empty
printf '%s' "$name"
return 0
fi
done
done
done
return 1
return 1
}
# config.exists : key is present (env or JSON), even if value is empty
bashio::config.exists() {
local key="$1" file="${STANDALONE_OPTIONS_JSON:-}"
_bashio_env_has "$key" && return 0
if [ -n "$file" ] && command -v jq >/dev/null 2>&1; then
jq -e --arg k "$key" 'haspath(($k|split(".")))' "$file" >/dev/null 2>&1
return $?
fi
return 1
local key="$1" file="${STANDALONE_OPTIONS_JSON:-}"
_bashio_env_has "$key" && return 0
if [ -n "$file" ] && command -v jq > /dev/null 2>&1; then
jq -e --arg k "$key" 'haspath(($k|split(".")))' "$file" > /dev/null 2>&1
return $?
fi
return 1
}
# addon.option : write/delete option in JSON when possible; fallback no-op/env
bashio::addon.option() {
local key="$1" value="${2-__BASHIO_UNSET__}" file="${STANDALONE_OPTIONS_JSON:-}"
if [ -n "$file" ] && command -v jq >/dev/null 2>&1; then
local tmp; tmp="$(mktemp)"
if [ "$value" = "__BASHIO_UNSET__" ]; then
jq --arg k "$key" 'delpath(($k|split(".")))' "$file" >"$tmp" && mv "$tmp" "$file"
else
jq --arg k "$key" --arg v "$value" 'setpath(($k|split(".")); $v)' "$file" >"$tmp" && mv "$tmp" "$file"
local key="$1" value="${2-__BASHIO_UNSET__}" file="${STANDALONE_OPTIONS_JSON:-}"
if [ -n "$file" ] && command -v jq > /dev/null 2>&1; then
local tmp
tmp="$(mktemp)"
if [ "$value" = "__BASHIO_UNSET__" ]; then
jq --arg k "$key" 'delpath(($k|split(".")))' "$file" > "$tmp" && mv "$tmp" "$file"
else
jq --arg k "$key" --arg v "$value" 'setpath(($k|split(".")); $v)' "$file" > "$tmp" && mv "$tmp" "$file"
fi
return 0
fi
# Fallbacks: export as env or treat delete as no-op
if [ "$value" != "__BASHIO_UNSET__" ]; then
export "$(printf '%s' "$key" | tr '.' '_' | tr '-' '_')"="$value"
fi
return 0
fi
# Fallbacks: export as env or treat delete as no-op
if [ "$value" != "__BASHIO_UNSET__" ]; then
export "$(printf '%s' "$key" | tr '.' '_' | tr '-' '_')"="$value"
fi
}
# services.available : check if we can resolve at least a host for the service
bashio::services.available() {
local svc="$1" host; host="$(bashio::services "$svc" "host")"
[ -n "$host" ]
local svc="$1" host
host="$(bashio::services "$svc" "host")"
[ -n "$host" ]
}
# var helpers
bashio::var.false() { ! _bashio_is_true "${1:-}"; }
bashio::var.has_value() { [ -n "${1:-}" ]; } # already present; keep if defined
bashio::var.false() { ! _bashio_is_true "${1:-}"; }
bashio::var.has_value() { [ -n "${1:-}" ]; } # already present; keep if defined
# exits used by many add-ons
bashio::exit.ok() { exit 0; }
bashio::exit.nok() { local m="${1:-}"; [ -n "$m" ] && bashio::log.red "$m"; exit 1; }
bashio::exit.ok() { exit 0; }
bashio::exit.nok() {
local m="${1:-}"
[ -n "$m" ] && bashio::log.red "$m"
exit 1
}
# core.check : Supervisor does a config check; allow an overridable command
# Set STANDALONE_CORE_CHECK_CMD="hass --script check_config -c /config" to enable
bashio::core.check() {
if [ -n "${STANDALONE_CORE_CHECK_CMD:-}" ]; then
eval "$STANDALONE_CORE_CHECK_CMD"
else
return 0
fi
if [ -n "${STANDALONE_CORE_CHECK_CMD:-}" ]; then
eval "$STANDALONE_CORE_CHECK_CMD"
else
return 0
fi
}
# --- improvements & extra shims ---------------------------------------------
# Respect NO_COLOR and dumb terminals
if [ -n "${NO_COLOR:-}" ] || [ "${TERM:-}" = "dumb" ]; then
_BASHIO_COLOR=0
_BASHIO_COLOR=0
fi
# net.wait_for: prefer nc if available, fallback to /dev/tcp
_bashio_tcp_wait_nc() {
# $1=host $2=port $3=timeout(s)
command -v nc >/dev/null 2>&1 || return 1
local host="$1" port="$2" to="${3:-30}"
# BusyBox and OpenBSD nc differ; cover both styles
nc -z -w "$to" "$host" "$port" 2>/dev/null || nc -z "$host" "$port" 2>/dev/null
# $1=host $2=port $3=timeout(s)
command -v nc > /dev/null 2>&1 || return 1
local host="$1" port="$2" to="${3:-30}"
# BusyBox and OpenBSD nc differ; cover both styles
nc -z -w "$to" "$host" "$port" 2> /dev/null || nc -z "$host" "$port" 2> /dev/null
}
bashio::net.wait_for() {
local host="$1" port="$2" to="${3:-30}"
_bashio_tcp_wait_nc "$host" "$port" "$to" && return 0
_bashio_tcp_wait "$host" "$port" "$to"
local host="$1" port="$2" to="${3:-30}"
_bashio_tcp_wait_nc "$host" "$port" "$to" && return 0
_bashio_tcp_wait "$host" "$port" "$to"
}
# DNS helper: bashio::dns.host <hostname> -> prints an IP (or empty)
bashio::dns.host() {
local h="${1:-}"
[ -z "$h" ] && return 1
if command -v getent >/dev/null 2>&1; then
getent ahostsv4 "$h" | awk '{print $1; exit}'
else
# fallback: try busybox nslookup
nslookup "$h" 2>/dev/null | awk '/^Address: /{print $2; exit}'
fi
local h="${1:-}"
[ -z "$h" ] && return 1
if command -v getent > /dev/null 2>&1; then
getent ahostsv4 "$h" | awk '{print $1; exit}'
else
# fallback: try busybox nslookup
nslookup "$h" 2> /dev/null | awk '/^Address: /{print $2; exit}'
fi
}
# Hostname
bashio::host.hostname() {
command -v hostname >/dev/null 2>&1 && hostname || printf '%s' "${HOSTNAME:-unknown}"
command -v hostname > /dev/null 2>&1 && hostname || printf '%s' "${HOSTNAME:-unknown}"
}
# Home Assistant token (no Supervisor; read from env or JSON)
bashio::homeassistant.token() {
local t="${HOMEASSISTANT_TOKEN:-${HASS_TOKEN:-}}"
if [ -z "$t" ] && [ -n "${STANDALONE_OPTIONS_JSON:-}" ] && command -v jq >/dev/null 2>&1; then
t="$(jq -er '.homeassistant.token // empty' "$STANDALONE_OPTIONS_JSON" 2>/dev/null || true)"
fi
printf '%s' "${t:-}"
local t="${HOMEASSISTANT_TOKEN:-${HASS_TOKEN:-}}"
if [ -z "$t" ] && [ -n "${STANDALONE_OPTIONS_JSON:-}" ] && command -v jq > /dev/null 2>&1; then
t="$(jq -er '.homeassistant.token // empty' "$STANDALONE_OPTIONS_JSON" 2> /dev/null || true)"
fi
printf '%s' "${t:-}"
}
# config.array:
# Accepts CSV ("a,b,c"), space/newline-separated text, or JSON array ["a","b"].
# Prints one item per line (common pattern in add-ons: `mapfile -t arr < <(bashio::config.array key)`).
bashio::config.array() {
local key="${1:-}" raw val
raw="$(bashio::config "$key")"
[ -z "$raw" ] && return 0
local key="${1:-}" raw val
raw="$(bashio::config "$key")"
[ -z "$raw" ] && return 0
# JSON array?
if command -v jq >/dev/null 2>&1 && printf '%s' "$raw" | jq -e . >/dev/null 2>&1; then
printf '%s' "$raw" | jq -r '.[]' 2>/dev/null && return 0
fi
# JSON array?
if command -v jq > /dev/null 2>&1 && printf '%s' "$raw" | jq -e . > /dev/null 2>&1; then
printf '%s' "$raw" | jq -r '.[]' 2> /dev/null && return 0
fi
# CSV -> newline
if printf '%s' "$raw" | grep -q ','; then
printf '%s' "$raw" | tr ',' '\n'
return 0
fi
# CSV -> newline
if printf '%s' "$raw" | grep -q ','; then
printf '%s' "$raw" | tr ',' '\n'
return 0
fi
# Already space/newline-separated
printf '%s\n' "$raw"
# Already space/newline-separated
printf '%s\n' "$raw"
}
# Optional: common require.* shims (treat as advisory in standalone)
bashio::config.require.username() { :; }
bashio::config.require.password() { :; }
bashio::config.require.port() { :; }
bashio::config.require.port() { :; }
# -------- end ----------------------------------------------------------------

View File

@@ -1,8 +1,7 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
set -e # Exit immediately if a command exits with a non-zero status
set -e # Exit immediately if a command exits with a non-zero status
# Detect if this is PID1 (main container process) — do this once at the start
PID1=false
@@ -38,7 +37,7 @@ for candidate in "${candidate_shebangs[@]}"; do
# Test if command exists and can actually execute a shell command (for shells)
if [ -x "$command_path" ]; then
# Try as both 'sh -c' and 'bashio echo' style
if "$command_path" -c 'echo yes' >/dev/null 2>&1 || "$command_path" echo "yes" >/dev/null 2>&1; then
if "$command_path" -c 'echo yes' > /dev/null 2>&1 || "$command_path" echo "yes" > /dev/null 2>&1; then
shebang="$candidate"
break
fi
@@ -91,7 +90,7 @@ done
# Start run scripts in services.d and s6-overlay/s6-rc.d if PID1
if $PID1; then
shopt -s nullglob # Don't expand unmatched globs to themselves
shopt -s nullglob # Don't expand unmatched globs to themselves
for runfile in /etc/services.d/*/run /etc/s6-overlay/s6-rc.d/*/run; do
[ -f "$runfile" ] || continue
echo "Starting: $runfile"
@@ -103,7 +102,8 @@ if $PID1; then
sed -i -E 's|s6-svwait[[:space:]]+-d[[:space:]]+([^[:space:]]+)|bash -c '\''while [ -f \1/supervise/pid ]; do sleep 0.5; done'\''|g' "$runfile"
sed -i -E 's|s6-svwait[[:space:]]+-u[[:space:]]+([^[:space:]]+)|bash -c '\''until [ -f \1/supervise/pid ]; do sleep 0.5; done'\''|g' "$runfile"
chmod +x "$runfile"
( exec "$runfile" ) & true
(exec "$runfile") &
true
done
shopt -u nullglob
fi
@@ -119,19 +119,19 @@ if $PID1; then
terminate() {
echo "Termination signal received, forwarding to subprocesses..."
# Terminate all direct child processes
if command -v pgrep &>/dev/null; then
if command -v pgrep &> /dev/null; then
for pid in $(pgrep -P $$); do
echo "Terminating child PID $pid"
kill -TERM "$pid" 2>/dev/null || echo "Failed to terminate PID $pid"
kill -TERM "$pid" 2> /dev/null || echo "Failed to terminate PID $pid"
done
else
# Fallback: Scan /proc for children
for pid in /proc/[0-9]*/; do
pid=${pid#/proc/}
pid=${pid%/}
if [[ "$pid" -ne 1 ]] && grep -q "^PPid:\s*$$" "/proc/$pid/status" 2>/dev/null; then
if [[ "$pid" -ne 1 ]] && grep -q "^PPid:\s*$$" "/proc/$pid/status" 2> /dev/null; then
echo "Terminating child PID $pid"
kill -TERM "$pid" 2>/dev/null || echo "Failed to terminate PID $pid"
kill -TERM "$pid" 2> /dev/null || echo "Failed to terminate PID $pid"
fi
done
fi