mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-06-03 22:34:11 +02:00
Compare commits
23 Commits
91b20ae5e0
...
72d0b0d6ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72d0b0d6ce | ||
|
|
763623cb5f | ||
|
|
8e1a05362a | ||
|
|
85fedccb13 | ||
|
|
92b14ba062 | ||
|
|
0586ededb4 | ||
|
|
9809407dd7 | ||
|
|
27241c1885 | ||
|
|
39d646005a | ||
|
|
9396500c6e | ||
|
|
de4d4da604 | ||
|
|
ebe1cd2b48 | ||
|
|
dc44fa1eff | ||
|
|
b76416d0d9 | ||
|
|
c8799206f1 | ||
|
|
1816450e76 | ||
|
|
643e571a97 | ||
|
|
4e5499c6d8 | ||
|
|
4036a1b8bd | ||
|
|
2bbe34e236 | ||
|
|
9f85d6553e | ||
|
|
b4c2bee078 | ||
|
|
611acb32f3 |
@@ -42,27 +42,6 @@ fi
|
||||
# General elements
|
||||
##################
|
||||
|
||||
# Use tphakala model v2 if enabled
|
||||
if [[ -f "$HOME/BirdNET-Pi/model/BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite2" ]]; then
|
||||
mv "$HOME/BirdNET-Pi/model/BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite2" "$HOME/BirdNET-Pi/model/BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite"
|
||||
fi
|
||||
if [[ -d "$HOME/BirdNET-Pi/model/labels_nm2" ]]; then
|
||||
mv "$HOME/BirdNET-Pi/model/labels_nm2" "$HOME/BirdNET-Pi/model/labels_nm"
|
||||
fi
|
||||
if false && bashio::config.true 'Use_tphakala_model_v2'; then
|
||||
echo "... applying tphakala model v2"
|
||||
if [[ -f "$HOME/BirdNET-Pi/model/BirdNET-Go_classifier.tflite" ]] && [[ -d "$HOME/BirdNET-Pi/model/labels_go" ]]; then
|
||||
# Move labels
|
||||
mv "$HOME/BirdNET-Pi/model/labels_nm" "$HOME/BirdNET-Pi/model/labels_nm2" || bashio::log.warning "Failed to move labels_nm"
|
||||
mv "$HOME/BirdNET-Pi/model/labels_go" "$HOME/BirdNET-Pi/model/labels_nm" || bashio::log.warning "Failed to move labels_go"
|
||||
# Move model
|
||||
mv "$HOME/BirdNET-Pi/model/BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite" "$HOME/BirdNET-Pi/model/BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite2" || bashio::log.warning "Failed to move base model"
|
||||
mv "$HOME/BirdNET-Pi/model/BirdNET-Go_classifier.tflite" "$HOME/BirdNET-Pi/model/BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite" || bashio::log.warning "Failed to move Go classifier"
|
||||
else
|
||||
bashio::log.fatal "model or labels not found, skipping"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Avoid updates
|
||||
echo "... modifying the config to silence update indicators"
|
||||
# Remove if two lines
|
||||
@@ -74,16 +53,36 @@ sed -i 's/"\.\$updatediv\.\"//g' "$HOME"/BirdNET-Pi/homepage/views.php
|
||||
|
||||
# Correct language labels according to birdnet.conf
|
||||
echo "... adapting labels according to birdnet.conf"
|
||||
if export "$(grep "^DATABASE_LANG" /config/birdnet.conf)"; then
|
||||
export "$(grep "^MODEL" /config/birdnet.conf)"
|
||||
if grep -q '^DATABASE_LANG=' /config/birdnet.conf; then
|
||||
export "$(grep -m1 '^DATABASE_LANG=' /config/birdnet.conf)"
|
||||
bashio::log.info "Setting language to ${DATABASE_LANG:-en}"
|
||||
if [ "$MODEL" == "BirdNET_GLOBAL_6K_V2.4_Model_FP16" ]; then
|
||||
BASEDIR=labels_nm
|
||||
|
||||
if [[ -f "$HOME/BirdNET-Pi/scripts/utils/maintainer.py" ]]; then
|
||||
PYTHONPATH="$HOME/BirdNET-Pi:$HOME/BirdNET-Pi/scripts:$HOME/BirdNET-Pi/scripts/utils:${PYTHONPATH:-}" \
|
||||
python3 - <<'PY' 1>/dev/null
|
||||
import importlib.util
|
||||
import os
|
||||
import sys
|
||||
|
||||
home = os.environ.get("HOME", "/home/pi")
|
||||
birdnet_root = os.path.join(home, "BirdNET-Pi")
|
||||
for path in (birdnet_root,
|
||||
os.path.join(birdnet_root, "scripts"),
|
||||
os.path.join(birdnet_root, "scripts", "utils")):
|
||||
if path not in sys.path:
|
||||
sys.path.insert(0, path)
|
||||
|
||||
if importlib.util.find_spec("scripts.utils.maintainer") is None:
|
||||
raise RuntimeError("Language maintainer module not found; skipping.")
|
||||
|
||||
from scripts.utils.maintainer import create_language
|
||||
|
||||
database_lang = os.environ.get("DATABASE_LANG", "en")
|
||||
create_language(database_lang)
|
||||
PY
|
||||
else
|
||||
BASEDIR=labels_l18n
|
||||
bashio::log.warning "Language maintainer script not found; skipping translation generation."
|
||||
fi
|
||||
label_file_name="labels_${DATABASE_LANG}.txt"
|
||||
ln -sf "$HOME/BirdNET-Pi/model/${BASEDIR}/${label_file_name}" "$HOME/BirdNET-Pi/model/labels.txt" || bashio::log.warning "Failed to update language labels"
|
||||
else
|
||||
bashio::log.warning "DATABASE_LANG not found in configuration. Using default labels."
|
||||
fi
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
## 5.1.2-9 (18-11-2025)
|
||||
- Minor bugs fixed
|
||||
## 5.1.2-12 (19-11-2025)
|
||||
- Improve ip monitoring with wireguard
|
||||
|
||||
## 5.1.2-8 (20-11-2025)
|
||||
- MAJOR FEAT: add WireGuard support with runtime validation and troubleshooting logs.
|
||||
- Added support for configuring extra environment variables via the `env_vars` add-on option alongside config.yaml. See https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2 for details.
|
||||
|
||||
@@ -125,6 +125,8 @@ RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.
|
||||
# 4 Entrypoint #
|
||||
################
|
||||
|
||||
RUN chmod +x /usr/local/bin/* /usr/local/sbin/*
|
||||
|
||||
# Add entrypoint
|
||||
ENV S6_STAGE2_HOOK=/ha_entrypoint.sh
|
||||
ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_entrypoint.sh" "/ha_entrypoint.sh"
|
||||
|
||||
@@ -145,4 +145,4 @@ schema:
|
||||
slug: qbittorrent
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: 5.1.2-9
|
||||
version: 5.1.2-12
|
||||
|
||||
@@ -23,13 +23,6 @@ if bashio::config.true 'openvpn_alt_mode'; then
|
||||
bashio::log.warning 'The openvpn_alt_mode option is ignored when WireGuard is enabled.'
|
||||
fi
|
||||
|
||||
port="$(bashio::addon.port '51820/udp' || true)"
|
||||
if bashio::var.has_value "${port}"; then
|
||||
bashio::log.info "WireGuard host port ${port}/udp mapped to container port 51820."
|
||||
else
|
||||
bashio::log.info 'WireGuard port 51820/udp is not exposed in the add-on options. Continuing with outbound-only connectivity.'
|
||||
fi
|
||||
|
||||
if bashio::config.has_value 'wireguard_config'; then
|
||||
configured_name="$(bashio::config 'wireguard_config')"
|
||||
configured_name="${configured_name##*/}"
|
||||
@@ -83,4 +76,7 @@ else
|
||||
bashio::log.warning "qBittorrent config file not found. Bind the client manually to interface ${interface_name}."
|
||||
fi
|
||||
|
||||
# Get current ip
|
||||
curl -s ipecho.net/plain > /currentip
|
||||
|
||||
bashio::log.info "WireGuard prepared with interface ${interface_name} using configuration ${wireguard_config##*/}."
|
||||
|
||||
@@ -1,44 +1,200 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
# ==============================================================================
|
||||
|
||||
# Wait for transmission to become available
|
||||
bashio::net.wait_for 8080 localhost 900
|
||||
set -euo pipefail
|
||||
|
||||
bashio::log.info "Starting NGinx..."
|
||||
export PATH="/usr/local/sbin:/usr/local/bin:${PATH}"
|
||||
|
||||
# Check vpn is working
|
||||
if [ -f /currentip ]; then
|
||||
nginx || nginx -s reload &
|
||||
while true; do
|
||||
# Get vpn ip
|
||||
if bashio::config.true 'wireguard_enabled'; then
|
||||
wireguard_interface="$(cat /var/run/wireguard/interface 2>/dev/null || echo 'wg0')"
|
||||
curl -s ipecho.net/plain --interface "${wireguard_interface}" > /vpnip
|
||||
elif bashio::config.true 'openvpn_alt_mode'; then
|
||||
curl -s ipecho.net/plain > /vpnip
|
||||
# File where the real (non-VPN) public IP is stored
|
||||
REAL_IP_FILE="/currentip"
|
||||
|
||||
# How often to re-check VPN IP (seconds)
|
||||
VPN_CHECK_INTERVAL="${VPN_CHECK_INTERVAL:-300}"
|
||||
|
||||
# Service used to get IP + country (JSON)
|
||||
VPN_INFO_URL="${VPN_INFO_URL:-https://ipinfo.io/json}"
|
||||
|
||||
# -------------------------------
|
||||
# Helpers
|
||||
# -------------------------------
|
||||
|
||||
read_real_ip() {
|
||||
# Reads the "real" IP saved before VPN start
|
||||
if [[ -f "${REAL_IP_FILE}" ]]; then
|
||||
# Strip whitespace/newlines just in case
|
||||
local ip
|
||||
ip="$(tr -d '[:space:]' < "${REAL_IP_FILE}")"
|
||||
if [[ -n "${ip}" ]]; then
|
||||
echo "${ip}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# No baseline is not fatal: just return empty
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
get_ip_info() {
|
||||
# Outputs: "<ip> <country>" on success
|
||||
local json ip country
|
||||
|
||||
if ! json="$(curl -fsS --max-time 10 "${VPN_INFO_URL}" 2>/dev/null)"; then
|
||||
bashio::log.warning "Unable to reach VPN info service at ${VPN_INFO_URL}."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract "ip" and "country" without jq
|
||||
ip="$(
|
||||
printf '%s\n' "${json}" \
|
||||
| sed -n 's/.*"ip"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
||||
| head -n1
|
||||
)"
|
||||
country="$(
|
||||
printf '%s\n' "${json}" \
|
||||
| sed -n 's/.*"country"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
||||
| head -n1
|
||||
)"
|
||||
|
||||
if [[ -z "${ip}" ]]; then
|
||||
bashio::log.warning "Failed to parse IP from VPN info response."
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s %s\n' "${ip}" "${country:-Unknown}"
|
||||
}
|
||||
|
||||
wait_for_vpn_ip() {
|
||||
# Waits until:
|
||||
# - we can read an external IP, AND
|
||||
# - it is different from REAL_IP (if REAL_IP is known)
|
||||
# Outputs "<ip> <country>" when ready.
|
||||
local attempt out ip country
|
||||
|
||||
for attempt in {1..20}; do
|
||||
if out="$(get_ip_info)"; then
|
||||
read -r ip country <<< "${out}"
|
||||
|
||||
if [[ -n "${REAL_IP:-}" ]] && [[ "${ip}" == "${REAL_IP}" ]]; then
|
||||
bashio::log.warning \
|
||||
"External IP still equals real IP (${ip}); VPN not ready yet (attempt ${attempt}/20)."
|
||||
else
|
||||
printf '%s %s\n' "${ip}" "${country}"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
curl -s ipecho.net/plain --interface tun0 > /vpnip
|
||||
bashio::log.warning "Unable to query external IP (attempt ${attempt}/20)."
|
||||
fi
|
||||
|
||||
# Verify ip has changed
|
||||
if [[ "$(cat /vpnip)" = "$(cat /currentip)" ]]; then
|
||||
bashio::log.fatal "VPN is not properly configured. Your ip is exposed. Please fix this, or do not use the vpn alt mode"
|
||||
bashio::exit.nok
|
||||
fi
|
||||
|
||||
# Get ip location
|
||||
COUNTRY=$(curl -s https://ipinfo.io/$(cat /vpnip) | grep country -i -m 1 | cut -d ':' -f 2 | xargs | awk 'gsub(/,$/,x)' || true)
|
||||
|
||||
# Inform by message
|
||||
bashio::log.info "VPN is up and running with ip $(cat /vpnip), based in country : $COUNTRY"
|
||||
|
||||
# Check every 15m
|
||||
sleep 15m
|
||||
|
||||
true
|
||||
sleep 5
|
||||
done
|
||||
else
|
||||
nginx || nginx -s reload
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
start_nginx_background() {
|
||||
bashio::log.info "Starting nginx..."
|
||||
nginx &
|
||||
echo $!
|
||||
}
|
||||
|
||||
# -------------------------------
|
||||
# Main logic
|
||||
# -------------------------------
|
||||
|
||||
# Detect VPN mode(s)
|
||||
vpn_openvpn=false
|
||||
vpn_wireguard=false
|
||||
|
||||
if bashio::config.true 'openvpn_enabled'; then
|
||||
vpn_openvpn=true
|
||||
fi
|
||||
|
||||
if bashio::config.true 'wireguard_enabled'; then
|
||||
vpn_wireguard=true
|
||||
fi
|
||||
|
||||
# If both are enabled, that's a configuration error
|
||||
if [[ "${vpn_openvpn}" == true && "${vpn_wireguard}" == true ]]; then
|
||||
bashio::log.error "Both OpenVPN and WireGuard are enabled. Only one VPN mode is supported."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
vpn_enabled=false
|
||||
vpn_mode="none"
|
||||
|
||||
if [[ "${vpn_openvpn}" == true ]]; then
|
||||
vpn_enabled=true
|
||||
vpn_mode="OpenVPN"
|
||||
fi
|
||||
|
||||
if [[ "${vpn_wireguard}" == true ]]; then
|
||||
vpn_enabled=true
|
||||
vpn_mode="WireGuard"
|
||||
fi
|
||||
|
||||
if [[ "${vpn_enabled}" != true ]]; then
|
||||
# No VPN: just boot nginx, no IP leak monitoring
|
||||
bashio::log.info "No VPN enabled (OpenVPN/WireGuard). Starting nginx without IP monitoring."
|
||||
exec nginx
|
||||
fi
|
||||
|
||||
# Read the baseline "real" IP from /currentip
|
||||
REAL_IP="$(read_real_ip)"
|
||||
|
||||
if [[ -n "${REAL_IP}" ]]; then
|
||||
bashio::log.info "Real (non-VPN) IP from ${REAL_IP_FILE}: ${REAL_IP}"
|
||||
else
|
||||
bashio::log.warning "Real IP file ${REAL_IP_FILE} missing or empty; IP leak detection will be less strict."
|
||||
fi
|
||||
|
||||
bashio::log.info "VPN mode detected: ${vpn_mode}. Enabling IP leak protection and periodic monitoring."
|
||||
|
||||
# Wait until VPN is up and external IP != REAL_IP
|
||||
if ! VPN_INFO_OUT="$(wait_for_vpn_ip)"; then
|
||||
bashio::log.error "Unable to obtain a VPN external IP different from real IP. Exiting to avoid leaking traffic."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -r VPN_IP VPN_COUNTRY <<< "${VPN_INFO_OUT}"
|
||||
|
||||
bashio::log.info "VPN external IP: ${VPN_IP} (${VPN_COUNTRY})"
|
||||
|
||||
# Start nginx in background and monitor
|
||||
nginx_pid="$(start_nginx_background)"
|
||||
|
||||
# Forward termination signals
|
||||
trap '
|
||||
bashio::log.info "Signal received, stopping nginx..."
|
||||
kill "'"${nginx_pid}"'" 2>/dev/null || true
|
||||
wait "'"${nginx_pid}"'" 2>/dev/null || true
|
||||
exit 0
|
||||
' SIGTERM SIGINT SIGHUP
|
||||
|
||||
# Monitoring loop
|
||||
while true; do
|
||||
# If nginx died, stop this service and let s6 handle restart policy
|
||||
if ! kill -0 "${nginx_pid}" 2>/dev/null; then
|
||||
bashio::log.error "nginx process exited unexpectedly; leaving service."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep "${VPN_CHECK_INTERVAL}"
|
||||
|
||||
if ! current_out="$(get_ip_info)"; then
|
||||
bashio::log.warning "Failed to refresh external IP; keeping previous assumptions."
|
||||
continue
|
||||
fi
|
||||
|
||||
read -r current_ip current_country <<< "${current_out}"
|
||||
|
||||
bashio::log.info "Current external IP: ${current_ip} (${current_country})"
|
||||
|
||||
# If we know the real IP, treat equality as a leak
|
||||
if [[ -n "${REAL_IP}" ]] && [[ "${current_ip}" == "${REAL_IP}" ]]; then
|
||||
bashio::log.error "IP LEAK DETECTED: current external IP ${current_ip} matches real IP ${REAL_IP}."
|
||||
bashio::log.error "Stopping nginx and exiting so the supervisor can restart the add-on."
|
||||
kill "${nginx_pid}" 2>/dev/null || true
|
||||
wait "${nginx_pid}" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user