feat(domoticz): Rewrite from Docker to LXC Debian container
- 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>
This commit is contained in:
parent
89896568b1
commit
f2f24afe12
@ -136,4 +136,5 @@ _Last updated: 2026-02-04_
|
||||
- `zigbee2mqtt`: Direct `/dev/ttyUSB0` passthrough (socat TCP bridge fails ASH protocol), adapter `ezsp`→`ember` (z2m 2.x), `ZIGBEE2MQTT_DATA` env var, `mosquitto-nossl` dependency.
|
||||
- `smbfs`: New SMB/CIFS remote mount manager package — UCI config, `smbfsctl` CLI (add/remove/mount/umount/test/status), auto-mount init script, credentials storage, Jellyfin+Lyrion integration, catalog entry.
|
||||
- `jellyfin`: KISS READMEs for both backend and LuCI packages.
|
||||
- `domoticz`: New `luci-app-domoticz` LuCI dashboard with IoT integration status (Mosquitto, Zigbee2MQTT, MQTT bridge), service lifecycle, HAProxy, mesh P2P, logs. `domoticzctl` enhanced with `configure-mqtt` (auto Mosquitto+Z2M bridge), `configure-haproxy`, `backup/restore`, `mesh-register`, `uninstall`. UCI extended with mqtt/network/mesh sections. Catalog updated.
|
||||
- `domoticz`: Rewrite from Docker to LXC Debian container with native binary from GitHub releases. LuCI dashboard with IoT integration status (Mosquitto, Zigbee2MQTT, MQTT bridge), service lifecycle, HAProxy, mesh P2P, logs. `domoticzctl` with `configure-mqtt` (auto Mosquitto+Z2M bridge), `configure-haproxy`, `backup/restore`, `mesh-register`, `uninstall`. UCI extended with mqtt/network/mesh sections. Catalog updated.
|
||||
- LXC cgroup2 fix: Added `lxc.tty.max`, `lxc.pty.max`, `lxc.cgroup2.devices.allow` for standard character devices, and `lxc.seccomp.profile` disable to fix terminal allocation failures on cgroup v2 systems. Applied to `streamlit` and `domoticz`.
|
||||
|
||||
@ -270,7 +270,8 @@
|
||||
"Bash(/home/reepost/CyberMindStudio/secubox-openwrt/secubox-tools/local-build.sh:*)",
|
||||
"Bash(do rsync -av --delete /home/reepost/CyberMindStudio/secubox-openwrt/package/secubox/$pkg/ /home/reepost/CyberMindStudio/secubox-openwrt/secubox-tools/local-feed/$pkg/)",
|
||||
"Bash(do rsync -av --delete \"package/secubox/$pkg/\" \"secubox-tools/local-feed/$pkg/\")",
|
||||
"Bash(xdg-open:*)"
|
||||
"Bash(xdg-open:*)",
|
||||
"WebFetch(domain:releases.domoticz.com)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ Requires `secubox-app-domoticz` (installed as dependency).
|
||||
|
||||
## Features
|
||||
|
||||
- **Service Status**: Container status, Docker availability, disk usage, USB devices
|
||||
- **Service Status**: Container status, LXC availability, memory/disk usage, USB devices
|
||||
- **IoT Integration**: Mosquitto broker status, Zigbee2MQTT status, MQTT bridge configuration
|
||||
- **MQTT Auto-Setup**: One-click Mosquitto installation and broker configuration
|
||||
- **Network**: HAProxy reverse proxy integration, WAN access control, domain configuration
|
||||
@ -28,9 +28,9 @@ Requires `secubox-app-domoticz` (installed as dependency).
|
||||
| `start` | — | Start Domoticz service |
|
||||
| `stop` | — | Stop Domoticz service |
|
||||
| `restart` | — | Restart Domoticz service |
|
||||
| `install` | — | Pull Docker image and configure |
|
||||
| `install` | — | Create LXC container and download Domoticz |
|
||||
| `uninstall` | — | Remove container (preserves data) |
|
||||
| `update` | — | Pull latest image and restart |
|
||||
| `update` | — | Download latest Domoticz and restart |
|
||||
| `configure_mqtt` | — | Auto-configure Mosquitto and MQTT bridge |
|
||||
| `configure_haproxy` | — | Register HAProxy vhost |
|
||||
| `backup` | — | Create data backup |
|
||||
|
||||
@ -119,10 +119,10 @@ return view.extend({
|
||||
return html;
|
||||
};
|
||||
|
||||
o = s.option(form.DummyValue, '_docker', _('Docker'));
|
||||
o = s.option(form.DummyValue, '_lxc', _('LXC'));
|
||||
o.rawhtml = true;
|
||||
o.cfgvalue = function() {
|
||||
return status.docker_available
|
||||
return status.lxc_available
|
||||
? '<span style="color:#27ae60;">Available</span>'
|
||||
: '<span style="color:#e74c3c;">Not available</span>';
|
||||
};
|
||||
@ -132,10 +132,11 @@ return view.extend({
|
||||
o.cfgvalue = function() {
|
||||
var port = status.port || 8080;
|
||||
var html = '<table style="border-collapse:collapse;">';
|
||||
html += '<tr><td style="padding:2px 12px 2px 0;color:#8892b0;">Image:</td><td>' + (status.image || '-') + '</td></tr>';
|
||||
html += '<tr><td style="padding:2px 12px 2px 0;color:#8892b0;">Port:</td><td>' + port + '</td></tr>';
|
||||
html += '<tr><td style="padding:2px 12px 2px 0;color:#8892b0;">Data:</td><td>' + (status.data_path || '-') + '</td></tr>';
|
||||
html += '<tr><td style="padding:2px 12px 2px 0;color:#8892b0;">Domain:</td><td>' + (status.domain || '-') + '</td></tr>';
|
||||
if (status.memory_usage)
|
||||
html += '<tr><td style="padding:2px 12px 2px 0;color:#8892b0;">Memory:</td><td>' + status.memory_usage + '</td></tr>';
|
||||
if (status.disk_usage)
|
||||
html += '<tr><td style="padding:2px 12px 2px 0;color:#8892b0;">Disk:</td><td>' + status.disk_usage + '</td></tr>';
|
||||
if (status.usb_devices && status.usb_devices.length > 0)
|
||||
@ -210,7 +211,7 @@ return view.extend({
|
||||
o.inputstyle = 'apply';
|
||||
o.onclick = function() {
|
||||
ui.showModal(_('Installing...'), [
|
||||
E('p', { 'class': 'spinning' }, _('Pulling Docker image and configuring...'))
|
||||
E('p', { 'class': 'spinning' }, _('Creating LXC container and downloading Domoticz...'))
|
||||
]);
|
||||
return callInstall().then(function(res) {
|
||||
ui.hideModal();
|
||||
@ -263,11 +264,11 @@ return view.extend({
|
||||
}
|
||||
|
||||
o = s.option(form.Button, '_update', _('Update'));
|
||||
o.inputtitle = _('Pull Latest Image');
|
||||
o.inputtitle = _('Update Domoticz');
|
||||
o.inputstyle = 'action';
|
||||
o.onclick = function() {
|
||||
ui.showModal(_('Updating...'), [
|
||||
E('p', { 'class': 'spinning' }, _('Pulling latest Docker image and restarting...'))
|
||||
E('p', { 'class': 'spinning' }, _('Downloading latest Domoticz and restarting container...'))
|
||||
]);
|
||||
return callUpdate().then(function(res) {
|
||||
ui.hideModal();
|
||||
@ -327,10 +328,6 @@ return view.extend({
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '8080';
|
||||
|
||||
o = s.option(form.Value, 'image', _('Docker Image'),
|
||||
_('Docker image to use.'));
|
||||
o.placeholder = 'domoticz/domoticz:latest';
|
||||
|
||||
o = s.option(form.Value, 'data_path', _('Data Path'),
|
||||
_('Path for Domoticz config and database.'));
|
||||
o.placeholder = '/srv/domoticz';
|
||||
|
||||
@ -3,9 +3,20 @@
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
CONTAINER="secbx-domoticz"
|
||||
LXC_NAME="domoticz"
|
||||
LXC_PATH="/srv/lxc"
|
||||
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
|
||||
LXC_CONF="$LXC_PATH/$LXC_NAME/config"
|
||||
CONFIG="domoticz"
|
||||
|
||||
lxc_running() {
|
||||
lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"
|
||||
}
|
||||
|
||||
lxc_exists() {
|
||||
[ -f "$LXC_CONF" ] && [ -d "$LXC_ROOTFS" ]
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
list)
|
||||
echo '{"status":{},"start":{},"stop":{},"restart":{},"install":{},"uninstall":{},"update":{},"configure_mqtt":{},"configure_haproxy":{},"backup":{},"restore":{"path":"str"},"logs":{"lines":"int"}}'
|
||||
@ -16,7 +27,6 @@ case "$1" in
|
||||
json_init
|
||||
|
||||
enabled=$(uci -q get ${CONFIG}.main.enabled)
|
||||
image=$(uci -q get ${CONFIG}.main.image)
|
||||
port=$(uci -q get ${CONFIG}.main.port)
|
||||
data_path=$(uci -q get ${CONFIG}.main.data_path)
|
||||
devices_path=$(uci -q get ${CONFIG}.main.devices_path)
|
||||
@ -38,7 +48,6 @@ case "$1" in
|
||||
mesh_enabled=$(uci -q get ${CONFIG}.mesh.enabled)
|
||||
|
||||
json_add_boolean "enabled" ${enabled:-0}
|
||||
json_add_string "image" "${image:-domoticz/domoticz:latest}"
|
||||
json_add_int "port" ${port:-8080}
|
||||
json_add_string "data_path" "${data_path:-/srv/domoticz}"
|
||||
json_add_string "devices_path" "${devices_path:-/srv/devices}"
|
||||
@ -55,24 +64,36 @@ case "$1" in
|
||||
json_add_boolean "firewall_wan" ${firewall_wan:-0}
|
||||
json_add_boolean "mesh_enabled" ${mesh_enabled:-0}
|
||||
|
||||
# Docker availability
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
json_add_boolean "docker_available" 1
|
||||
# LXC availability
|
||||
if command -v lxc-start >/dev/null 2>&1; then
|
||||
json_add_boolean "lxc_available" 1
|
||||
else
|
||||
json_add_boolean "docker_available" 0
|
||||
json_add_boolean "lxc_available" 0
|
||||
fi
|
||||
|
||||
# Container status
|
||||
if docker ps --filter "name=$CONTAINER" --format '{{.Status}}' 2>/dev/null | grep -q .; then
|
||||
if lxc_running; then
|
||||
json_add_string "container_status" "running"
|
||||
uptime=$(docker ps --filter "name=$CONTAINER" --format '{{.Status}}' 2>/dev/null)
|
||||
uptime=$(lxc-info -n "$LXC_NAME" -s 2>/dev/null | head -1)
|
||||
json_add_string "container_uptime" "$uptime"
|
||||
elif docker ps -a --filter "name=$CONTAINER" --format '{{.Status}}' 2>/dev/null | grep -q .; then
|
||||
# Memory from cgroup
|
||||
mem_usage=""
|
||||
if [ -f "/sys/fs/cgroup/lxc.payload.$LXC_NAME/memory.current" ]; then
|
||||
mem_bytes=$(cat "/sys/fs/cgroup/lxc.payload.$LXC_NAME/memory.current" 2>/dev/null)
|
||||
if [ -n "$mem_bytes" ] && [ "$mem_bytes" -gt 0 ] 2>/dev/null; then
|
||||
mem_mb=$(( mem_bytes / 1048576 ))
|
||||
mem_usage="${mem_mb} MB"
|
||||
fi
|
||||
fi
|
||||
json_add_string "memory_usage" "$mem_usage"
|
||||
elif lxc_exists; then
|
||||
json_add_string "container_status" "stopped"
|
||||
json_add_string "container_uptime" ""
|
||||
json_add_string "memory_usage" ""
|
||||
else
|
||||
json_add_string "container_status" "not_installed"
|
||||
json_add_string "container_uptime" ""
|
||||
json_add_string "memory_usage" ""
|
||||
fi
|
||||
|
||||
# Mosquitto broker status
|
||||
@ -225,7 +246,7 @@ case "$1" in
|
||||
lines=$(echo "$input" | jsonfilter -e '@.lines' 2>/dev/null)
|
||||
[ -z "$lines" ] && lines=50
|
||||
|
||||
logs=$(docker logs --tail "$lines" "$CONTAINER" 2>&1 | tail -100)
|
||||
logs=$(/usr/sbin/domoticzctl logs "$lines" 2>&1)
|
||||
json_init
|
||||
json_add_string "logs" "$logs"
|
||||
json_dump
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-app-domoticz
|
||||
PKG_RELEASE:=3
|
||||
PKG_RELEASE:=4
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_ARCH:=all
|
||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||
@ -14,13 +14,13 @@ define Package/secubox-app-domoticz
|
||||
CATEGORY:=Utilities
|
||||
PKGARCH:=all
|
||||
SUBMENU:=SecuBox Apps
|
||||
TITLE:=SecuBox Domoticz docker app
|
||||
DEPENDS:=dockerd +docker +containerd
|
||||
TITLE:=SecuBox Domoticz LXC app
|
||||
DEPENDS:=+lxc +lxc-common
|
||||
endef
|
||||
|
||||
define Package/secubox-app-domoticz/description
|
||||
Installer, configuration, and service manager for running Domoticz
|
||||
inside Docker on SecuBox-powered OpenWrt systems.
|
||||
inside an LXC Alpine container on SecuBox-powered OpenWrt systems.
|
||||
endef
|
||||
|
||||
define Package/secubox-app-domoticz/conffiles
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# SecuBox Domoticz
|
||||
|
||||
Home automation platform running in Docker with MQTT bridge, Zigbee2MQTT integration, and P2P mesh support.
|
||||
Home automation platform running in an LXC Alpine container with MQTT bridge, Zigbee2MQTT integration, and P2P mesh support.
|
||||
|
||||
## Installation
|
||||
|
||||
@ -17,7 +17,6 @@ UCI config file: `/etc/config/domoticz`
|
||||
```
|
||||
config domoticz 'main'
|
||||
option enabled '0'
|
||||
option image 'domoticz/domoticz:latest'
|
||||
option data_path '/srv/domoticz'
|
||||
option devices_path '/srv/devices'
|
||||
option port '8080'
|
||||
@ -42,9 +41,9 @@ config domoticz 'mesh'
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
domoticzctl install # Pull image, install prerequisites
|
||||
domoticzctl install # Create LXC container, download Domoticz
|
||||
domoticzctl uninstall # Remove container (data preserved)
|
||||
domoticzctl update # Pull latest image, restart
|
||||
domoticzctl update # Download latest Domoticz, restart
|
||||
domoticzctl status # Show container status
|
||||
domoticzctl logs [-f] # Container logs
|
||||
domoticzctl configure-mqtt # Auto-setup Mosquitto + MQTT bridge
|
||||
@ -79,7 +78,7 @@ When `secubox-app-zigbee2mqtt` is installed:
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `dockerd`, `docker`, `containerd`
|
||||
- `lxc`, `lxc-common`
|
||||
- Optional: `mosquitto-nossl`, `secubox-app-zigbee2mqtt`
|
||||
|
||||
## License
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
config domoticz 'main'
|
||||
option enabled '0'
|
||||
option image 'domoticz/domoticz:latest'
|
||||
option data_path '/srv/domoticz'
|
||||
option devices_path '/srv/devices'
|
||||
option port '8080'
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
#!/bin/sh
|
||||
# SecuBox Domoticz manager — IoT home automation with MQTT/Zigbee integration
|
||||
# SecuBox Domoticz manager — LXC Alpine container with MQTT/Zigbee integration
|
||||
|
||||
CONFIG="domoticz"
|
||||
CONTAINER="secbx-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() {
|
||||
@ -10,13 +15,14 @@ usage() {
|
||||
Usage: domoticzctl <command>
|
||||
|
||||
Commands:
|
||||
install Install prerequisites, prepare directories, pull image
|
||||
install Create LXC container, download Domoticz, configure
|
||||
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
|
||||
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
|
||||
@ -26,6 +32,8 @@ Commands:
|
||||
USAGE
|
||||
}
|
||||
|
||||
# ---------- helpers ----------
|
||||
|
||||
require_root() { [ "$(id -u)" -eq 0 ]; }
|
||||
|
||||
uci_get() {
|
||||
@ -33,13 +41,8 @@ uci_get() {
|
||||
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)"
|
||||
}
|
||||
log_info() { echo "[INFO] $*"; logger -t domoticzctl "$*"; }
|
||||
log_error() { echo "[ERROR] $*" >&2; logger -t domoticzctl -p err "$*"; }
|
||||
|
||||
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
|
||||
|
||||
@ -55,60 +58,450 @@ ensure_packages() {
|
||||
done
|
||||
}
|
||||
|
||||
check_prereqs() {
|
||||
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
|
||||
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
|
||||
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."
|
||||
}
|
||||
|
||||
pull_image() { defaults; docker pull "$image"; }
|
||||
# ---------- startup script ----------
|
||||
|
||||
stop_container() {
|
||||
docker stop "$CONTAINER" >/dev/null 2>&1 || true
|
||||
docker rm "$CONTAINER" >/dev/null 2>&1 || true
|
||||
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 || { echo "Root required" >&2; exit 1; }
|
||||
check_prereqs || exit 1
|
||||
ensure_dir "$data_path/config"
|
||||
pull_image || exit 1
|
||||
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
|
||||
echo "Domoticz installed. Start with /etc/init.d/domoticz start"
|
||||
|
||||
log_info "Domoticz installed in LXC container. Start with /etc/init.d/domoticz start"
|
||||
}
|
||||
|
||||
cmd_uninstall() {
|
||||
require_root || { echo "Root required" >&2; exit 1; }
|
||||
require_root || { log_error "Root required"; exit 1; }
|
||||
|
||||
/etc/init.d/domoticz stop >/dev/null 2>&1
|
||||
stop_container
|
||||
lxc_stop
|
||||
|
||||
defaults
|
||||
docker rmi "$image" >/dev/null 2>&1 || true
|
||||
|
||||
# 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
|
||||
echo "Domoticz uninstalled. Data preserved at ${data_path}."
|
||||
|
||||
log_info "Domoticz container removed. Data preserved at ${data_path}."
|
||||
}
|
||||
|
||||
cmd_check() { check_prereqs; echo "Prerequisite check completed."; }
|
||||
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 || { echo "Root required" >&2; exit 1; }
|
||||
pull_image || exit 1
|
||||
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
|
||||
echo "Domoticz updated and restarted."
|
||||
|
||||
log_info "Domoticz updated and restarted."
|
||||
}
|
||||
|
||||
cmd_status() { docker ps -a --filter "name=$CONTAINER"; }
|
||||
cmd_status() {
|
||||
defaults
|
||||
echo "=== Domoticz Status ==="
|
||||
|
||||
cmd_logs() { docker logs "$@" "$CONTAINER"; }
|
||||
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 || { echo "Root required" >&2; exit 1; }
|
||||
require_root || { log_error "Root required"; exit 1; }
|
||||
|
||||
local broker=$(uci_get broker mqtt)
|
||||
local broker_port=$(uci_get broker_port mqtt)
|
||||
@ -122,9 +515,9 @@ cmd_configure_mqtt() {
|
||||
|
||||
# Ensure Mosquitto is installed and running
|
||||
if ! command -v mosquitto >/dev/null 2>&1; then
|
||||
echo "Installing mosquitto-nossl..."
|
||||
log_info "Installing mosquitto-nossl..."
|
||||
ensure_packages mosquitto-nossl mosquitto-client-nossl || {
|
||||
echo "[ERROR] Failed to install Mosquitto" >&2
|
||||
log_error "Failed to install Mosquitto"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
@ -132,7 +525,6 @@ cmd_configure_mqtt() {
|
||||
# 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"
|
||||
@ -156,7 +548,7 @@ cmd_configure_mqtt() {
|
||||
done
|
||||
|
||||
if ! netstat -tln 2>/dev/null | grep -q ":${broker_port} "; then
|
||||
echo "[WARN] Mosquitto may not be listening on port ${broker_port}" >&2
|
||||
log_info "Mosquitto may not be listening on port ${broker_port}"
|
||||
fi
|
||||
|
||||
# Check zigbee2mqtt MQTT settings
|
||||
@ -164,7 +556,7 @@ cmd_configure_mqtt() {
|
||||
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
|
||||
# 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}"
|
||||
@ -190,9 +582,6 @@ cmd_configure_mqtt() {
|
||||
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}"
|
||||
@ -207,129 +596,137 @@ cmd_configure_mqtt() {
|
||||
}
|
||||
|
||||
cmd_configure_haproxy() {
|
||||
require_root || { echo "Root required" >&2; exit 1; }
|
||||
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}"
|
||||
|
||||
# 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}"
|
||||
log_info "HAProxy vhost configured for ${domain} -> 127.0.0.1:${port_val}"
|
||||
else
|
||||
echo "[ERROR] haproxyctl add-vhost failed" >&2
|
||||
log_error "haproxyctl add-vhost failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "[ERROR] haproxyctl not found — install secubox-app-haproxy first" >&2
|
||||
log_error "haproxyctl not found — install secubox-app-haproxy first"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_backup() {
|
||||
require_root || { echo "Root required" >&2; exit 1; }
|
||||
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/config" ]; then
|
||||
echo "[ERROR] No data to backup at ${data_path}/config" >&2
|
||||
if [ ! -d "$data_path" ]; then
|
||||
log_error "No data to backup at ${data_path}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
tar czf "$backup_path" -C "$data_path" config 2>&1
|
||||
tar czf "$backup_path" -C "$data_path" . 2>&1
|
||||
local code=$?
|
||||
if [ $code -eq 0 ]; then
|
||||
echo "Backup created: ${backup_path}"
|
||||
log_info "Backup created: ${backup_path}"
|
||||
else
|
||||
echo "[ERROR] Backup failed" >&2
|
||||
log_error "Backup failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_restore() {
|
||||
require_root || { echo "Root required" >&2; exit 1; }
|
||||
require_root || { log_error "Root required"; exit 1; }
|
||||
defaults
|
||||
local backup_path="$1"
|
||||
|
||||
if [ -z "$backup_path" ] || [ ! -f "$backup_path" ]; then
|
||||
echo "[ERROR] Backup file not found: ${backup_path}" >&2
|
||||
log_error "Backup file not found: ${backup_path}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Stop service before restore
|
||||
/etc/init.d/domoticz stop >/dev/null 2>&1
|
||||
stop_container
|
||||
lxc_stop
|
||||
|
||||
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}"
|
||||
log_info "Restored from ${backup_path}"
|
||||
echo "Restart Domoticz with: /etc/init.d/domoticz start"
|
||||
else
|
||||
echo "[ERROR] Restore failed" >&2
|
||||
log_error "Restore failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_mesh_register() {
|
||||
require_root || { echo "Root required" >&2; exit 1; }
|
||||
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}
|
||||
echo "Domoticz registered in P2P mesh on port ${port}"
|
||||
log_info "Domoticz registered in P2P mesh on port ${port}"
|
||||
else
|
||||
echo "[ERROR] secubox-p2p not found — install secubox-p2p first" >&2
|
||||
log_error "secubox-p2p not found — install secubox-p2p first"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_service_run() {
|
||||
require_root || { echo "Root required" >&2; exit 1; }
|
||||
check_prereqs || exit 1
|
||||
defaults
|
||||
stop_container
|
||||
require_root || { log_error "Root required"; exit 1; }
|
||||
|
||||
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)"
|
||||
if ! lxc_exists; then
|
||||
log_error "Container not installed. Run 'domoticzctl install' first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec docker run --rm $docker_args -e TZ="$timezone" "$image"
|
||||
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 || { echo "Root required" >&2; exit 1; }
|
||||
stop_container
|
||||
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 "$@" ;;
|
||||
configure-mqtt) shift; cmd_configure_mqtt "$@" ;;
|
||||
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 "$@" ;;
|
||||
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 ;;
|
||||
*) log_error "Unknown command: $1"; usage >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
@ -438,11 +438,20 @@ lxc.mount.entry = $data_path/logs srv/logs none bind,create=dir 0 0
|
||||
lxc.environment = STREAMLIT_THEME_BASE=$theme_base
|
||||
lxc.environment = STREAMLIT_THEME_PRIMARY=$theme_primary
|
||||
|
||||
# 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_admin sys_module mac_admin mac_override sys_time sys_rawio
|
||||
|
||||
# Resource limits
|
||||
lxc.cgroup.memory.limit_in_bytes = $mem_bytes
|
||||
lxc.cgroup2.memory.max = $mem_bytes
|
||||
|
||||
# Init command
|
||||
lxc.init.cmd = /opt/start-streamlit.sh
|
||||
|
||||
@ -1522,7 +1522,7 @@
|
||||
"name": "Domoticz",
|
||||
"version": "1.0.0",
|
||||
"category": "iot",
|
||||
"runtime": "docker",
|
||||
"runtime": "lxc",
|
||||
"description": "Home automation system with MQTT bridge, Zigbee2MQTT integration, and IoT device management",
|
||||
"author": "CyberMind.fr",
|
||||
"license": "GPL-3.0",
|
||||
@ -1532,7 +1532,7 @@
|
||||
"home-automation",
|
||||
"iot",
|
||||
"smart-home",
|
||||
"docker",
|
||||
"lxc",
|
||||
"mqtt",
|
||||
"zigbee",
|
||||
"mesh"
|
||||
@ -1541,8 +1541,8 @@
|
||||
"required": [
|
||||
"secubox-app-domoticz",
|
||||
"luci-app-domoticz",
|
||||
"docker",
|
||||
"dockerd"
|
||||
"lxc",
|
||||
"lxc-common"
|
||||
],
|
||||
"optional": [
|
||||
"mosquitto-nossl",
|
||||
@ -1562,18 +1562,20 @@
|
||||
"min_storage_mb": 100
|
||||
},
|
||||
"status": "stable",
|
||||
"pkg_version": "1.0.0-3",
|
||||
"pkg_version": "1.0.0-4",
|
||||
"app_version": "1.0.0",
|
||||
"changelog": {
|
||||
"1.0.0-3": {
|
||||
"1.0.0-4": {
|
||||
"date": "2026-02-04",
|
||||
"changes": [
|
||||
"Rewrite from Docker to LXC Alpine container",
|
||||
"Native Domoticz binary download from releases.domoticz.com",
|
||||
"LXC config with USB cgroup2 passthrough and memory limits",
|
||||
"LuCI dashboard with IoT integration status",
|
||||
"MQTT auto-bridge for Mosquitto and Zigbee2MQTT",
|
||||
"HAProxy reverse proxy integration",
|
||||
"P2P mesh service registration",
|
||||
"Backup and restore support",
|
||||
"USB device passthrough visibility"
|
||||
"Backup and restore support"
|
||||
]
|
||||
},
|
||||
"1.0.0": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user