- Switch from Docker to LXC with Debian bookworm rootfs and native Domoticz binary from GitHub releases (latest/download pattern) - Fix LXC cgroup2 terminal allocation: add lxc.tty.max, lxc.pty.max, cgroup2 device permissions for standard char devices, disable seccomp - Fix PID 1 issue: run domoticz as child process with signal forwarding - Use quoted heredoc with sed placeholders for start script generation - Update LuCI view: Docker → LXC references, add memory usage display - Remove Docker image UCI option, update catalog runtime to "lxc" - Fix streamlit LXC config: same cgroup2/terminal/seccomp fixes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
733 lines
19 KiB
Bash
733 lines
19 KiB
Bash
#!/bin/sh
|
|
# SecuBox Domoticz manager — LXC Alpine container with MQTT/Zigbee integration
|
|
|
|
CONFIG="domoticz"
|
|
LXC_NAME="domoticz"
|
|
LXC_PATH="/srv/lxc"
|
|
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
|
|
LXC_CONF="$LXC_PATH/$LXC_NAME/config"
|
|
DATA_PATH_DEFAULT="/srv/domoticz"
|
|
LOG_FILE="/srv/domoticz/domoticz.log"
|
|
OPKG_UPDATED=0
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: domoticzctl <command>
|
|
|
|
Commands:
|
|
install Create LXC container, download Domoticz, configure
|
|
uninstall Remove container (preserves data)
|
|
check Run prerequisite checks
|
|
update Download latest Domoticz release and restart
|
|
status Show container and service status
|
|
logs [N] Show last N lines of logs (default: 50)
|
|
shell Open interactive shell in container
|
|
configure-mqtt Auto-configure Mosquitto broker and MQTT bridge
|
|
configure-haproxy Register as HAProxy vhost for reverse proxy
|
|
backup [path] Backup Domoticz data
|
|
restore <path> Restore Domoticz from backup
|
|
mesh-register Register Domoticz in P2P mesh service catalog
|
|
service-run Internal: run container via procd
|
|
service-stop Stop container
|
|
USAGE
|
|
}
|
|
|
|
# ---------- helpers ----------
|
|
|
|
require_root() { [ "$(id -u)" -eq 0 ]; }
|
|
|
|
uci_get() {
|
|
local section="${2:-main}"
|
|
uci -q get ${CONFIG}.${section}.$1
|
|
}
|
|
|
|
log_info() { echo "[INFO] $*"; logger -t domoticzctl "$*"; }
|
|
log_error() { echo "[ERROR] $*" >&2; logger -t domoticzctl -p err "$*"; }
|
|
|
|
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
|
|
|
|
ensure_packages() {
|
|
for pkg in "$@"; do
|
|
if ! opkg status "$pkg" 2>/dev/null | grep -q "Status:.*installed"; then
|
|
if [ "$OPKG_UPDATED" -eq 0 ]; then
|
|
opkg update || return 1
|
|
OPKG_UPDATED=1
|
|
fi
|
|
opkg install "$pkg" || return 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
defaults() {
|
|
data_path="$(uci_get data_path || echo $DATA_PATH_DEFAULT)"
|
|
devices_path="$(uci_get devices_path || echo /srv/devices)"
|
|
port="$(uci_get port || echo 8080)"
|
|
timezone="$(uci_get timezone || echo UTC)"
|
|
}
|
|
|
|
detect_arch() {
|
|
case "$(uname -m)" in
|
|
aarch64) echo "aarch64" ;;
|
|
armv7l) echo "armv7" ;;
|
|
x86_64) echo "x86_64" ;;
|
|
*) echo "x86_64" ;;
|
|
esac
|
|
}
|
|
|
|
# ---------- LXC helpers ----------
|
|
|
|
lxc_running() {
|
|
lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"
|
|
}
|
|
|
|
lxc_exists() {
|
|
[ -f "$LXC_CONF" ] && [ -d "$LXC_ROOTFS" ]
|
|
}
|
|
|
|
lxc_exec() {
|
|
lxc-attach -n "$LXC_NAME" -- "$@"
|
|
}
|
|
|
|
lxc_stop() {
|
|
if lxc_running; then
|
|
lxc-stop -n "$LXC_NAME" -k 2>/dev/null || true
|
|
sleep 1
|
|
fi
|
|
}
|
|
|
|
# ---------- rootfs creation ----------
|
|
|
|
lxc_create_rootfs() {
|
|
local arch=$(detect_arch)
|
|
|
|
# Map to Debian architecture names
|
|
local debian_arch
|
|
case "$arch" in
|
|
aarch64) debian_arch="arm64" ;;
|
|
armv7) debian_arch="armhf" ;;
|
|
x86_64) debian_arch="amd64" ;;
|
|
*) debian_arch="amd64" ;;
|
|
esac
|
|
|
|
# Use debootstrap if available, otherwise download pre-built rootfs
|
|
ensure_dir "$LXC_ROOTFS"
|
|
|
|
# Minimal Debian rootfs via tarball from LXC image server
|
|
local rootfs_url="https://images.linuxcontainers.org/images/debian/bookworm/${debian_arch}/default/"
|
|
log_info "Downloading Debian bookworm rootfs for ${debian_arch}..."
|
|
|
|
# Get latest build directory
|
|
local latest_path
|
|
latest_path=$(wget -q -O - "$rootfs_url" 2>/dev/null | grep -oE '[0-9]{8}_[0-9]{2}:[0-9]{2}' | tail -1)
|
|
if [ -z "$latest_path" ]; then
|
|
log_error "Failed to find latest Debian rootfs build"
|
|
return 1
|
|
fi
|
|
|
|
local tarball="/tmp/debian-domoticz.tar.xz"
|
|
local tarball_url="${rootfs_url}${latest_path}/rootfs.tar.xz"
|
|
wget -q -O "$tarball" "$tarball_url" || {
|
|
log_error "Failed to download Debian rootfs from $tarball_url"
|
|
return 1
|
|
}
|
|
|
|
tar -xJf "$tarball" -C "$LXC_ROOTFS" || {
|
|
log_error "Failed to extract Debian rootfs"
|
|
return 1
|
|
}
|
|
rm -f "$tarball"
|
|
|
|
# DNS
|
|
cp /etc/resolv.conf "$LXC_ROOTFS/etc/resolv.conf" 2>/dev/null || \
|
|
echo "nameserver 127.0.0.1" > "$LXC_ROOTFS/etc/resolv.conf"
|
|
|
|
# Create /dev/null for apt operations
|
|
[ -c "$LXC_ROOTFS/dev/null" ] || mknod -m 666 "$LXC_ROOTFS/dev/null" c 1 3 2>/dev/null
|
|
|
|
# Install runtime dependencies via apt
|
|
log_info "Installing Domoticz runtime dependencies..."
|
|
chroot "$LXC_ROOTFS" /bin/sh -c "
|
|
apt-get -o Acquire::AllowInsecureRepositories=true update -qq 2>/dev/null && \
|
|
apt-get --allow-unauthenticated install -y -qq --no-install-recommends \
|
|
libcurl3-gnutls libusb-0.1-4 libusb-1.0-0 \
|
|
python3-minimal libsqlite3-0 tzdata ca-certificates \
|
|
2>&1 | tail -5
|
|
" || {
|
|
log_error "Failed to install runtime dependencies"
|
|
return 1
|
|
}
|
|
|
|
# Rebuild linker cache
|
|
chroot "$LXC_ROOTFS" ldconfig 2>/dev/null
|
|
|
|
# Clean apt cache
|
|
chroot "$LXC_ROOTFS" /bin/sh -c "apt-get clean; rm -rf /var/lib/apt/lists/*" 2>/dev/null
|
|
|
|
log_info "Debian rootfs created."
|
|
}
|
|
|
|
domoticz_download() {
|
|
local arch=$(detect_arch)
|
|
local domoticz_arch="$arch"
|
|
|
|
# Domoticz release URLs use different arch names
|
|
case "$arch" in
|
|
aarch64) domoticz_arch="aarch64" ;;
|
|
armv7) domoticz_arch="armv7l" ;;
|
|
x86_64) domoticz_arch="x86_64" ;;
|
|
esac
|
|
|
|
local release_url="https://github.com/domoticz/domoticz/releases/latest/download/domoticz_linux_${domoticz_arch}.tgz"
|
|
local tarball="/tmp/domoticz-release.tgz"
|
|
|
|
log_info "Downloading Domoticz release for ${domoticz_arch}..."
|
|
wget -q -O "$tarball" "$release_url" || {
|
|
log_error "Failed to download Domoticz release"
|
|
return 1
|
|
}
|
|
|
|
ensure_dir "$LXC_ROOTFS/opt/domoticz"
|
|
tar -xzf "$tarball" -C "$LXC_ROOTFS/opt/domoticz" || {
|
|
log_error "Failed to extract Domoticz"
|
|
return 1
|
|
}
|
|
rm -f "$tarball"
|
|
|
|
chmod +x "$LXC_ROOTFS/opt/domoticz/domoticz" 2>/dev/null
|
|
log_info "Domoticz binary installed."
|
|
}
|
|
|
|
# ---------- LXC config generation ----------
|
|
|
|
lxc_create_config() {
|
|
defaults
|
|
local arch=$(detect_arch)
|
|
local mem_limit=$(uci_get memory_limit || echo "512M")
|
|
|
|
# Convert memory limit to bytes
|
|
local mem_bytes
|
|
case "$mem_limit" in
|
|
*M) mem_bytes=$(( ${mem_limit%M} * 1048576 )) ;;
|
|
*G) mem_bytes=$(( ${mem_limit%G} * 1073741824 )) ;;
|
|
*) mem_bytes=536870912 ;;
|
|
esac
|
|
|
|
ensure_dir "$data_path/config"
|
|
ensure_dir "$data_path/db"
|
|
ensure_dir "$data_path/scripts"
|
|
ensure_dir "$data_path/backups"
|
|
|
|
cat > "$LXC_CONF" << EOF
|
|
# Domoticz LXC Container
|
|
lxc.uts.name = domoticz
|
|
lxc.rootfs.path = dir:${LXC_ROOTFS}
|
|
lxc.arch = ${arch}
|
|
|
|
# Host network (no veth isolation)
|
|
lxc.net.0.type = none
|
|
|
|
# Auto-mounts
|
|
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
|
|
|
|
# Data persistence
|
|
lxc.mount.entry = ${data_path}/config opt/domoticz/config none bind,create=dir 0 0
|
|
lxc.mount.entry = ${data_path}/db opt/domoticz/db none bind,create=dir 0 0
|
|
lxc.mount.entry = ${data_path}/scripts opt/domoticz/scripts none bind,create=dir 0 0
|
|
lxc.mount.entry = ${data_path}/backups opt/domoticz/backups none bind,create=dir 0 0
|
|
EOF
|
|
|
|
# USB device passthrough
|
|
if [ -d "$devices_path" ]; then
|
|
ensure_dir "$LXC_ROOTFS/devices"
|
|
cat >> "$LXC_CONF" << EOF
|
|
|
|
# Device passthrough
|
|
lxc.mount.entry = ${devices_path} devices none bind,create=dir 0 0
|
|
EOF
|
|
fi
|
|
|
|
# USB serial device passthrough (for Z-Wave/Zigbee dongles)
|
|
for dev in /dev/ttyUSB* /dev/ttyACM*; do
|
|
if [ -c "$dev" ]; then
|
|
local devname=$(basename "$dev")
|
|
local major=$(stat -c '%t' "$dev" 2>/dev/null)
|
|
major=$((0x${major:-bc}))
|
|
cat >> "$LXC_CONF" << EOF
|
|
lxc.cgroup2.devices.allow = c ${major}:* rwm
|
|
lxc.mount.entry = ${dev} dev/${devname} none bind,create=file 0 0
|
|
EOF
|
|
fi
|
|
done
|
|
|
|
cat >> "$LXC_CONF" << EOF
|
|
|
|
# Terminal
|
|
lxc.tty.max = 0
|
|
lxc.pty.max = 256
|
|
|
|
# Standard character devices
|
|
lxc.cgroup2.devices.allow = c 1:* rwm
|
|
lxc.cgroup2.devices.allow = c 5:* rwm
|
|
lxc.cgroup2.devices.allow = c 136:* rwm
|
|
|
|
# Security
|
|
lxc.cap.drop = sys_module mac_admin mac_override sys_time
|
|
|
|
# Disable default seccomp
|
|
lxc.seccomp.profile =
|
|
|
|
# Resource limits
|
|
lxc.cgroup2.memory.max = ${mem_bytes}
|
|
|
|
# Init
|
|
lxc.init.cmd = /opt/start-domoticz.sh
|
|
EOF
|
|
|
|
log_info "LXC config generated."
|
|
}
|
|
|
|
# ---------- startup script ----------
|
|
|
|
generate_start_script() {
|
|
defaults
|
|
local tz="$timezone"
|
|
|
|
# Write start script - use quoted heredoc to prevent variable expansion,
|
|
# then sed in the runtime values
|
|
cat > "$LXC_ROOTFS/opt/start-domoticz.sh" << 'STARTEOF'
|
|
#!/bin/sh
|
|
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
|
cd /opt/domoticz
|
|
|
|
# Set timezone
|
|
TZ_FILE="/usr/share/zoneinfo/__TZ__"
|
|
if [ -f "$TZ_FILE" ]; then
|
|
cp "$TZ_FILE" /etc/localtime
|
|
echo "__TZ__" > /etc/timezone
|
|
fi
|
|
export TZ="__TZ__"
|
|
|
|
# Ensure database directory exists
|
|
mkdir -p /opt/domoticz/db
|
|
|
|
# Trap signals and forward to domoticz (PID 1 can't exec directly)
|
|
trap 'kill $CHILD 2>/dev/null; wait $CHILD' TERM INT
|
|
|
|
# Run Domoticz as child process (not PID 1)
|
|
./domoticz -www __PORT__ -dbase /opt/domoticz/db/domoticz.db \
|
|
-userdata /opt/domoticz/config/ \
|
|
-log /opt/domoticz/domoticz.log \
|
|
-sslwww 0 &
|
|
CHILD=$!
|
|
wait $CHILD
|
|
STARTEOF
|
|
|
|
# Replace placeholders with actual values
|
|
sed -i "s|__TZ__|${tz}|g" "$LXC_ROOTFS/opt/start-domoticz.sh"
|
|
sed -i "s|__PORT__|${port}|g" "$LXC_ROOTFS/opt/start-domoticz.sh"
|
|
|
|
chmod +x "$LXC_ROOTFS/opt/start-domoticz.sh"
|
|
}
|
|
|
|
# ---------- commands ----------
|
|
|
|
cmd_install() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
if lxc_exists; then
|
|
log_info "Container already exists. Use 'update' to refresh Domoticz."
|
|
return 0
|
|
fi
|
|
|
|
# Ensure LXC is available
|
|
ensure_packages lxc lxc-common || {
|
|
log_error "Failed to install LXC packages"
|
|
exit 1
|
|
}
|
|
|
|
defaults
|
|
ensure_dir "$LXC_PATH/$LXC_NAME"
|
|
ensure_dir "$data_path"
|
|
|
|
lxc_create_rootfs || exit 1
|
|
domoticz_download || exit 1
|
|
lxc_create_config
|
|
generate_start_script
|
|
|
|
uci set ${CONFIG}.main.enabled='1'
|
|
uci commit ${CONFIG}
|
|
/etc/init.d/domoticz enable
|
|
|
|
log_info "Domoticz installed in LXC container. Start with /etc/init.d/domoticz start"
|
|
}
|
|
|
|
cmd_uninstall() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
/etc/init.d/domoticz stop >/dev/null 2>&1
|
|
lxc_stop
|
|
|
|
defaults
|
|
|
|
# Remove container rootfs but preserve data
|
|
rm -rf "$LXC_PATH/$LXC_NAME"
|
|
|
|
uci set ${CONFIG}.main.enabled='0'
|
|
uci commit ${CONFIG}
|
|
/etc/init.d/domoticz disable >/dev/null 2>&1
|
|
|
|
log_info "Domoticz container removed. Data preserved at ${data_path}."
|
|
}
|
|
|
|
cmd_check() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
echo "=== Prerequisites ==="
|
|
|
|
# LXC
|
|
if command -v lxc-start >/dev/null 2>&1; then
|
|
echo "[OK] lxc-start available"
|
|
else
|
|
echo "[MISSING] lxc-start — install lxc"
|
|
fi
|
|
|
|
# cgroup
|
|
if [ -d /sys/fs/cgroup ]; then
|
|
echo "[OK] /sys/fs/cgroup present"
|
|
else
|
|
echo "[MISSING] /sys/fs/cgroup"
|
|
fi
|
|
|
|
# Container
|
|
if lxc_exists; then
|
|
echo "[OK] Container rootfs exists"
|
|
else
|
|
echo "[MISSING] Container not installed — run 'domoticzctl install'"
|
|
fi
|
|
|
|
# Container state
|
|
if lxc_running; then
|
|
echo "[OK] Container is RUNNING"
|
|
else
|
|
echo "[STOPPED] Container is not running"
|
|
fi
|
|
|
|
echo ""
|
|
echo "=== USB Devices ==="
|
|
for dev in /dev/ttyUSB* /dev/ttyACM*; do
|
|
[ -e "$dev" ] && echo " $dev"
|
|
done
|
|
[ ! -e /dev/ttyUSB0 ] && [ ! -e /dev/ttyACM0 ] && echo " (none detected)"
|
|
|
|
echo ""
|
|
echo "=== Mosquitto ==="
|
|
if pgrep mosquitto >/dev/null 2>&1; then
|
|
echo "[OK] Mosquitto running"
|
|
else
|
|
echo "[STOPPED] Mosquitto not running"
|
|
fi
|
|
|
|
echo ""
|
|
echo "=== Zigbee2MQTT ==="
|
|
if [ -f /srv/zigbee2mqtt/alpine/rootfs/run.pid ] || pgrep -f zigbee2mqtt >/dev/null 2>&1; then
|
|
echo "[OK] Zigbee2MQTT running"
|
|
else
|
|
echo "[STOPPED] Zigbee2MQTT not running"
|
|
fi
|
|
}
|
|
|
|
cmd_update() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
if ! lxc_exists; then
|
|
log_error "Container not installed. Run 'domoticzctl install' first."
|
|
exit 1
|
|
fi
|
|
|
|
lxc_stop
|
|
domoticz_download || exit 1
|
|
generate_start_script
|
|
/etc/init.d/domoticz restart
|
|
|
|
log_info "Domoticz updated and restarted."
|
|
}
|
|
|
|
cmd_status() {
|
|
defaults
|
|
echo "=== Domoticz Status ==="
|
|
|
|
if lxc_running; then
|
|
echo "Container: RUNNING"
|
|
lxc-info -n "$LXC_NAME" 2>/dev/null | grep -E "PID|Memory|CPU"
|
|
elif lxc_exists; then
|
|
echo "Container: STOPPED (installed)"
|
|
else
|
|
echo "Container: NOT INSTALLED"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Port: ${port}"
|
|
echo "Data: ${data_path}"
|
|
|
|
if [ -d "$data_path/db" ]; then
|
|
local db_size=$(du -sh "$data_path/db" 2>/dev/null | cut -f1)
|
|
echo "DB size: ${db_size:-0}"
|
|
fi
|
|
}
|
|
|
|
cmd_logs() {
|
|
defaults
|
|
local lines="${1:-50}"
|
|
local logfile="$data_path/domoticz.log"
|
|
|
|
if [ -f "$logfile" ]; then
|
|
tail -n "$lines" "$logfile"
|
|
elif lxc_running; then
|
|
lxc_exec tail -n "$lines" /opt/domoticz/domoticz.log 2>/dev/null || echo "No logs available."
|
|
else
|
|
echo "No logs available."
|
|
fi
|
|
}
|
|
|
|
cmd_shell() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
if ! lxc_running; then
|
|
log_error "Container is not running. Start it first."
|
|
exit 1
|
|
fi
|
|
|
|
lxc_exec /bin/sh
|
|
}
|
|
|
|
cmd_configure_mqtt() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
local broker=$(uci_get broker mqtt)
|
|
local broker_port=$(uci_get broker_port mqtt)
|
|
local topic_prefix=$(uci_get topic_prefix mqtt)
|
|
local z2m_topic=$(uci_get z2m_topic mqtt)
|
|
|
|
broker="${broker:-127.0.0.1}"
|
|
broker_port="${broker_port:-1883}"
|
|
topic_prefix="${topic_prefix:-domoticz}"
|
|
z2m_topic="${z2m_topic:-zigbee2mqtt}"
|
|
|
|
# Ensure Mosquitto is installed and running
|
|
if ! command -v mosquitto >/dev/null 2>&1; then
|
|
log_info "Installing mosquitto-nossl..."
|
|
ensure_packages mosquitto-nossl mosquitto-client-nossl || {
|
|
log_error "Failed to install Mosquitto"
|
|
return 1
|
|
}
|
|
fi
|
|
|
|
# Configure Mosquitto listener
|
|
local mosquitto_conf="/etc/mosquitto/mosquitto.conf"
|
|
if [ -f "$mosquitto_conf" ]; then
|
|
if ! grep -q "^listener ${broker_port}" "$mosquitto_conf" 2>/dev/null; then
|
|
echo "" >> "$mosquitto_conf"
|
|
echo "# Auto-configured by domoticzctl" >> "$mosquitto_conf"
|
|
echo "listener ${broker_port}" >> "$mosquitto_conf"
|
|
echo "allow_anonymous true" >> "$mosquitto_conf"
|
|
fi
|
|
fi
|
|
|
|
# Start Mosquitto
|
|
/etc/init.d/mosquitto enable >/dev/null 2>&1
|
|
/etc/init.d/mosquitto start >/dev/null 2>&1
|
|
|
|
# Verify broker is listening
|
|
local retries=0
|
|
while [ $retries -lt 5 ]; do
|
|
if netstat -tln 2>/dev/null | grep -q ":${broker_port} "; then
|
|
break
|
|
fi
|
|
retries=$((retries + 1))
|
|
sleep 1
|
|
done
|
|
|
|
if ! netstat -tln 2>/dev/null | grep -q ":${broker_port} "; then
|
|
log_info "Mosquitto may not be listening on port ${broker_port}"
|
|
fi
|
|
|
|
# Check zigbee2mqtt MQTT settings
|
|
if [ -f /etc/config/zigbee2mqtt ]; then
|
|
local z2m_mqtt_uri=$(uci -q get zigbee2mqtt.main.mqtt_host)
|
|
local z2m_base=$(uci -q get zigbee2mqtt.main.base_topic)
|
|
|
|
# z2m stores full URI (mqtt://host:port)
|
|
local z2m_host=$(echo "$z2m_mqtt_uri" | sed 's|^mqtt[s]*://||' | cut -d: -f1)
|
|
local z2m_port_conf=$(echo "$z2m_mqtt_uri" | sed 's|^mqtt[s]*://||' | cut -d: -f2)
|
|
z2m_host="${z2m_host:-127.0.0.1}"
|
|
z2m_port_conf="${z2m_port_conf:-1883}"
|
|
|
|
if [ "$z2m_host" = "$broker" ] && [ "$z2m_port_conf" = "$broker_port" ]; then
|
|
echo "Zigbee2MQTT shares same broker (${broker}:${broker_port})."
|
|
echo "z2m topic: ${z2m_base:-zigbee2mqtt}"
|
|
else
|
|
echo "[INFO] Zigbee2MQTT uses broker ${z2m_host}:${z2m_port_conf}"
|
|
echo "[INFO] Domoticz will connect to ${broker}:${broker_port}"
|
|
echo "[INFO] Ensure both use the same Mosquitto broker for device bridging."
|
|
fi
|
|
else
|
|
echo "[INFO] Zigbee2MQTT not installed — MQTT bridge will work with other MQTT publishers."
|
|
fi
|
|
|
|
# Update UCI MQTT settings
|
|
uci set ${CONFIG}.mqtt.enabled='1'
|
|
uci set ${CONFIG}.mqtt.broker="$broker"
|
|
uci set ${CONFIG}.mqtt.broker_port="$broker_port"
|
|
uci set ${CONFIG}.mqtt.topic_prefix="$topic_prefix"
|
|
uci set ${CONFIG}.mqtt.z2m_topic="$z2m_topic"
|
|
uci commit ${CONFIG}
|
|
|
|
echo ""
|
|
echo "MQTT bridge configured:"
|
|
echo " Broker: ${broker}:${broker_port}"
|
|
echo " Domoticz: topic_prefix=${topic_prefix}"
|
|
echo " Z2M: topic=${z2m_topic}"
|
|
echo ""
|
|
echo "Next step: In Domoticz UI (Setup > Hardware), add:"
|
|
echo " Type: MQTT Client Gateway with LAN interface"
|
|
echo " Remote Address: ${broker}"
|
|
echo " Port: ${broker_port}"
|
|
echo " Topic prefix: ${topic_prefix}"
|
|
}
|
|
|
|
cmd_configure_haproxy() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
local domain=$(uci_get domain network)
|
|
local port_val=$(uci_get port)
|
|
domain="${domain:-domoticz.secubox.local}"
|
|
port_val="${port_val:-8080}"
|
|
|
|
if command -v haproxyctl >/dev/null 2>&1; then
|
|
haproxyctl add-vhost "$domain" "127.0.0.1:${port_val}" 2>&1
|
|
local code=$?
|
|
if [ $code -eq 0 ]; then
|
|
uci set ${CONFIG}.network.haproxy='1'
|
|
uci commit ${CONFIG}
|
|
log_info "HAProxy vhost configured for ${domain} -> 127.0.0.1:${port_val}"
|
|
else
|
|
log_error "haproxyctl add-vhost failed"
|
|
return 1
|
|
fi
|
|
else
|
|
log_error "haproxyctl not found — install secubox-app-haproxy first"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
cmd_backup() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
defaults
|
|
local backup_path="${1:-/tmp/domoticz-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
|
|
|
|
if [ ! -d "$data_path" ]; then
|
|
log_error "No data to backup at ${data_path}"
|
|
return 1
|
|
fi
|
|
|
|
tar czf "$backup_path" -C "$data_path" . 2>&1
|
|
local code=$?
|
|
if [ $code -eq 0 ]; then
|
|
log_info "Backup created: ${backup_path}"
|
|
else
|
|
log_error "Backup failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
cmd_restore() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
defaults
|
|
local backup_path="$1"
|
|
|
|
if [ -z "$backup_path" ] || [ ! -f "$backup_path" ]; then
|
|
log_error "Backup file not found: ${backup_path}"
|
|
return 1
|
|
fi
|
|
|
|
/etc/init.d/domoticz stop >/dev/null 2>&1
|
|
lxc_stop
|
|
|
|
ensure_dir "$data_path"
|
|
tar xzf "$backup_path" -C "$data_path" 2>&1
|
|
local code=$?
|
|
if [ $code -eq 0 ]; then
|
|
log_info "Restored from ${backup_path}"
|
|
echo "Restart Domoticz with: /etc/init.d/domoticz start"
|
|
else
|
|
log_error "Restore failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
cmd_mesh_register() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
defaults
|
|
|
|
if command -v secubox-p2p >/dev/null 2>&1; then
|
|
secubox-p2p register-service domoticz "$port" 2>&1
|
|
uci set ${CONFIG}.mesh.enabled='1'
|
|
uci commit ${CONFIG}
|
|
log_info "Domoticz registered in P2P mesh on port ${port}"
|
|
else
|
|
log_error "secubox-p2p not found — install secubox-p2p first"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
cmd_service_run() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
|
|
if ! lxc_exists; then
|
|
log_error "Container not installed. Run 'domoticzctl install' first."
|
|
exit 1
|
|
fi
|
|
|
|
defaults
|
|
|
|
# Regenerate config and startup script on each run (picks up UCI changes)
|
|
lxc_create_config
|
|
generate_start_script
|
|
|
|
# Stop any previous instance
|
|
lxc_stop
|
|
|
|
# Update DNS
|
|
cp /etc/resolv.conf "$LXC_ROOTFS/etc/resolv.conf" 2>/dev/null
|
|
|
|
log_info "Starting Domoticz LXC container..."
|
|
exec lxc-start -n "$LXC_NAME" -F
|
|
}
|
|
|
|
cmd_service_stop() {
|
|
require_root || { log_error "Root required"; exit 1; }
|
|
lxc_stop
|
|
log_info "Domoticz container stopped."
|
|
}
|
|
|
|
# ---------- main ----------
|
|
|
|
case "${1:-}" in
|
|
install) shift; cmd_install "$@" ;;
|
|
uninstall) shift; cmd_uninstall "$@" ;;
|
|
check) shift; cmd_check "$@" ;;
|
|
update) shift; cmd_update "$@" ;;
|
|
status) shift; cmd_status "$@" ;;
|
|
logs) shift; cmd_logs "$@" ;;
|
|
shell) shift; cmd_shell "$@" ;;
|
|
configure-mqtt) shift; cmd_configure_mqtt "$@" ;;
|
|
configure-haproxy) shift; cmd_configure_haproxy "$@" ;;
|
|
backup) shift; cmd_backup "$@" ;;
|
|
restore) shift; cmd_restore "$@" ;;
|
|
mesh-register) shift; cmd_mesh_register "$@" ;;
|
|
service-run) shift; cmd_service_run "$@" ;;
|
|
service-stop) shift; cmd_service_stop "$@" ;;
|
|
help|--help|-h|'') usage ;;
|
|
*) log_error "Unknown command: $1"; usage >&2; exit 1 ;;
|
|
esac
|