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:
CyberMind-FR 2026-02-04 22:05:39 +01:00
parent 89896568b1
commit f2f24afe12
11 changed files with 566 additions and 140 deletions

View File

@ -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. - `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. - `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. - `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`.

View File

@ -270,7 +270,8 @@
"Bash(/home/reepost/CyberMindStudio/secubox-openwrt/secubox-tools/local-build.sh:*)", "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 /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(do rsync -av --delete \"package/secubox/$pkg/\" \"secubox-tools/local-feed/$pkg/\")",
"Bash(xdg-open:*)" "Bash(xdg-open:*)",
"WebFetch(domain:releases.domoticz.com)"
] ]
} }
} }

View File

@ -12,7 +12,7 @@ Requires `secubox-app-domoticz` (installed as dependency).
## Features ## 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 - **IoT Integration**: Mosquitto broker status, Zigbee2MQTT status, MQTT bridge configuration
- **MQTT Auto-Setup**: One-click Mosquitto installation and broker configuration - **MQTT Auto-Setup**: One-click Mosquitto installation and broker configuration
- **Network**: HAProxy reverse proxy integration, WAN access control, domain 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 | | `start` | — | Start Domoticz service |
| `stop` | — | Stop Domoticz service | | `stop` | — | Stop Domoticz service |
| `restart` | — | Restart Domoticz service | | `restart` | — | Restart Domoticz service |
| `install` | — | Pull Docker image and configure | | `install` | — | Create LXC container and download Domoticz |
| `uninstall` | — | Remove container (preserves data) | | `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_mqtt` | — | Auto-configure Mosquitto and MQTT bridge |
| `configure_haproxy` | — | Register HAProxy vhost | | `configure_haproxy` | — | Register HAProxy vhost |
| `backup` | — | Create data backup | | `backup` | — | Create data backup |

View File

