mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-01-08 08:51:03 +01:00
fix: auto-fix linting issues
This commit is contained in:
committed by
github-actions[bot]
parent
205a7e9b84
commit
0d3c7619b4
@@ -25,7 +25,7 @@ if [[ "${ADDON_TYPE:-media}" == "media" ]]; then
|
||||
if bashio::config.has_value "transcoding_quality"; then
|
||||
validate_string "transcoding_quality" "^(low|medium|high|ultra)$" "Transcoding quality (low, medium, high, ultra)" false
|
||||
fi
|
||||
|
||||
|
||||
# Validate maximum concurrent streams
|
||||
if bashio::config.has_value "max_streams"; then
|
||||
validate_numeric "max_streams" 1 20 "Maximum concurrent streams (1-20)" false
|
||||
@@ -38,7 +38,7 @@ if [[ "${ADDON_TYPE:-file}" == "file" ]]; then
|
||||
if bashio::config.has_value "base_folder"; then
|
||||
validate_path "base_folder" "/config" "Base folder for file browsing" false
|
||||
fi
|
||||
|
||||
|
||||
# Validate disable thumbnails setting
|
||||
if bashio::config.has_value "disable_thumbnails"; then
|
||||
validate_boolean "disable_thumbnails" "Disable thumbnail generation" false
|
||||
@@ -51,12 +51,12 @@ if [[ "${ADDON_TYPE:-network}" == "network" ]]; then
|
||||
if bashio::config.has_value "target_ip"; then
|
||||
validate_ip "target_ip" "Target device IP address"
|
||||
fi
|
||||
|
||||
|
||||
# Validate gateway IP
|
||||
if bashio::config.has_value "gateway_ip"; then
|
||||
validate_ip "gateway_ip" "Network gateway IP address"
|
||||
fi
|
||||
|
||||
|
||||
# Validate block duration
|
||||
if bashio::config.has_value "block_duration"; then
|
||||
validate_numeric "block_duration" 1 3600 "Block duration in seconds (1-3600)"
|
||||
@@ -70,25 +70,25 @@ fi
|
||||
# Validate authentication settings
|
||||
if bashio::config.has_value "enable_auth"; then
|
||||
validate_boolean "enable_auth" "Enable authentication"
|
||||
|
||||
|
||||
if bashio::config.true "enable_auth"; then
|
||||
# If auth is enabled, validate credentials
|
||||
validate_string "username" "^[a-zA-Z0-9_-]{3,20}$" "Username (3-20 alphanumeric characters)"
|
||||
|
||||
|
||||
# Validate password strength
|
||||
if bashio::config.has_value "password"; then
|
||||
local password
|
||||
password=$(bashio::config "password")
|
||||
|
||||
|
||||
if [[ ${#password} -lt 8 ]]; then
|
||||
bashio::log.fatal "Password too short. Minimum 8 characters required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! "$password" =~ [A-Z] ]] || [[ ! "$password" =~ [a-z] ]] || [[ ! "$password" =~ [0-9] ]]; then
|
||||
bashio::log.warning "⚠️ Weak password detected. Consider using uppercase, lowercase, and numbers."
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.debug "✅ Validated password strength"
|
||||
fi
|
||||
fi
|
||||
@@ -108,4 +108,4 @@ bashio::log.info "Starting application with validated configuration..."
|
||||
export VALIDATED_CONFIG="true"
|
||||
export CONFIG_VALIDATION_TIME="$(date -Iseconds)"
|
||||
|
||||
bashio::log.debug "Environment prepared with validated configuration"
|
||||
bashio::log.debug "Environment prepared with validated configuration"
|
||||
|
||||
@@ -13,17 +13,17 @@ echo "📦 Installing packages securely: $PACKAGES"
|
||||
# Install dependencies securely
|
||||
install_dependencies() {
|
||||
echo "🔧 Installing required dependencies..."
|
||||
|
||||
|
||||
# Install bash if needed
|
||||
if ! command -v bash > /dev/null 2>&1; then
|
||||
(apt-get update && apt-get install -yqq --no-install-recommends bash || apk add --no-cache bash) > /dev/null
|
||||
fi
|
||||
|
||||
# Install curl if needed
|
||||
|
||||
# Install curl if needed
|
||||
if ! command -v curl > /dev/null 2>&1; then
|
||||
(apt-get update && apt-get install -yqq --no-install-recommends curl || apk add --no-cache curl) > /dev/null
|
||||
fi
|
||||
|
||||
|
||||
# Install ca-certificates for SSL verification
|
||||
(apt-get update && apt-get install -yqq --no-install-recommends ca-certificates || apk add --no-cache ca-certificates) > /dev/null 2>&1 || true
|
||||
}
|
||||
@@ -33,9 +33,9 @@ secure_download() {
|
||||
local url="$1"
|
||||
local output_file="$2"
|
||||
local expected_sha256="${3:-}"
|
||||
|
||||
|
||||
echo "🔒 Downloading: $(basename "$output_file")"
|
||||
|
||||
|
||||
# Download with security headers and timeouts
|
||||
if ! curl -fsSL \
|
||||
--retry 3 \
|
||||
@@ -48,15 +48,15 @@ secure_download() {
|
||||
echo "❌ Failed to download: $url" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
# Verify checksum if provided
|
||||
if [ -n "$expected_sha256" ]; then
|
||||
local actual_sha256
|
||||
actual_sha256=$(sha256sum "$output_file" | cut -d' ' -f1)
|
||||
|
||||
|
||||
if [ "$actual_sha256" != "$expected_sha256" ]; then
|
||||
echo "❌ Checksum verification failed for $output_file" >&2
|
||||
echo "Expected: $expected_sha256" >&2
|
||||
echo "Expected: $expected_sha256" >&2
|
||||
echo "Actual: $actual_sha256" >&2
|
||||
rm -f "$output_file"
|
||||
return 1
|
||||
@@ -65,7 +65,7 @@ secure_download() {
|
||||
else
|
||||
echo "⚠️ No checksum provided - consider adding one for security"
|
||||
fi
|
||||
|
||||
|
||||
# Set secure permissions
|
||||
chmod 755 "$output_file"
|
||||
}
|
||||
@@ -73,21 +73,21 @@ secure_download() {
|
||||
# Main execution
|
||||
main() {
|
||||
echo "🛡️ Starting secure package installation..."
|
||||
|
||||
|
||||
# Install dependencies
|
||||
install_dependencies
|
||||
|
||||
|
||||
# For now, we'll download without checksum but with secure practices
|
||||
# TODO: Add checksums for ha_automatic_packages.sh in future releases
|
||||
echo "📥 Downloading package installer..."
|
||||
|
||||
|
||||
local script_url="https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_automatic_packages.sh"
|
||||
local script_file="/ha_automatic_packages.sh"
|
||||
|
||||
|
||||
# Download securely (without checksum for now - to be added)
|
||||
if secure_download "$script_url" "$script_file" ""; then
|
||||
echo "🏃 Executing package installer..."
|
||||
|
||||
|
||||
# Execute with error handling
|
||||
if bash "$script_file" "${PACKAGES:-}"; then
|
||||
echo "✅ Package installation completed successfully"
|
||||
@@ -95,7 +95,7 @@ main() {
|
||||
echo "❌ Package installation failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Clean up
|
||||
rm -f "$script_file"
|
||||
echo "🧹 Cleanup completed"
|
||||
@@ -106,4 +106,4 @@ main() {
|
||||
}
|
||||
|
||||
# Execute main function
|
||||
main "$@"
|
||||
main "$@"
|
||||
|
||||
@@ -13,27 +13,27 @@ validate_string() {
|
||||
local pattern="$2"
|
||||
local description="$3"
|
||||
local required="${4:-true}"
|
||||
|
||||
|
||||
if ! bashio::config.has_value "$config_key"; then
|
||||
if [[ "$required" == "true" ]]; then
|
||||
bashio::log.fatal "Required configuration '$config_key' not found"
|
||||
bashio::log.fatal "Expected: $description"
|
||||
exit 1
|
||||
else
|
||||
return 0 # Optional field not provided
|
||||
return 0 # Optional field not provided
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local value
|
||||
value=$(bashio::config "$config_key")
|
||||
|
||||
|
||||
if [[ ! $value =~ $pattern ]]; then
|
||||
bashio::log.fatal "Invalid format for '$config_key': '$value'"
|
||||
bashio::log.fatal "Expected: $description"
|
||||
bashio::log.fatal "Pattern: $pattern"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.debug "✅ Validated $config_key: $value"
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ validate_numeric() {
|
||||
local max_val="$3"
|
||||
local description="$4"
|
||||
local required="${5:-true}"
|
||||
|
||||
|
||||
if ! bashio::config.has_value "$config_key"; then
|
||||
if [[ "$required" == "true" ]]; then
|
||||
bashio::log.fatal "Required configuration '$config_key' not found"
|
||||
@@ -53,24 +53,24 @@ validate_numeric() {
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local value
|
||||
value=$(bashio::config "$config_key")
|
||||
|
||||
|
||||
# Check if it's a valid number
|
||||
if ! [[ "$value" =~ ^[0-9]+$ ]]; then
|
||||
bashio::log.fatal "Invalid numeric value for '$config_key': '$value'"
|
||||
bashio::log.fatal "Expected: $description"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Check bounds
|
||||
if [[ $value -lt $min_val ]] || [[ $value -gt $max_val ]]; then
|
||||
bashio::log.fatal "Value for '$config_key' out of range: $value"
|
||||
bashio::log.fatal "Expected: $description (range: $min_val-$max_val)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.debug "✅ Validated $config_key: $value"
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ validate_boolean() {
|
||||
local config_key="$1"
|
||||
local description="$2"
|
||||
local required="${3:-true}"
|
||||
|
||||
|
||||
if ! bashio::config.has_value "$config_key"; then
|
||||
if [[ "$required" == "true" ]]; then
|
||||
bashio::log.fatal "Required configuration '$config_key' not found"
|
||||
@@ -88,16 +88,16 @@ validate_boolean() {
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local value
|
||||
value=$(bashio::config "$config_key")
|
||||
|
||||
|
||||
if [[ ! "$value" =~ ^(true|false)$ ]]; then
|
||||
bashio::log.fatal "Invalid boolean value for '$config_key': '$value'"
|
||||
bashio::log.fatal "Expected: $description (true or false)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.debug "✅ Validated $config_key: $value"
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ validate_path() {
|
||||
local base_path="$2"
|
||||
local description="$3"
|
||||
local required="${4:-true}"
|
||||
|
||||
|
||||
if ! bashio::config.has_value "$config_key"; then
|
||||
if [[ "$required" == "true" ]]; then
|
||||
bashio::log.fatal "Required configuration '$config_key' not found"
|
||||
@@ -116,10 +116,10 @@ validate_path() {
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local value
|
||||
value=$(bashio::config "$config_key")
|
||||
|
||||
|
||||
# Check for directory traversal attempts
|
||||
if [[ "$value" =~ \.\. ]] || [[ "$value" =~ ^/ ]]; then
|
||||
bashio::log.fatal "Invalid path for '$config_key': '$value'"
|
||||
@@ -127,30 +127,30 @@ validate_path() {
|
||||
bashio::log.fatal "Expected: $description"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Normalize path and check if it's within base path
|
||||
local full_path="$base_path/$value"
|
||||
local real_path
|
||||
real_path=$(realpath -m "$full_path" 2>/dev/null || echo "$full_path")
|
||||
real_path=$(realpath -m "$full_path" 2> /dev/null || echo "$full_path")
|
||||
local real_base
|
||||
real_base=$(realpath -m "$base_path")
|
||||
|
||||
|
||||
if [[ ! "$real_path" =~ ^"$real_base" ]]; then
|
||||
bashio::log.fatal "Path '$config_key' outside allowed base: '$value'"
|
||||
bashio::log.fatal "Expected: $description"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.debug "✅ Validated path $config_key: $value"
|
||||
}
|
||||
|
||||
# Function to validate URL
|
||||
validate_url() {
|
||||
local config_key="$1"
|
||||
local allowed_schemes="$2" # e.g., "http|https"
|
||||
local allowed_schemes="$2" # e.g., "http|https"
|
||||
local description="$3"
|
||||
local required="${4:-true}"
|
||||
|
||||
|
||||
if ! bashio::config.has_value "$config_key"; then
|
||||
if [[ "$required" == "true" ]]; then
|
||||
bashio::log.fatal "Required configuration '$config_key' not found"
|
||||
@@ -159,20 +159,20 @@ validate_url() {
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local value
|
||||
value=$(bashio::config "$config_key")
|
||||
|
||||
|
||||
# Basic URL validation
|
||||
local url_pattern="^($allowed_schemes)://[A-Za-z0-9.-]+(:[0-9]+)?(/.*)?$"
|
||||
|
||||
|
||||
if [[ ! "$value" =~ $url_pattern ]]; then
|
||||
bashio::log.fatal "Invalid URL for '$config_key': '$value'"
|
||||
bashio::log.fatal "Expected: $description"
|
||||
bashio::log.fatal "Allowed schemes: $allowed_schemes"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.debug "✅ Validated URL $config_key: $value"
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ validate_ip() {
|
||||
local config_key="$1"
|
||||
local description="$2"
|
||||
local required="${3:-true}"
|
||||
|
||||
|
||||
if ! bashio::config.has_value "$config_key"; then
|
||||
if [[ "$required" == "true" ]]; then
|
||||
bashio::log.fatal "Required configuration '$config_key' not found"
|
||||
@@ -190,13 +190,13 @@ validate_ip() {
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local value
|
||||
value=$(bashio::config "$config_key")
|
||||
|
||||
|
||||
# IPv4 validation
|
||||
local ipv4_pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
|
||||
|
||||
|
||||
if [[ "$value" =~ $ipv4_pattern ]]; then
|
||||
# Validate each octet is 0-255
|
||||
IFS='.' read -ra octets <<< "$value"
|
||||
@@ -212,38 +212,38 @@ validate_ip() {
|
||||
bashio::log.fatal "Expected: $description"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.debug "✅ Validated IP $config_key: $value"
|
||||
}
|
||||
|
||||
# Function to validate common add-on configurations
|
||||
validate_common_config() {
|
||||
bashio::log.info "🔍 Validating common configuration parameters..."
|
||||
|
||||
|
||||
# Validate SSL configuration if present
|
||||
if bashio::config.has_value "ssl"; then
|
||||
validate_boolean "ssl" "Enable/disable SSL"
|
||||
|
||||
|
||||
if bashio::config.true "ssl"; then
|
||||
validate_string "certfile" "^[a-zA-Z0-9._-]+\.pem$" "SSL certificate filename" true
|
||||
validate_string "keyfile" "^[a-zA-Z0-9._-]+\.pem$" "SSL private key filename" true
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Validate user/group IDs if present
|
||||
if bashio::config.has_value "PUID"; then
|
||||
validate_numeric "PUID" 0 65535 "User ID (0-65535)"
|
||||
fi
|
||||
|
||||
|
||||
if bashio::config.has_value "PGID"; then
|
||||
validate_numeric "PGID" 0 65535 "Group ID (0-65535)"
|
||||
fi
|
||||
|
||||
|
||||
# Validate timezone if present
|
||||
if bashio::config.has_value "TZ"; then
|
||||
validate_string "TZ" "^[A-Za-z0-9/_+-]+$" "Timezone (e.g., Europe/London)" false
|
||||
fi
|
||||
|
||||
|
||||
bashio::log.info "✅ Common configuration validation completed"
|
||||
}
|
||||
|
||||
@@ -253,4 +253,4 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
bashio::log.info "This library provides secure validation functions for add-on configurations"
|
||||
echo ""
|
||||
bashio::log.info "Usage: source /ha_input_validation.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -11,13 +11,13 @@ secure_download() {
|
||||
local url="$1"
|
||||
local output_file="$2"
|
||||
local expected_sha256="$3"
|
||||
|
||||
|
||||
echo "🔒 Securely downloading: $(basename "$output_file")"
|
||||
|
||||
|
||||
# Download with retry logic
|
||||
local retries=3
|
||||
local retry_delay=2
|
||||
|
||||
|
||||
for i in $(seq 1 $retries); do
|
||||
if curl -fsSL --retry 3 --retry-delay 1 --connect-timeout 10 --max-time 30 "$url" -o "$output_file"; then
|
||||
break
|
||||
@@ -29,13 +29,13 @@ secure_download() {
|
||||
sleep $retry_delay
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# Verify SHA256 checksum if provided
|
||||
if [ -n "$expected_sha256" ]; then
|
||||
echo "🔍 Verifying integrity..."
|
||||
local actual_sha256
|
||||
actual_sha256=$(sha256sum "$output_file" | cut -d' ' -f1)
|
||||
|
||||
|
||||
if [ "$actual_sha256" = "$expected_sha256" ]; then
|
||||
echo "✅ Integrity verification passed"
|
||||
else
|
||||
@@ -48,7 +48,7 @@ secure_download() {
|
||||
else
|
||||
echo "⚠️ No checksum provided - skipping integrity verification"
|
||||
fi
|
||||
|
||||
|
||||
# Set secure permissions
|
||||
chmod 755 "$output_file"
|
||||
echo "🔧 Set secure permissions (755)"
|
||||
@@ -57,17 +57,17 @@ secure_download() {
|
||||
# Function to install common dependencies securely
|
||||
install_dependencies() {
|
||||
echo "📦 Installing secure dependencies..."
|
||||
|
||||
|
||||
# Install bash if needed
|
||||
if ! command -v bash > /dev/null 2>&1; then
|
||||
(apt-get update && apt-get install -yqq --no-install-recommends bash || apk add --no-cache bash) > /dev/null
|
||||
fi
|
||||
|
||||
|
||||
# Install curl if needed
|
||||
if ! command -v curl > /dev/null 2>&1; then
|
||||
(apt-get update && apt-get install -yqq --no-install-recommends curl || apk add --no-cache curl) > /dev/null
|
||||
fi
|
||||
|
||||
|
||||
# Install ca-certificates for SSL verification
|
||||
(apt-get update && apt-get install -yqq --no-install-recommends ca-certificates || apk add --no-cache ca-certificates) > /dev/null 2>&1 || true
|
||||
}
|
||||
@@ -83,4 +83,4 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " secure_download 'https://example.com/script.sh' '/tmp/script.sh' 'abc123...'"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -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
498
.templates/bashio-standalone.sh
Normal file → Executable 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 ----------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -73,9 +73,9 @@ if [ -n "${ALSA_CARD:-}" ]; then
|
||||
fi
|
||||
|
||||
# Define permissions for audio
|
||||
AUDIO_GID=$(stat -c %g /dev/snd/* | head -n1) && \
|
||||
( groupmod -o -g "$AUDIO_GID" audio 2>/dev/null || groupadd -o -g "$AUDIO_GID" audio || true ) && \
|
||||
usermod -aG audio "${USER:-pi}" || true
|
||||
AUDIO_GID=$(stat -c %g /dev/snd/* | head -n1) \
|
||||
&& (groupmod -o -g "$AUDIO_GID" audio 2> /dev/null || groupadd -o -g "$AUDIO_GID" audio || true) \
|
||||
&& usermod -aG audio "${USER:-pi}" || true
|
||||
|
||||
# Fix timezone as per installer
|
||||
CURRENT_TIMEZONE="$(timedatectl show --value --property=Timezone)"
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
],
|
||||
"name": "WIP - Collabora",
|
||||
"options": {
|
||||
"domain": "",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"aliasgroup1": "",
|
||||
"dictionaries": "",
|
||||
"extra_params": ""
|
||||
"domain": "",
|
||||
"extra_params": "",
|
||||
"password": "",
|
||||
"username": ""
|
||||
},
|
||||
"ports": {
|
||||
"9980/tcp": 9980
|
||||
@@ -30,13 +30,13 @@
|
||||
"9980/tcp": "Web interface"
|
||||
},
|
||||
"schema": {
|
||||
"domain": "str?",
|
||||
"username": "str?",
|
||||
"password": "password?",
|
||||
"TZ": "str?",
|
||||
"aliasgroup1": "str?",
|
||||
"dictionaries": "str?",
|
||||
"domain": "str?",
|
||||
"extra_params": "str?",
|
||||
"TZ": "str?"
|
||||
"password": "password?",
|
||||
"username": "str?"
|
||||
},
|
||||
"slug": "collabora",
|
||||
"url": "https://github.com/alexbelgium/hassio-addons",
|
||||
|
||||
@@ -48,7 +48,7 @@ ln -sf "${CONFIG_DEST}" "${COOL_CONFIG}"
|
||||
SYSTEMPLATE_DIR="/opt/cool/systemplate/etc"
|
||||
if [ -d "${SYSTEMPLATE_DIR}" ]; then
|
||||
cp /etc/hosts "${SYSTEMPLATE_DIR}/hosts"
|
||||
cp /etc/hostname "${SYSTEMPLATE_DIR}/hostname" 2>/dev/null || true
|
||||
cp /etc/hostname "${SYSTEMPLATE_DIR}/hostname" 2> /dev/null || true
|
||||
cp /etc/resolv.conf "${SYSTEMPLATE_DIR}/resolv.conf"
|
||||
fi
|
||||
chown 1001 /opt/cool/systemplate
|
||||
|
||||
0
emby/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
emby/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
emby_beta/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
emby_beta/rootfs/etc/services.d/nginx/run
Normal file → Executable file
@@ -57,12 +57,12 @@ The web UI can be found at `<your-ip>:8071` or through the Home Assistant sideba
|
||||
| `keyfile` | str | `privkey.pem` | SSL private key file (in `/ssl/`) |
|
||||
| `NoAuth` | bool | `true` | Disable authentication (resets database when changed) |
|
||||
| `disable_thumbnails` | bool | `true` | Disable thumbnail generation for improved performance |
|
||||
| `base_folder` | str | *(optional)* | Root folder for file browser (defaults to all mapped folders) |
|
||||
| `localdisks` | str | *(optional)* | Local drives to mount (e.g., `sda1,sdb1,MYNAS`) |
|
||||
| `networkdisks` | str | *(optional)* | SMB shares to mount (e.g., `//SERVER/SHARE`) |
|
||||
| `cifsusername` | str | *(optional)* | SMB username for network shares |
|
||||
| `cifspassword` | str | *(optional)* | SMB password for network shares |
|
||||
| `cifsdomain` | str | *(optional)* | SMB domain for network shares |
|
||||
| `base_folder` | str | _(optional)_ | Root folder for file browser (defaults to all mapped folders) |
|
||||
| `localdisks` | str | _(optional)_ | Local drives to mount (e.g., `sda1,sdb1,MYNAS`) |
|
||||
| `networkdisks` | str | _(optional)_ | SMB shares to mount (e.g., `//SERVER/SHARE`) |
|
||||
| `cifsusername` | str | _(optional)_ | SMB username for network shares |
|
||||
| `cifspassword` | str | _(optional)_ | SMB password for network shares |
|
||||
| `cifsdomain` | str | _(optional)_ | SMB domain for network shares |
|
||||
|
||||
### Example Configuration
|
||||
|
||||
|
||||
@@ -78,12 +78,13 @@ environment:
|
||||
}
|
||||
image: ghcr.io/alexbelgium/grampsweb-{arch}
|
||||
init: false
|
||||
options: {
|
||||
options:
|
||||
{
|
||||
"CELERY_NUM_WORKERS": 2,
|
||||
"GUNICORN_NUM_WORKERS": 8,
|
||||
"certfile": "fullchain.pem",
|
||||
"keyfile": "privkey.pem",
|
||||
"ssl": false
|
||||
"ssl": false,
|
||||
}
|
||||
map:
|
||||
- type: addon_config
|
||||
|
||||
0
nextcloud/rootfs/etc/services.d/cron/run
Normal file → Executable file
0
nextcloud/rootfs/etc/services.d/cron/run
Normal file → Executable file
0
nzbget/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
nzbget/rootfs/etc/services.d/nginx/run
Normal file → Executable file
@@ -7,4 +7,4 @@
|
||||
|
||||
# Start omni-tools container content
|
||||
bashio::log.info "Starting application"
|
||||
/./docker-entrypoint.sh nginx -g "daemon off;" &>/proc/1/fd/1
|
||||
/./docker-entrypoint.sh nginx -g "daemon off;" &> /proc/1/fd/1
|
||||
|
||||
@@ -48,7 +48,7 @@ Webui can be found at <http://homeassistant:2342> or through the sidebar using I
|
||||
Configurations can be done through the app webUI, except for the following options.
|
||||
|
||||
**System Requirements**: Minimum 2 cores and 4GB RAM
|
||||
**Default Credentials**:
|
||||
**Default Credentials**:
|
||||
- Username: admin
|
||||
- Password: please_change_password
|
||||
|
||||
|
||||
@@ -170,10 +170,10 @@
|
||||
"cifspassword": "str?",
|
||||
"cifsusername": "str?",
|
||||
"claim": "str",
|
||||
"clear_codecs_folder": "bool?",
|
||||
"localdisks": "str?",
|
||||
"networkdisks": "str?",
|
||||
"skip_permissions_check": "bool?",
|
||||
"clear_codecs_folder": "bool?",
|
||||
"smbv1": "bool?"
|
||||
},
|
||||
"slug": "plex_nas",
|
||||
|
||||
0
qbittorrent/rootfs/etc/s6-overlay/s6-rc.d/svc-qbittorrent/run
Normal file → Executable file
0
qbittorrent/rootfs/etc/s6-overlay/s6-rc.d/svc-qbittorrent/run
Normal file → Executable file
0
qbittorrent/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
qbittorrent/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
qbittorrent/rootfs/etc/services.d/timer/run
Normal file → Executable file
0
qbittorrent/rootfs/etc/services.d/timer/run
Normal file → Executable file
0
scrutiny/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
scrutiny/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
sonarr/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
sonarr/rootfs/etc/services.d/nginx/run
Normal file → Executable file
@@ -98,6 +98,9 @@
|
||||
"80/tcp": "Web interface"
|
||||
},
|
||||
"schema": {
|
||||
"AI_API_KEY": "str?",
|
||||
"AI_MODEL_NAME": "str?",
|
||||
"AI_RATELIMIT": "str?",
|
||||
"ALLOWED_HOSTS": "str?",
|
||||
"DB_TYPE": "list(sqlite|postgresql_external)",
|
||||
"DEBUG": "list(1|0)",
|
||||
@@ -107,9 +110,6 @@
|
||||
"POSTGRES_PORT": "str?",
|
||||
"POSTGRES_USER": "str?",
|
||||
"SECRET_KEY": "str",
|
||||
"AI_MODEL_NAME": "str?",
|
||||
"AI_API_KEY": "str?",
|
||||
"AI_RATELIMIT": "str?",
|
||||
"externalfiles_folder": "str?"
|
||||
},
|
||||
"services": [
|
||||
|
||||
@@ -77,7 +77,7 @@ cifsdomain: "workgroup"
|
||||
|
||||
### Setting up Distributed Transcoding
|
||||
|
||||
1. **Configure the Server**:
|
||||
1. **Configure the Server**:
|
||||
- Access the Web UI at `<your-ip>:8265`
|
||||
- Set up your media libraries and transcoding settings
|
||||
- Configure plugins and workflows as needed
|
||||
|
||||
0
zzz_archived_bitwarden/rootfs/etc/services.d/bitwarden/run
Normal file → Executable file
0
zzz_archived_bitwarden/rootfs/etc/services.d/bitwarden/run
Normal file → Executable file
0
zzz_archived_bitwarden/rootfs/etc/services.d/nginx/run
Normal file → Executable file
0
zzz_archived_bitwarden/rootfs/etc/services.d/nginx/run
Normal file → Executable file
Reference in New Issue
Block a user