secubox-openwrt/package/secubox/secubox-app-domoticz/files/usr/sbin/domoticzctl
CyberMind-FR 89896568b1 feat(domoticz): Add LuCI dashboard with MQTT auto-bridge and Zigbee2MQTT integration
New luci-app-domoticz package with RPCD handler (12 methods), LuCI overview
(status, IoT integration, MQTT, HAProxy, mesh, logs), and full service lifecycle.
Enhanced domoticzctl with configure-mqtt (auto Mosquitto+Z2M bridge), configure-haproxy,
backup/restore, mesh-register, and uninstall commands. UCI extended with mqtt/network/mesh
sections. Catalog updated with LuCI package and IoT tags. MirrorNetworking strategic
document noted in planning files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 21:32:17 +01:00

336 lines
9.9 KiB
Bash

#!/bin/sh
# SecuBox Domoticz manager — IoT home automation with MQTT/Zigbee integration
CONFIG="domoticz"
CONTAINER="secbx-domoticz"
OPKG_UPDATED=0
usage() {
cat <<'USAGE'
Usage: domoticzctl <command>
Commands:
install Install prerequisites, prepare directories, pull image
uninstall Remove container (preserves data)
check Run prerequisite checks
update Pull new image and restart
status Show container status
logs [-f] Show container logs (use -f to follow)
configure-mqtt Auto-configure Mosquitto broker and Domoticz 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
}
require_root() { [ "$(id -u)" -eq 0 ]; }
uci_get() {
local section="${2:-main}"
uci -q get ${CONFIG}.${section}.$1
}
defaults() {
image="$(uci_get image || echo domoticz/domoticz:latest)"
data_path="$(uci_get data_path || echo /srv/domoticz)"
devices_path="$(uci_get devices_path || echo /srv/devices)"
port="$(uci_get port || echo 8080)"
timezone="$(uci_get timezone || echo UTC)"
}
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
}
check_prereqs() {
defaults
ensure_dir "$data_path"
[ -d /sys/fs/cgroup ] || { echo "[ERROR] /sys/fs/cgroup missing" >&2; return 1; }
ensure_packages dockerd docker containerd
/etc/init.d/dockerd enable >/dev/null 2>&1
/etc/init.d/dockerd start >/dev/null 2>&1
}
pull_image() { defaults; docker pull "$image"; }
stop_container() {
docker stop "$CONTAINER" >/dev/null 2>&1 || true
docker rm "$CONTAINER" >/dev/null 2>&1 || true
}
cmd_install() {
require_root || { echo "Root required" >&2; exit 1; }
check_prereqs || exit 1
ensure_dir "$data_path/config"
pull_image || exit 1
uci set ${CONFIG}.main.enabled='1'
uci commit ${CONFIG}
/etc/init.d/domoticz enable
echo "Domoticz installed. Start with /etc/init.d/domoticz start"
}
cmd_uninstall() {
require_root || { echo "Root required" >&2; exit 1; }
/etc/init.d/domoticz stop >/dev/null 2>&1
stop_container
defaults
docker rmi "$image" >/dev/null 2>&1 || true
uci set ${CONFIG}.main.enabled='0'
uci commit ${CONFIG}
/etc/init.d/domoticz disable >/dev/null 2>&1
echo "Domoticz uninstalled. Data preserved at ${data_path}."
}
cmd_check() { check_prereqs; echo "Prerequisite check completed."; }
cmd_update() {
require_root || { echo "Root required" >&2; exit 1; }
pull_image || exit 1
/etc/init.d/domoticz restart
echo "Domoticz updated and restarted."
}
cmd_status() { docker ps -a --filter "name=$CONTAINER"; }
cmd_logs() { docker logs "$@" "$CONTAINER"; }
cmd_configure_mqtt() {
require_root || { echo "Root required" >&2; 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
echo "Installing mosquitto-nossl..."
ensure_packages mosquitto-nossl mosquitto-client-nossl || {
echo "[ERROR] Failed to install Mosquitto" >&2
return 1
}
fi
# Configure Mosquitto listener
local mosquitto_conf="/etc/mosquitto/mosquitto.conf"
if [ -f "$mosquitto_conf" ]; then
# Ensure listener on configured port
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
echo "[WARN] Mosquitto may not be listening on port ${broker_port}" >&2
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) — extract host and 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}
# Note: Domoticz MQTT configuration happens inside the Domoticz web UI
# (Hardware > MQTT Client Gateway). We configure the broker and topic,
# but the user must add the MQTT hardware in Domoticz UI.
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 || { echo "Root required" >&2; exit 1; }
local domain=$(uci_get domain network)
local port_val=$(uci_get port)
domain="${domain:-domoticz.secubox.local}"
port_val="${port_val:-8080}"
# Use haproxyctl if available
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}
echo "HAProxy vhost configured for ${domain} -> 127.0.0.1:${port_val}"
else
echo "[ERROR] haproxyctl add-vhost failed" >&2
return 1
fi
else
echo "[ERROR] haproxyctl not found — install secubox-app-haproxy first" >&2
return 1
fi
}
cmd_backup() {
require_root || { echo "Root required" >&2; exit 1; }
defaults
local backup_path="${1:-/tmp/domoticz-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
if [ ! -d "$data_path/config" ]; then
echo "[ERROR] No data to backup at ${data_path}/config" >&2
return 1
fi
tar czf "$backup_path" -C "$data_path" config 2>&1
local code=$?
if [ $code -eq 0 ]; then
echo "Backup created: ${backup_path}"
else
echo "[ERROR] Backup failed" >&2
return 1
fi
}
cmd_restore() {
require_root || { echo "Root required" >&2; exit 1; }
defaults
local backup_path="$1"
if [ -z "$backup_path" ] || [ ! -f "$backup_path" ]; then
echo "[ERROR] Backup file not found: ${backup_path}" >&2
return 1
fi
# Stop service before restore
/etc/init.d/domoticz stop >/dev/null 2>&1
stop_container
ensure_dir "$data_path"
tar xzf "$backup_path" -C "$data_path" 2>&1
local code=$?
if [ $code -eq 0 ]; then
echo "Restored from ${backup_path}"
echo "Restart Domoticz with: /etc/init.d/domoticz start"
else
echo "[ERROR] Restore failed" >&2
return 1
fi
}
cmd_mesh_register() {
require_root || { echo "Root required" >&2; 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}
echo "Domoticz registered in P2P mesh on port ${port}"
else
echo "[ERROR] secubox-p2p not found — install secubox-p2p first" >&2
return 1
fi
}
cmd_service_run() {
require_root || { echo "Root required" >&2; exit 1; }
check_prereqs || exit 1
defaults
stop_container
local docker_args="--name $CONTAINER -p ${port}:8080 -v $data_path/config:/config"
[ -d "$devices_path" ] && docker_args="$docker_args -v $devices_path:/devices"
# If MQTT enabled, pass broker info to container network
local mqtt_en=$(uci_get enabled mqtt)
if [ "${mqtt_en:-0}" = "1" ]; then
docker_args="$docker_args --add-host=mqtt-broker:$(uci_get broker mqtt || echo 127.0.0.1)"
fi
exec docker run --rm $docker_args -e TZ="$timezone" "$image"
}
cmd_service_stop() {
require_root || { echo "Root required" >&2; exit 1; }
stop_container
}
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 "$@" ;;
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 ;;
*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;;
esac