@ -119,10 +119,10 @@ return view.extend({
return html; return html;
}; };
o = s.option(form.DummyValue, '_docker', _('Docker')); o = s.option(form.DummyValue, '_lxc', _('LXC'));
o.rawhtml = true; o.rawhtml = true;
o.cfgvalue = function() { o.cfgvalue = function() {
return status.docker_available return status.lxc_available
? '<span style="color:#27ae60;">Available</span>' ? '<span style="color:#27ae60;">Available</span>'
: '<span style="color:#e74c3c;">Not available</span>'; : '<span style="color:#e74c3c;">Not available</span>';
}; };
@ -132,10 +132,11 @@ return view.extend({
o.cfgvalue = function() { o.cfgvalue = function() {
var port = status.port || 8080; var port = status.port || 8080;
var html = '<table style="border-collapse:collapse;">'; 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;">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;">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>'; 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) if (status.disk_usage)
html += '<tr><td style="padding:2px 12px 2px 0;color:#8892b0;">Disk:</td><td>' + status.disk_usage + '</td></tr>'; 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) if (status.usb_devices && status.usb_devices.length > 0)
@ -210,7 +211,7 @@ return view.extend({
o.inputstyle = 'apply'; o.inputstyle = 'apply';
o.onclick = function() { o.onclick = function() {
ui.showModal(_('Installing...'), [ 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) { return callInstall().then(function(res) {
ui.hideModal(); ui.hideModal();
@ -263,11 +264,11 @@ return view.extend({
} }
o = s.option(form.Button, '_update', _('Update')); o = s.option(form.Button, '_update', _('Update'));
o.inputtitle = _('Pull Latest Image'); o.inputtitle = _('Update Domoticz');
o.inputstyle = 'action'; o.inputstyle = 'action';
o.onclick = function() { o.onclick = function() {
ui.showModal(_('Updating...'), [ 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) { return callUpdate().then(function(res) {
ui.hideModal(); ui.hideModal();
@ -327,10 +328,6 @@ return view.extend({
o.datatype = 'port'; o.datatype = 'port';
o.placeholder = '8080'; 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'), o = s.option(form.Value, 'data_path', _('Data Path'),
_('Path for Domoticz config and database.')); _('Path for Domoticz config and database.'));
o.placeholder = '/srv/domoticz'; o.placeholder = '/srv/domoticz';

View File

@ -3,9 +3,20 @@
. /lib/functions.sh . /lib/functions.sh
. /usr/share/libubox/jshn.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" 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 case "$1" in
list) list)
echo '{"status":{},"start":{},"stop":{},"restart":{},"install":{},"uninstall":{},"update":{},"configure_mqtt":{},"configure_haproxy":{},"backup":{},"restore":{"path":"str"},"logs":{"lines":"int"}}' 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 json_init
enabled=$(uci -q get ${CONFIG}.main.enabled) enabled=$(uci -q get ${CONFIG}.main.enabled)
image=$(uci -q get ${CONFIG}.main.image)
port=$(uci -q get ${CONFIG}.main.port) port=$(uci -q get ${CONFIG}.main.port)
data_path=$(uci -q get ${CONFIG}.main.data_path) data_path=$(uci -q get ${CONFIG}.main.data_path)
devices_path=$(uci -q get ${CONFIG}.main.devices_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) mesh_enabled=$(uci -q get ${CONFIG}.mesh.enabled)
json_add_boolean "enabled" ${enabled:-0} json_add_boolean "enabled" ${enabled:-0}
json_add_string "image" "${image:-domoticz/domoticz:latest}"
json_add_int "port" ${port:-8080} json_add_int "port" ${port:-8080}
json_add_string "data_path" "${data_path:-/srv/domoticz}" json_add_string "data_path" "${data_path:-/srv/domoticz}"
json_add_string "devices_path" "${devices_path:-/srv/devices}" 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 "firewall_wan" ${firewall_wan:-0}
json_add_boolean "mesh_enabled" ${mesh_enabled:-0} json_add_boolean "mesh_enabled" ${mesh_enabled:-0}
# Docker availability # LXC availability
if command -v docker >/dev/null 2>&1; then if command -v lxc-start >/dev/null 2>&1; then
json_add_boolean "docker_available" 1 json_add_boolean "lxc_available" 1
else else
json_add_boolean "docker_available" 0 json_add_boolean "lxc_available" 0
fi fi
# Container status # 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" 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" 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_status" "stopped"
json_add_string "container_uptime" "" json_add_string "container_uptime" ""
json_add_string "memory_usage" ""
else else
json_add_string "container_status" "not_installed" json_add_string "container_status" "not_installed"
json_add_string "container_uptime" "" json_add_string "container_uptime" ""
json_add_string "memory_usage" ""
fi fi
# Mosquitto broker status # Mosquitto broker status
@ -225,7 +246,7 @@ case "$1" in
lines=$(echo "$input" | jsonfilter -e '@.lines' 2>/dev/null) lines=$(echo "$input" | jsonfilter -e '@.lines' 2>/dev/null)
[ -z "$lines" ] && lines=50 [ -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_init
json_add_string "logs" "$logs" json_add_string "logs" "$logs"
json_dump json_dump

View File

@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-domoticz PKG_NAME:=secubox-app-domoticz
PKG_RELEASE:=3 PKG_RELEASE:=4
PKG_VERSION:=1.0.0 PKG_VERSION:=1.0.0
PKG_ARCH:=all PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr> PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
@ -14,13 +14,13 @@ define Package/secubox-app-domoticz
CATEGORY:=Utilities CATEGORY:=Utilities
PKGARCH:=all PKGARCH:=all
SUBMENU:=SecuBox Apps SUBMENU:=SecuBox Apps
TITLE:=SecuBox Domoticz docker app TITLE:=SecuBox Domoticz LXC app
DEPENDS:=dockerd +docker +containerd DEPENDS:=+lxc +lxc-common
endef endef
define Package/secubox-app-domoticz/description define Package/secubox-app-domoticz/description
Installer, configuration, and service manager for running Domoticz 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 endef
define Package/secubox-app-domoticz/conffiles define Package/secubox-app-domoticz/conffiles

View File

@ -1,6 +1,6 @@
# SecuBox Domoticz # 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 ## Installation
@ -17,7 +17,6 @@ UCI config file: `/etc/config/domoticz`
``` ```
config domoticz 'main' config domoticz 'main'
option enabled '0' option enabled '0'
option image 'domoticz/domoticz:latest'
option data_path '/srv/domoticz' option data_path '/srv/domoticz'
option devices_path '/srv/devices' option devices_path '/srv/devices'
option port '8080' option port '8080'
@ -42,9 +41,9 @@ config domoticz 'mesh'
## Usage ## Usage
```sh ```sh
domoticzctl install # Pull image, install prerequisites domoticzctl install # Create LXC container, download Domoticz
domoticzctl uninstall # Remove container (data preserved) domoticzctl uninstall # Remove container (data preserved)
domoticzctl update # Pull latest image, restart domoticzctl update # Download latest Domoticz, restart
domoticzctl status # Show container status domoticzctl status # Show container status
domoticzctl logs [-f] # Container logs domoticzctl logs [-f] # Container logs
domoticzctl configure-mqtt # Auto-setup Mosquitto + MQTT bridge domoticzctl configure-mqtt # Auto-setup Mosquitto + MQTT bridge
@ -79,7 +78,7 @@ When `secubox-app-zigbee2mqtt` is installed:
## Dependencies ## Dependencies
- `dockerd`, `docker`, `containerd` - `lxc`, `lxc-common`
- Optional: `mosquitto-nossl`, `secubox-app-zigbee2mqtt` - Optional: `mosquitto-nossl`, `secubox-app-zigbee2mqtt`
## License ## License

View File

@ -1,6 +1,5 @@
config domoticz 'main' config domoticz 'main'
option enabled '0' option enabled '0'
option image 'domoticz/domoticz:latest'
option data_path '/srv/domoticz' option data_path '/srv/domoticz'
option devices_path '/srv/devices' option devices_path '/srv/devices'
option port '8080' option port '8080'

View File

@ -1,8 +1,13 @@
#!/bin/sh #!/bin/sh
# SecuBox Domoticz manager — IoT home automation with MQTT/Zigbee integration # SecuBox Domoticz manager — LXC Alpine container with MQTT/Zigbee integration
CONFIG="domoticz" 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 OPKG_UPDATED=0
usage() { usage() {
@ -10,13 +15,14 @@ usage() {
Usage: domoticzctl <command> Usage: domoticzctl <command>
Commands: Commands:
install Install prerequisites, prepare directories, pull image install Create LXC container, download Domoticz, configure
uninstall Remove container (preserves data) uninstall Remove container (preserves data)
check Run prerequisite checks check Run prerequisite checks
update Pull new image and restart update Download latest Domoticz release and restart
status Show container status status Show container and service status
logs [-f] Show container logs (use -f to follow) logs [N] Show last N lines of logs (default: 50)
configure-mqtt Auto-configure Mosquitto broker and Domoticz MQTT bridge shell Open interactive shell in container
configure-mqtt Auto-configure Mosquitto broker and MQTT bridge
configure-haproxy Register as HAProxy vhost for reverse proxy configure-haproxy Register as HAProxy vhost for reverse proxy
backup [path] Backup Domoticz data backup [path] Backup Domoticz data
restore <path> Restore Domoticz from backup restore <path> Restore Domoticz from backup
@ -26,6 +32,8 @@ Commands:
USAGE USAGE
} }
# ---------- helpers ----------
require_root() { [ "$(id -u)" -eq 0 ]; } require_root() { [ "$(id -u)" -eq 0 ]; }
uci_get() { uci_get() {
@ -33,13 +41,8 @@ uci_get() {
uci -q get ${CONFIG}.${section}.$1 uci -q get ${CONFIG}.${section}.$1
} }
defaults() { log_info() { echo "[INFO] $*"; logger -t domoticzctl "$*"; }
image="$(uci_get image || echo domoticz/domoticz:latest)" log_error() { echo "[ERROR] $*" >&2; logger -t domoticzctl -p err "$*"; }
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_dir() { [ -d "$1" ] || mkdir -p "$1"; }
@ -55,60 +58,450 @@ ensure_packages() {
done 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 defaults
ensure_dir "$data_path" local arch=$(detect_arch)
[ -d /sys/fs/cgroup ] || { echo "[ERROR] /sys/fs/cgroup missing" >&2; return 1; } local mem_limit=$(uci_get memory_limit || echo "512M")
ensure_packages dockerd docker containerd
/etc/init.d/dockerd enable >/dev/null 2>&1 # Convert memory limit to bytes
/etc/init.d/dockerd start >/dev/null 2>&1 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() { generate_start_script() {
docker stop "$CONTAINER" >/dev/null 2>&1 || true defaults
docker rm "$CONTAINER" >/dev/null 2>&1 || true 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() { cmd_install() {
require_root || { echo "Root required" >&2; exit 1; } require_root || { log_error "Root required"; exit 1; }
check_prereqs || exit 1
ensure_dir "$data_path/config" if lxc_exists; then
pull_image || exit 1 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 set ${CONFIG}.main.enabled='1'
uci commit ${CONFIG} uci commit ${CONFIG}
/etc/init.d/domoticz enable /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() { 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 /etc/init.d/domoticz stop >/dev/null 2>&1
stop_container lxc_stop
defaults 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 set ${CONFIG}.main.enabled='0'
uci commit ${CONFIG} uci commit ${CONFIG}
/etc/init.d/domoticz disable >/dev/null 2>&1 /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() { cmd_update() {
require_root || { echo "Root required" >&2; exit 1; } require_root || { log_error "Root required"; exit 1; }
pull_image || 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 /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() { 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=$(uci_get broker mqtt)
local broker_port=$(uci_get broker_port mqtt) local broker_port=$(uci_get broker_port mqtt)
@ -122,9 +515,9 @@ cmd_configure_mqtt() {
# Ensure Mosquitto is installed and running # Ensure Mosquitto is installed and running
if ! command -v mosquitto >/dev/null 2>&1; then 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 || { ensure_packages mosquitto-nossl mosquitto-client-nossl || {
echo "[ERROR] Failed to install Mosquitto" >&2 log_error "Failed to install Mosquitto"
return 1 return 1
} }
fi fi
@ -132,7 +525,6 @@ cmd_configure_mqtt() {
# Configure Mosquitto listener # Configure Mosquitto listener
local mosquitto_conf="/etc/mosquitto/mosquitto.conf" local mosquitto_conf="/etc/mosquitto/mosquitto.conf"
if [ -f "$mosquitto_conf" ]; then if [ -f "$mosquitto_conf" ]; then
# Ensure listener on configured port
if ! grep -q "^listener ${broker_port}" "$mosquitto_conf" 2>/dev/null; then if ! grep -q "^listener ${broker_port}" "$mosquitto_conf" 2>/dev/null; then
echo "" >> "$mosquitto_conf" echo "" >> "$mosquitto_conf"
echo "# Auto-configured by domoticzctl" >> "$mosquitto_conf" echo "# Auto-configured by domoticzctl" >> "$mosquitto_conf"
@ -156,7 +548,7 @@ cmd_configure_mqtt() {
done done
if ! netstat -tln 2>/dev/null | grep -q ":${broker_port} "; then 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 fi
# Check zigbee2mqtt MQTT settings # Check zigbee2mqtt MQTT settings
@ -164,7 +556,7 @@ cmd_configure_mqtt() {
local z2m_mqtt_uri=$(uci -q get zigbee2mqtt.main.mqtt_host) local z2m_mqtt_uri=$(uci -q get zigbee2mqtt.main.mqtt_host)
local z2m_base=$(uci -q get zigbee2mqtt.main.base_topic) 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_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) local z2m_port_conf=$(echo "$z2m_mqtt_uri" | sed 's|^mqtt[s]*://||' | cut -d: -f2)
z2m_host="${z2m_host:-127.0.0.1}" z2m_host="${z2m_host:-127.0.0.1}"
@ -190,9 +582,6 @@ cmd_configure_mqtt() {
uci set ${CONFIG}.mqtt.z2m_topic="$z2m_topic" uci set ${CONFIG}.mqtt.z2m_topic="$z2m_topic"
uci commit ${CONFIG} 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 ""
echo "MQTT bridge configured:" echo "MQTT bridge configured:"
echo " Broker: ${broker}:${broker_port}" echo " Broker: ${broker}:${broker_port}"
@ -207,129 +596,137 @@ cmd_configure_mqtt() {
} }
cmd_configure_haproxy() { 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 domain=$(uci_get domain network)
local port_val=$(uci_get port) local port_val=$(uci_get port)
domain="${domain:-domoticz.secubox.local}" domain="${domain:-domoticz.secubox.local}"
port_val="${port_val:-8080}" port_val="${port_val:-8080}"
# Use haproxyctl if available
if command -v haproxyctl >/dev/null 2>&1; then if command -v haproxyctl >/dev/null 2>&1; then
haproxyctl add-vhost "$domain" "127.0.0.1:${port_val}" 2>&1 haproxyctl add-vhost "$domain" "127.0.0.1:${port_val}" 2>&1
local code=$? local code=$?
if [ $code -eq 0 ]; then if [ $code -eq 0 ]; then
uci set ${CONFIG}.network.haproxy='1' uci set ${CONFIG}.network.haproxy='1'
uci commit ${CONFIG} 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 else
echo "[ERROR] haproxyctl add-vhost failed" >&2 log_error "haproxyctl add-vhost failed"
return 1 return 1
fi fi
else else
echo "[ERROR] haproxyctl not found — install secubox-app-haproxy first" >&2 log_error "haproxyctl not found — install secubox-app-haproxy first"
return 1 return 1
fi fi
} }
cmd_backup() { cmd_backup() {
require_root || { echo "Root required" >&2; exit 1; } require_root || { log_error "Root required"; exit 1; }
defaults defaults
local backup_path="${1:-/tmp/domoticz-backup-$(date +%Y%m%d-%H%M%S).tar.gz}" local backup_path="${1:-/tmp/domoticz-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
if [ ! -d "$data_path/config" ]; then if [ ! -d "$data_path" ]; then
echo "[ERROR] No data to backup at ${data_path}/config" >&2 log_error "No data to backup at ${data_path}"
return 1 return 1
fi fi
tar czf "$backup_path" -C "$data_path" config 2>&1 tar czf "$backup_path" -C "$data_path" . 2>&1
local code=$? local code=$?
if [ $code -eq 0 ]; then if [ $code -eq 0 ]; then
echo "Backup created: ${backup_path}" log_info "Backup created: ${backup_path}"
else else
echo "[ERROR] Backup failed" >&2 log_error "Backup failed"
return 1 return 1
fi fi
} }
cmd_restore() { cmd_restore() {
require_root || { echo "Root required" >&2; exit 1; } require_root || { log_error "Root required"; exit 1; }
defaults defaults
local backup_path="$1" local backup_path="$1"
if [ -z "$backup_path" ] || [ ! -f "$backup_path" ]; then 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 return 1
fi fi
# Stop service before restore
/etc/init.d/domoticz stop >/dev/null 2>&1 /etc/init.d/domoticz stop >/dev/null 2>&1
stop_container lxc_stop
ensure_dir "$data_path" ensure_dir "$data_path"
tar xzf "$backup_path" -C "$data_path" 2>&1 tar xzf "$backup_path" -C "$data_path" 2>&1
local code=$? local code=$?
if [ $code -eq 0 ]; then 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" echo "Restart Domoticz with: /etc/init.d/domoticz start"
else else
echo "[ERROR] Restore failed" >&2 log_error "Restore failed"
return 1 return 1
fi fi
} }
cmd_mesh_register() { cmd_mesh_register() {
require_root || { echo "Root required" >&2; exit 1; } require_root || { log_error "Root required"; exit 1; }
defaults defaults
if command -v secubox-p2p >/dev/null 2>&1; then if command -v secubox-p2p >/dev/null 2>&1; then
secubox-p2p register-service domoticz "$port" 2>&1 secubox-p2p register-service domoticz "$port" 2>&1
uci set ${CONFIG}.mesh.enabled='1' uci set ${CONFIG}.mesh.enabled='1'
uci commit ${CONFIG} uci commit ${CONFIG}
echo "Domoticz registered in P2P mesh on port ${port}" log_info "Domoticz registered in P2P mesh on port ${port}"
else else
echo "[ERROR] secubox-p2p not found — install secubox-p2p first" >&2 log_error "secubox-p2p not found — install secubox-p2p first"
return 1 return 1
fi fi
} }
cmd_service_run() { cmd_service_run() {
require_root || { echo "Root required" >&2; exit 1; } require_root || { log_error "Root required"; exit 1; }
check_prereqs || exit 1
defaults
stop_container
local docker_args="--name $CONTAINER -p ${port}:8080 -v $data_path/config:/config" if ! lxc_exists; then
[ -d "$devices_path" ] && docker_args="$docker_args -v $devices_path:/devices" log_error "Container not installed. Run 'domoticzctl install' first."
exit 1
# 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 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() { cmd_service_stop() {
require_root || { echo "Root required" >&2; exit 1; } require_root || { log_error "Root required"; exit 1; }
stop_container lxc_stop
log_info "Domoticz container stopped."
} }
# ---------- main ----------
case "${1:-}" in case "${1:-}" in
install) shift; cmd_install "$@" ;; install) shift; cmd_install "$@" ;;
uninstall) shift; cmd_uninstall "$@" ;; uninstall) shift; cmd_uninstall "$@" ;;
check) shift; cmd_check "$@" ;; check) shift; cmd_check "$@" ;;
update) shift; cmd_update "$@" ;; update) shift; cmd_update "$@" ;;
status) shift; cmd_status "$@" ;; status) shift; cmd_status "$@" ;;
logs) shift; cmd_logs "$@" ;; logs) shift; cmd_logs "$@" ;;
configure-mqtt) shift; cmd_configure_mqtt "$@" ;; shell) shift; cmd_shell "$@" ;;
configure-mqtt) shift; cmd_configure_mqtt "$@" ;;
configure-haproxy) shift; cmd_configure_haproxy "$@" ;; configure-haproxy) shift; cmd_configure_haproxy "$@" ;;
backup) shift; cmd_backup "$@" ;; backup) shift; cmd_backup "$@" ;;
restore) shift; cmd_restore "$@" ;; restore) shift; cmd_restore "$@" ;;
mesh-register) shift; cmd_mesh_register "$@" ;; mesh-register) shift; cmd_mesh_register "$@" ;;
service-run) shift; cmd_service_run "$@" ;; service-run) shift; cmd_service_run "$@" ;;
service-stop) shift; cmd_service_stop "$@" ;; service-stop) shift; cmd_service_stop "$@" ;;
help|--help|-h|'') usage ;; help|--help|-h|'') usage ;;
*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;; *) log_error "Unknown command: $1"; usage >&2; exit 1 ;;
esac esac

