Files
rothmanandClaude Opus 5.5 eb0242c182
CI / test-bookworm (push) Successful in 29s
CI / test-latest (push) Successful in 31s
Rename every remaining voxip name to daynivo, with a one-time migration
Services (daynivo-kiosk-*, daynivo-network-guard, daynivo-boot-import),
programs in /usr/lib/daynivo-kiosk, folders /etc, /var/lib, /var/log and
/var/cache daynivo-kiosk*, the system user daynivo, DAYNIVO_* settings,
the plymouth theme, network profiles daynivo-eth0/wlan0, the image stage
and its first user daynivoadmin.

install.sh moves a kiosk installed up to 1.2.x over once: old services
stopped and removed, folders moved (configuration, password, browser
profile, logs) with symlinks under the old names, the voxip user renamed
with the same uid, network profiles renamed unless a change awaits
confirmation, and old leftovers removed. Tested by upgrading a configured
1.2.0 installation in a container, and running the install a second time.

Old names keep working where people type or place them: voxip-config.json,
voxip-authorized_keys, voxip-network-reset, voxip-kiosk-ctl, and
VOXIP_DEFAULT_URL / VOXIP_DEVICE_NAME for image builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:58:03 +01:00

415 lines
19 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Daynivo Kiosk OS installer.
#
# Turns Raspberry Pi OS Lite (Bookworm/Trixie, NetworkManager) into a Daynivo
# kiosk. Also used by the image builder (inside the pi-gen chroot), so a
# flashed image and a manual install are identical.
#
# sudo ./install.sh [--url https://…] [--name "Hal begane grond"] [--hostname hal-bg]
# sudo ./install.sh --upgrade # update code, keep configuration
# ./install.sh --image … # used by image/stage-daynivo (no service starts)
#
# Existing configuration in /etc/daynivo-kiosk is never overwritten.
set -euo pipefail
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MODE="install"
URL=""
NAME=""
NEW_HOSTNAME=""
FORCE=0
LIB=/usr/lib/daynivo-kiosk
SHARE=/usr/share/daynivo-kiosk
DOC=/usr/share/doc/daynivo-kiosk
ETC=/etc/daynivo-kiosk
UNITS=(daynivo-kiosk-x.service daynivo-kiosk-browser.service daynivo-kiosk-agent.service daynivo-kiosk-manager.service
daynivo-kiosk-healthcheck.service daynivo-kiosk-healthcheck.timer daynivo-network-guard.service daynivo-boot-import.service
daynivo-kiosk-update.service daynivo-kiosk-update.timer)
ENABLE=(daynivo-kiosk-x.service daynivo-kiosk-browser.service daynivo-kiosk-agent.service daynivo-kiosk-manager.service
daynivo-kiosk-healthcheck.timer daynivo-network-guard.service daynivo-boot-import.service daynivo-kiosk-update.timer)
say() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mLet op:\033[0m %s\n' "$*" >&2; }
die() { printf '\033[1;31mFout:\033[0m %s\n' "$*" >&2; exit 1; }
usage() {
sed -n '3,15p' "$0" | sed 's/^# \{0,1\}//'
exit "${1:-0}"
}
while [[ $# -gt 0 ]]; do
case "$1" in
--url) URL="${2:?--url vereist een waarde}"; shift 2 ;;
--name) NAME="${2:?--name vereist een waarde}"; shift 2 ;;
--hostname) NEW_HOSTNAME="${2:?--hostname vereist een waarde}"; shift 2 ;;
--upgrade) MODE="upgrade"; shift ;;
--image) MODE="image"; shift ;;
--force) FORCE=1; shift ;;
-h|--help) usage 0 ;;
*) warn "Onbekende optie: $1"; usage 1 ;;
esac
done
[[ $EUID -eq 0 ]] || die "Voer dit script uit met sudo."
[[ -f "$SRC/src/daynivo_kiosk/config.py" ]] || die "Start install.sh vanuit de Daynivo Kiosk OS-repository."
in_chroot() {
if command -v systemd-detect-virt >/dev/null 2>&1 && systemd-detect-virt --chroot --quiet; then
return 0
fi
[[ "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/. 2>/dev/null || echo x)" ]]
}
LIVE=1
if [[ $MODE == image ]] || in_chroot; then LIVE=0; fi
# --- checks ---------------------------------------------------------------
# In a subshell: os-release defines NAME and VERSION, which must not clobber ours.
# shellcheck disable=SC1091
CODENAME="$(. /etc/os-release && echo "${VERSION_CODENAME:-}")"
case "$CODENAME" in
bookworm|trixie) ;;
*) [[ $FORCE -eq 1 ]] || die "Niet-ondersteunde release '${CODENAME:-?}'. Ondersteund: bookworm, trixie (--force om toch door te gaan)." ;;
esac
if [[ $LIVE -eq 1 && $MODE == install ]]; then
if ! systemctl is-enabled --quiet NetworkManager.service 2>/dev/null && systemctl is-enabled --quiet dhcpcd.service 2>/dev/null; then
die "Dit systeem gebruikt dhcpcd. Daynivo Kiosk OS vereist NetworkManager (standaard sinds Bookworm). Schakel om via 'sudo raspi-config' → Advanced Options → Network Config, of gebruik de kant-en-klare image."
fi
fi
VERSION="$(tr -d '[:space:]' < "$SRC/VERSION")"
say "Daynivo Kiosk OS $VERSION installeren (modus: $MODE)"
# --- packages -----------------------------------------------------------------
pkg_available() { apt-cache policy "$1" 2>/dev/null | grep -q 'Candidate: [0-9]'; }
{
say "Pakketten installeren"
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
CHROMIUM=chromium
if pkg_available chromium-browser && [[ $CODENAME == bookworm ]]; then CHROMIUM=chromium-browser; fi
PKGS=(
"$CHROMIUM" xserver-xorg-core xserver-xorg-input-libinput xserver-xorg-legacy x11-xserver-utils xauth
fonts-dejavu-core fonts-noto-color-emoji
python3 python3-flask python3-argon2 python3-websocket gunicorn
network-manager avahi-daemon iw rfkill iproute2 openssl logrotate sudo kbd plymouth
)
for opt in systemd-zram-generator libraspberrypi-bin raspi-utils-core; do
if pkg_available "$opt"; then PKGS+=("$opt"); fi
done
# The "chromium" package needs its setuid sandbox where unprivileged user namespaces are unavailable.
if [[ $CHROMIUM == chromium ]] && pkg_available chromium-sandbox; then PKGS+=(chromium-sandbox); fi
apt-get install -y --no-install-recommends "${PKGS[@]}"
}
# --- migration from the VoxIP names ---------------------------------------------------
# Kiosks installed before the rename (up to 1.2.x) use voxip-* services and folders
# and a "voxip" user. Move them over once, keeping the configuration, the password,
# the browser profile (offline cache) and the network settings. The old folder
# names stay as symlinks for whatever still runs from the old version, such as
# the updater that started this upgrade.
if [[ -d /etc/voxip-kiosk && ! -L /etc/voxip-kiosk && ! -e /etc/daynivo-kiosk ]]; then
say "Overstappen van de VoxIP-namen naar Daynivo"
if [[ $LIVE -eq 1 ]]; then
# Not voxip-kiosk-update.service: a nightly auto-update runs this very script in it.
systemctl stop voxip-kiosk-healthcheck.timer voxip-kiosk-update.timer voxip-kiosk-agent.service \
voxip-kiosk-browser.service voxip-kiosk-x.service voxip-kiosk-manager.service 2>/dev/null || true
fi
for unit in voxip-kiosk-x.service voxip-kiosk-browser.service voxip-kiosk-agent.service voxip-kiosk-manager.service \
voxip-kiosk-healthcheck.service voxip-kiosk-healthcheck.timer voxip-kiosk-update.service \
voxip-kiosk-update.timer voxip-network-guard.service voxip-boot-import.service; do
systemctl disable "$unit" >/dev/null 2>&1 || true
rm -f "/etc/systemd/system/$unit"
done
if [[ $LIVE -eq 1 ]]; then systemctl daemon-reload; fi
for old in /etc/voxip-kiosk /var/lib/voxip-kiosk /var/lib/voxip-kiosk-network /var/lib/voxip-kiosk-browser \
/var/log/voxip-kiosk /var/cache/voxip-kiosk; do
new="${old/voxip/daynivo}"
if [[ -d $old && ! -L $old && ! -e $new ]]; then
mv "$old" "$new"
ln -s "$new" "$old"
fi
done
# Same uid and gid, so every file keeps the right owner.
if id voxip >/dev/null 2>&1 && ! id daynivo >/dev/null 2>&1; then
usermod -l daynivo -d /var/lib/daynivo-kiosk voxip
if getent group voxip >/dev/null; then groupmod -n daynivo voxip; fi
fi
# Network profiles written by the web interface; not while a change awaits confirmation.
if [[ ! -f /var/lib/daynivo-kiosk-network/pending.json ]]; then
renamed=0
for iface in eth0 wlan0; do
old=/etc/NetworkManager/system-connections/voxip-$iface.nmconnection
new=/etc/NetworkManager/system-connections/daynivo-$iface.nmconnection
if [[ -f $old && ! -e $new ]]; then
sed -i "s/^id=voxip-$iface\$/id=daynivo-$iface/" "$old"
mv "$old" "$new"
chmod 0600 "$new"
renamed=1
fi
done
if [[ $renamed -eq 1 && $LIVE -eq 1 ]]; then nmcli connection reload 2>/dev/null || true; fi
fi
rm -f /etc/sudoers.d/voxip-kiosk /etc/systemd/journald.conf.d/voxip-kiosk.conf \
/etc/systemd/system.conf.d/voxip-watchdog.conf /etc/logrotate.d/voxip-kiosk \
/etc/avahi/services/voxip-kiosk.service /etc/chromium/policies/managed/voxip-kiosk.json \
/etc/chromium-browser/policies/managed/voxip-kiosk.json /usr/local/sbin/voxip-kiosk-ctl
if [[ -f /etc/ssh/sshd_config.d/voxip-kiosk.conf ]]; then
mv /etc/ssh/sshd_config.d/voxip-kiosk.conf /etc/ssh/sshd_config.d/daynivo-kiosk.conf
fi
if [[ -f /etc/X11/Xwrapper.config.voxip-orig ]]; then
mv /etc/X11/Xwrapper.config.voxip-orig /etc/X11/Xwrapper.config.daynivo-orig
fi
if [[ -f /etc/systemd/system/display-kiosk.service.voxip-disabled ]]; then
mv /etc/systemd/system/display-kiosk.service.voxip-disabled /etc/systemd/system/display-kiosk.service.daynivo-disabled
fi
# The old programs: an updater that started this install has its code loaded already.
rm -rf /usr/lib/voxip-kiosk /usr/share/voxip-kiosk /usr/share/doc/voxip-kiosk /usr/share/plymouth/themes/voxip-kiosk
fi
# --- users ----------------------------------------------------------------------
say "Gebruikers aanmaken"
getent group kiosk >/dev/null || groupadd --system kiosk
id kiosk >/dev/null 2>&1 || useradd --system --gid kiosk --home-dir /home/kiosk --create-home \
--shell /usr/sbin/nologin --comment "Daynivo kiosk display" kiosk
for g in video render input audio; do
getent group "$g" >/dev/null && usermod -aG "$g" kiosk
done
getent group daynivo >/dev/null || groupadd --system daynivo
id daynivo >/dev/null 2>&1 || useradd --system --gid daynivo --home-dir /var/lib/daynivo-kiosk --no-create-home \
--shell /usr/sbin/nologin --comment "Daynivo Kiosk Manager" daynivo
for g in systemd-journal video; do
getent group "$g" >/dev/null && usermod -aG "$g" daynivo
done
# --- application files --------------------------------------------------------------
say "Programmabestanden installeren"
install -d -m 0755 "$LIB" "$SHARE" "$DOC"
rm -rf "$LIB/python.new"
install -d -m 0755 "$LIB/python.new"
tar -C "$SRC/src" --exclude='__pycache__' --exclude='*.pyc' -cf - daynivo_kiosk | tar -C "$LIB/python.new" -xf -
chown -R root:root "$LIB/python.new"
chmod -R u=rwX,go=rX "$LIB/python.new"
python3 -m compileall -q "$LIB/python.new" >/dev/null
rm -rf "$LIB/python.old"
[[ -d "$LIB/python" ]] && mv "$LIB/python" "$LIB/python.old"
mv "$LIB/python.new" "$LIB/python"
rm -rf "$LIB/python.old"
for bin in daynivo-kiosk-agent daynivo-kiosk-browser daynivo-kiosk-healthcheck daynivo-kiosk-helper daynivo-kiosk-manager daynivo-kiosk-updater daynivo-kiosk-x; do
install -m 0755 -o root -g root "$SRC/bin/$bin" "$LIB/$bin"
done
install -m 0755 -o root -g root "$SRC/bin/daynivo-kiosk-ctl" /usr/local/sbin/daynivo-kiosk-ctl
# The name from before the rename to Daynivo keeps working.
ln -sfn daynivo-kiosk-ctl /usr/local/sbin/voxip-kiosk-ctl
rm -rf "$SHARE/www"
install -d -m 0755 "$SHARE/www"
install -m 0644 "$SRC"/www/* "$SHARE/www/"
install -m 0644 "$SRC/VERSION" "$SHARE/VERSION"
install -m 0644 "$SRC/README.md" "$DOC/README.md"
install -m 0644 "$SRC"/docs/*.md "$DOC/"
# --- configuration -------------------------------------------------------------------
say "Configuratie"
install -d -o daynivo -g kiosk -m 2750 "$ETC"
install -d -o daynivo -g daynivo -m 0750 /var/lib/daynivo-kiosk
install -d -o root -g root -m 0755 /var/lib/daynivo-kiosk-network
install -d -o daynivo -g daynivo -m 0750 /var/log/daynivo-kiosk
# Migrate from Daynivo's raspberry-pi/install-kiosk.sh: take over its URL, then disable it.
LEGACY_KIOSK_UNIT=/etc/systemd/system/display-kiosk.service
if [[ -f $LEGACY_KIOSK_UNIT ]]; then
OLD_URL="$(grep -oE 'https://[^" ]+' "$LEGACY_KIOSK_UNIT" | tail -n1 || true)"
if [[ -n $OLD_URL && -z $URL && ! -f $ETC/config.json ]]; then
URL="$OLD_URL"
say "URL overgenomen van de Daynivo-kioskinstallatie"
fi
if [[ $LIVE -eq 1 ]]; then systemctl disable --now display-kiosk.service 2>/dev/null || true; fi
mv "$LEGACY_KIOSK_UNIT" "$LEGACY_KIOSK_UNIT.daynivo-disabled"
fi
PYRUN=(python3 -I -c)
"${PYRUN[@]}" '
import json, os, sys
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
from daynivo_kiosk import config as c
path, default, url, name = sys.argv[1:5]
updates = {}
if url: updates["url"] = url
if name: updates["device_name"] = name
if os.path.exists(path):
# The old default device name went along with the rename to Daynivo; a chosen name stays.
if "device_name" not in updates and c.load_config(c.Path(path)).get("device_name") == "VoxIP Kiosk":
updates["device_name"] = "Daynivo Kiosk"
if updates:
c.save_config(c.Path(path), updates)
print("Bestaande configuratie bijgewerkt:", ", ".join(updates))
else:
print("Bestaande configuratie behouden")
else:
with open(default, encoding="utf-8") as fh:
data = json.load(fh)
data.update(c.validate(updates))
c.validate(data)
c.atomic_write_json(c.Path(path), data)
print("Nieuwe configuratie aangemaakt")
' "$ETC/config.json" "$SRC/config/config.default.json" "$URL" "$NAME" || die "Ongeldige --url of --name."
chown daynivo:kiosk "$ETC/config.json" "$ETC/config.json.bak" 2>/dev/null || true
chmod 0640 "$ETC/config.json"
touch "$ETC/.config.lock" && chown daynivo:kiosk "$ETC/.config.lock" && chmod 0640 "$ETC/.config.lock"
[[ -f $ETC/admin.json ]] && chown daynivo:daynivo "$ETC/admin.json" && chmod 0600 "$ETC/admin.json"
# --- system integration ----------------------------------------------------------------
say "Systeemintegratie"
TMP_SUDOERS="$(mktemp)"
install -m 0440 "$SRC/config/sudoers" "$TMP_SUDOERS"
visudo -cqf "$TMP_SUDOERS" || die "sudoers-bestand ongeldig"
mv "$TMP_SUDOERS" /etc/sudoers.d/daynivo-kiosk
chown root:root /etc/sudoers.d/daynivo-kiosk
install -D -m 0644 "$SRC/config/journald.conf" /etc/systemd/journald.conf.d/daynivo-kiosk.conf
install -D -m 0644 "$SRC/config/watchdog.conf" /etc/systemd/system.conf.d/daynivo-watchdog.conf
install -D -m 0644 "$SRC/config/logrotate" /etc/logrotate.d/daynivo-kiosk
if [[ -f /etc/X11/Xwrapper.config && ! -f /etc/X11/Xwrapper.config.daynivo-orig ]]; then
cp -a /etc/X11/Xwrapper.config /etc/X11/Xwrapper.config.daynivo-orig
fi
install -D -m 0644 "$SRC/config/Xwrapper.config" /etc/X11/Xwrapper.config
for dir in /etc/chromium/policies/managed /etc/chromium-browser/policies/managed; do
install -D -m 0644 "$SRC/config/chromium/daynivo-kiosk.json" "$dir/daynivo-kiosk.json"
done
ADMIN_PORT="$(python3 -I -c 'import sys; sys.path.insert(0, "/usr/lib/daynivo-kiosk/python"); from daynivo_kiosk.config import load_config; print(load_config(__import__("pathlib").Path(sys.argv[1]))["admin_port"])' "$ETC/config.json")"
install -d -m 0755 /etc/avahi/services
sed "s#<port>8080</port>#<port>${ADMIN_PORT}</port>#" "$SRC/config/avahi-daynivo.service" > /etc/avahi/services/daynivo-kiosk.service
chmod 0644 /etc/avahi/services/daynivo-kiosk.service
# Swap: zram in RAM instead of a swap file on the SD card (Trixie's rpi-swap already does this).
if dpkg -s systemd-zram-generator >/dev/null 2>&1 && ! dpkg -s rpi-swap >/dev/null 2>&1; then
install -m 0644 "$SRC/config/zram-generator.conf" /etc/systemd/zram-generator.conf
if dpkg -s dphys-swapfile >/dev/null 2>&1; then
if [[ $LIVE -eq 1 ]]; then dphys-swapfile swapoff 2>/dev/null || true; fi
systemctl disable dphys-swapfile.service 2>/dev/null || true
rm -f /var/swap
fi
fi
# /tmp in RAM (default on Trixie, opt-in on Bookworm).
if [[ -f /usr/share/systemd/tmp.mount && ! -e /etc/systemd/system/tmp.mount ]] && ! systemctl is-enabled --quiet tmp.mount 2>/dev/null; then
cp /usr/share/systemd/tmp.mount /etc/systemd/system/tmp.mount
systemctl enable tmp.mount 2>/dev/null || true
fi
# Kernel command line: no console blanking (the kiosk runs on vt7, but keep the console
# awake too), and a quiet boot behind the splash: no boot text, Raspberry logos or cursor.
add_cmdline() { # add "key" or "key=value" unless the key is already present
local key="${2%%=*}"
grep -qE "(^| )${key//./\\.}(=[^ ]*)?( |\$)" "$1" || sed -i "1 s/\$/ $2/" "$1"
}
for cmdline in /boot/firmware/cmdline.txt /boot/cmdline.txt; do
if [[ -f $cmdline ]]; then
for opt in consoleblank=0 quiet splash plymouth.ignore-serial-consoles logo.nologo vt.global_cursor_default=0; do
add_cmdline "$cmdline" "$opt"
done
config_txt="$(dirname "$cmdline")/config.txt"
# No rainbow square from the firmware. [all] in case the file ends in a model section.
if [[ -f $config_txt ]] && ! grep -q '^disable_splash=' "$config_txt"; then
printf '\n[all]\ndisable_splash=1\n' >> "$config_txt"
fi
break
fi
done
# Boot splash: logo on white (config/plymouth). Rebuilding the initramfs takes a while
# on a Pi 3, so only when the theme changed.
SPLASH_DIR=/usr/share/plymouth/themes/daynivo-kiosk
install -d -m 0755 "$SPLASH_DIR"
install -m 0644 "$SRC"/config/plymouth/daynivo-kiosk/* "$SPLASH_DIR/"
if [[ ! -f /var/lib/daynivo-kiosk/previous-plymouth-theme ]]; then
plymouth-set-default-theme > /var/lib/daynivo-kiosk/previous-plymouth-theme 2>/dev/null || true
fi
SPLASH_SUM="$(cat "$SPLASH_DIR"/* | sha256sum | cut -d' ' -f1)"
if [[ "$(plymouth-set-default-theme 2>/dev/null)" != daynivo-kiosk || "$(cat /var/lib/daynivo-kiosk/splash.sha256 2>/dev/null)" != "$SPLASH_SUM" ]]; then
say "Opstartscherm instellen (initramfs bijwerken, dit duurt even)"
plymouth-set-default-theme daynivo-kiosk
if update-initramfs -u -k all >/dev/null 2>&1; then
echo "$SPLASH_SUM" > /var/lib/daynivo-kiosk/splash.sha256
else
warn "initramfs bijwerken mislukt; het opstartscherm verschijnt mogelijk pas later tijdens het opstarten."
fi
fi
# A desktop display manager would fight over vt7 and display :0 (Pi OS Desktop images).
# uninstall.sh re-enables the ones recorded here.
for dm in lightdm.service gdm3.service sddm.service; do
if systemctl is-enabled --quiet "$dm" 2>/dev/null; then
warn "$dm wordt uitgeschakeld (de kiosk gebruikt een eigen minimale X-sessie)."
systemctl disable "$dm" 2>/dev/null || true
grep -qx "$dm" /var/lib/daynivo-kiosk/disabled-display-managers 2>/dev/null \
|| echo "$dm" >> /var/lib/daynivo-kiosk/disabled-display-managers
fi
if [[ $LIVE -eq 1 ]] && systemctl is-active --quiet "$dm" 2>/dev/null; then
warn "$dm wordt gestopt: de desktop op het scherm sluit nu af."
systemctl stop "$dm" || true
fi
done
# --- services ------------------------------------------------------------------------------
say "Services installeren"
for unit in "${UNITS[@]}"; do
install -m 0644 "$SRC/systemd/$unit" "/etc/systemd/system/$unit"
done
if [[ $LIVE -eq 1 ]]; then systemctl daemon-reload; fi
systemctl enable "${ENABLE[@]}" NetworkManager.service avahi-daemon.service >/dev/null
if [[ ! -f /var/lib/daynivo-kiosk/previous-default-target ]]; then
systemctl get-default > /var/lib/daynivo-kiosk/previous-default-target 2>/dev/null || true
fi
systemctl set-default multi-user.target >/dev/null
if [[ -n $NEW_HOSTNAME && $LIVE -eq 0 ]]; then
warn "--hostname wordt genegeerd in image-modus (gebruik TARGET_HOSTNAME van pi-gen)."
elif [[ -n $NEW_HOSTNAME ]]; then
"$LIB/daynivo-kiosk-helper" set-hostname "$NEW_HOSTNAME" >/dev/null || die "Ongeldige hostname: $NEW_HOSTNAME"
fi
if [[ $LIVE -eq 0 ]]; then
say "Klaar (image-modus: services starten bij de eerste boot)"
exit 0
fi
say "Services starten"
systemctl restart systemd-journald.service || true
systemctl restart avahi-daemon.service || true
systemctl restart daynivo-kiosk-manager.service daynivo-kiosk-agent.service
systemctl start daynivo-kiosk-healthcheck.timer daynivo-kiosk-update.timer
systemctl reset-failed daynivo-kiosk-x.service daynivo-kiosk-browser.service 2>/dev/null || true
systemctl restart daynivo-kiosk-x.service
systemctl restart daynivo-kiosk-browser.service
HOST="$(hostname)"
SCHEME=http
grep -q '"admin_https": true' "$ETC/config.json" && SCHEME=https
echo
say "Daynivo Kiosk OS $VERSION is geïnstalleerd."
echo " Beheerinterface: ${SCHEME}://${HOST}.local:${ADMIN_PORT}"
for ip in $(hostname -I 2>/dev/null); do
[[ $ip == *:* ]] || echo " ${SCHEME}://${ip}:${ADMIN_PORT}"
done
if [[ ! -f $ETC/admin.json ]]; then
for _ in $(seq 1 20); do [[ -f $ETC/setup-code ]] && break; sleep 0.5; done
if [[ -f $ETC/setup-code ]]; then
echo " Setupcode: $(cat "$ETC/setup-code") (staat ook op het scherm)"
fi
fi
echo " Status: sudo daynivo-kiosk-ctl status"