Rename the Python package to daynivo_kiosk
CI / test-bookworm (push) Successful in 29s
CI / test-latest (push) Successful in 33s

src/voxip_kiosk -> src/daynivo_kiosk, with every import, the launchers,
install.sh, the Makefile and the manager's gunicorn app path. An upgrade
replaces /usr/lib/voxip-kiosk/python as a whole, so the old package
disappears; tested by upgrading an installation of the previous commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rothmanandClaude Opus 5.5 committed 2026-11-05 23:52:39 +01:00
1 parent d0f3206ff4
commit 6733aacb6e
62 files changed
+43 -43

No files matched your search

+127
View File
@@ -0,0 +1,127 @@
"""Network status for the web interface (read-only, no privileges needed)."""
from __future__ import annotations
import json
import re
import shutil
import subprocess
from pathlib import Path
from typing import Any
from .netconfig import INTERFACES, profile_name, split_terse
STATE_LABELS = {
"connected": "Verbonden",
"connecting": "Bezig met verbinden",
"disconnected": "Niet verbonden",
"unavailable": "Niet beschikbaar",
"unmanaged": "Niet beheerd",
}
def _nmcli(*args: str) -> str | None:
if not shutil.which("nmcli"):
return None
try:
proc = subprocess.run(["nmcli", *args], capture_output=True, text=True, timeout=10)
except (OSError, subprocess.SubprocessError):
return None
return proc.stdout if proc.returncode == 0 else None
def device_details(device: str) -> dict[str, Any]:
out = _nmcli("-t", "device", "show", device)
info: dict[str, Any] = {"addresses": [], "dns": [], "gateway": "", "hwaddr": "", "state": "", "connection": ""}
if not out:
return info
for line in out.splitlines():
key, _, value = line.partition(":")
value = value.replace("\\:", ":")
base = key.split("[", 1)[0]
if base == "IP4.ADDRESS" and value:
info["addresses"].append(value)
elif base == "IP4.DNS" and value:
info["dns"].append(value)
elif base == "IP4.GATEWAY" and value and value != "--":
info["gateway"] = value
elif base == "GENERAL.HWADDR":
info["hwaddr"] = value
elif base == "GENERAL.STATE":
info["state"] = re.sub(r"^\d+\s*", "", value).strip("() ")
elif base == "GENERAL.CONNECTION" and value != "--":
info["connection"] = value
return info
def profile_settings(interface: str) -> dict[str, Any]:
"""Current settings of our own profile, used to pre-fill the forms."""
out = _nmcli("-t", "-f", "ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns,802-11-wireless.ssid,802-11-wireless.hidden",
"connection", "show", profile_name(interface))
settings = {"exists": False, "method": "dhcp", "address": "", "gateway": "", "dns": "", "ssid": "", "hidden": False}
if not out:
return settings
settings["exists"] = True
for line in out.splitlines():
key, _, value = line.partition(":")
value = value.replace("\\:", ":")
if value == "--":
value = ""
if key == "ipv4.method":
settings["method"] = "static" if value == "manual" else "dhcp"
elif key == "ipv4.addresses":
settings["address"] = value.split(",")[0].strip()
elif key == "ipv4.gateway":
settings["gateway"] = value
elif key == "ipv4.dns":
settings["dns"] = ", ".join(x for x in value.split(",") if x)
elif key == "802-11-wireless.ssid":
settings["ssid"] = value
elif key == "802-11-wireless.hidden":
settings["hidden"] = value == "yes"
return settings
def overview() -> dict[str, Any]:
available = shutil.which("nmcli") is not None
devices = {}
status = _nmcli("-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device", "status") or ""
present = {split_terse(line)[0]: split_terse(line) for line in status.splitlines() if line}
for iface, kind in INTERFACES.items():
fields = present.get(iface)
details = device_details(iface) if fields else {}
state = fields[2] if fields and len(fields) > 2 else ""
devices[iface] = {
"interface": iface,
"kind": kind,
"present": fields is not None,
"state": state,
"state_label": STATE_LABELS.get(state, state or "Niet aanwezig"),
"connected": state == "connected",
"connection": fields[3] if fields and len(fields) > 3 and fields[3] != "--" else "",
"addresses": details.get("addresses", []),
"gateway": details.get("gateway", ""),
"dns": details.get("dns", []),
"hwaddr": details.get("hwaddr", ""),
"profile": profile_settings(iface) if available else {},
}
return {"available": available, "devices": devices, "wifi_country": wifi_country()}
def wifi_country() -> str:
if not shutil.which("iw"):
return ""
try:
out = subprocess.run(["iw", "reg", "get"], capture_output=True, text=True, timeout=5).stdout
except (OSError, subprocess.SubprocessError):
return ""
match = re.search(r"country ([A-Z0-9]{2}):", out)
return match.group(1) if match and match.group(1) != "00" else ""
def pending_change(pending_file: Path) -> dict[str, Any] | None:
try:
data = json.loads(pending_file.read_text(encoding="utf-8"))
except (OSError, ValueError):
return None
return data if isinstance(data, dict) else None