View File

@ -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_BASE=$theme_base
lxc.environment = STREAMLIT_THEME_PRIMARY=$theme_primary 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 # Security
lxc.cap.drop = sys_admin sys_module mac_admin mac_override sys_time sys_rawio lxc.cap.drop = sys_admin sys_module mac_admin mac_override sys_time sys_rawio
# Resource limits # Resource limits
lxc.cgroup.memory.limit_in_bytes = $mem_bytes lxc.cgroup2.memory.max = $mem_bytes
# Init command # Init command
lxc.init.cmd = /opt/start-streamlit.sh lxc.init.cmd = /opt/start-streamlit.sh

View File

@ -1522,7 +1522,7 @@
"name": "Domoticz", "name": "Domoticz",
"version": "1.0.0", "version": "1.0.0",
"category": "iot", "category": "iot",
"runtime": "docker", "runtime": "lxc",
"description": "Home automation system with MQTT bridge, Zigbee2MQTT integration, and IoT device management", "description": "Home automation system with MQTT bridge, Zigbee2MQTT integration, and IoT device management",
"author": "CyberMind.fr", "author": "CyberMind.fr",
"license": "GPL-3.0", "license": "GPL-3.0",
@ -1532,7 +1532,7 @@
"home-automation", "home-automation",
"iot", "iot",
"smart-home", "smart-home",
"docker", "lxc",
"mqtt", "mqtt",
"zigbee", "zigbee",
"mesh" "mesh"
@ -1541,8 +1541,8 @@
"required": [ "required": [
"secubox-app-domoticz", "secubox-app-domoticz",
"luci-app-domoticz", "luci-app-domoticz",
"docker", "lxc",
"dockerd" "lxc-common"
], ],
"optional": [ "optional": [
"mosquitto-nossl", "mosquitto-nossl",
@ -1562,18 +1562,20 @@
"min_storage_mb": 100 "min_storage_mb": 100
}, },
"status": "stable", "status": "stable",
"pkg_version": "1.0.0-3", "pkg_version": "1.0.0-4",
"app_version": "1.0.0", "app_version": "1.0.0",
"changelog": { "changelog": {
"1.0.0-3": { "1.0.0-4": {
"date": "2026-02-04", "date": "2026-02-04",
"changes": [ "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", "LuCI dashboard with IoT integration status",
"MQTT auto-bridge for Mosquitto and Zigbee2MQTT", "MQTT auto-bridge for Mosquitto and Zigbee2MQTT",
"HAProxy reverse proxy integration", "HAProxy reverse proxy integration",
"P2P mesh service registration", "P2P mesh service registration",
"Backup and restore support", "Backup and restore support"
"USB device passthrough visibility"
] ]
}, },
"1.0.0": { "1.0.0": {