secubox-deb/remote-ui/square/deploy.sh
CyberMind dee8bf8b81
feat(remote-ui): Phase 3 — Pillow+framebuffer kiosk for Pi 4B/400 (ref #127)
* feat(remote-ui/square): carry forward Phase 2 helper + debian + firstboot (ref #127)

Snapshot of Phase 2 PR #131's validated infrastructure, brought into Phase 3
unmodified. The Chromium+PySide6 dual-window kiosk path is dropped (replaced
by Pillow+framebuffer per the Phase 3 design spec). What carries forward:

packages/secubox-eye-square/
├── helper/                  21 pytest cases green
│   ├── eye_square_helper/   FastAPI app + SO_PEERCRED auth + 4 route modules
│   │   ├── app.py           middleware (JSONResponse fix preserved)
│   │   ├── auth.py          ALLOWED_UIDS frozenset
│   │   ├── __main__.py      uvicorn UDS bind
│   │   └── routes/          usb_gadget, service, lockdown, console
│   └── tests/               6 test files (test_e2e.py dropped — it depended
│                            on the right_panel package which is gone)
└── debian/                  arm64 package shell; control will be edited by
                             Phase 3 plan to drop Chromium/Qt/X deps

remote-ui/square/
├── files/etc/systemd/system/
│   ├── secubox-eye-square-helper.service    Helper FastAPI as secubox-eye-square user
│   ├── secubox-otg-gadget.service           configfs composite gadget (square variant)
│   └── secubox-firstboot.service            oneshot trigger for firstboot.sh
├── files/etc/udev/rules.d/
│   └── 90-secubox-otg-square.rules          host-side interface rename rule
├── files/etc/apparmor.d/
│   └── secubox-eye-square-helper            CAP_NET_ADMIN + CAP_SYS_ADMIN scope
├── files/etc/secubox/eye-square.toml.example
├── files/usr/local/sbin/firstboot.sh        GPIO 5V check + hostname + SSH + toml
├── build-eye-square-image.sh                will be heavily modified — drop Chromium/Qt/X
├── install_pi4.sh                           SD flash with safety guards
└── deploy.sh                                SSH hot-update — kiosk service list will change

Phase 1's remote-ui/common/ (PR #130) is NOT brought in — Phase 3 doesn't
consume it. round/fb_dashboard.py stays on master untouched.

What's DROPPED from Phase 2 (not brought in):
- right_panel/ entire PySide6 widget tree + ipc_bridge + WebSocket server
- square-bridge.js Chromium-side TM hook override
- secubox-kiosk-x.service, secubox-square-chromium.service,
  secubox-square-right-panel.service systemd units
- etc/openbox/{autostart,rc.xml}, etc/nginx/sites-available/secubox-square
- home/secubox/.xinitrc

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(remote-ui): Phase 3 implementation plan — Pillow+framebuffer kiosk (ref #127)

25 tasks covering kiosk skeleton, theme + modules table, sim, framebuffer +
touch input, transport manager + helper client, 4 right-pane tabs (Alerts /
Module Detail / Console / Mode Controls), right_panel composer, ring_dashboard
(Pillow port of Phase 2's QPainter ring renderer — visually faithful to
round/'s dashboard but independently authored, since round/fb_dashboard.py
must remain unchanged), __main__ event loop, systemd unit, fb udev rule,
debian/control simplification (drop Qt/X/Chromium, add python3-pil/evdev),
build script + firstboot + deploy.sh edits, README + CLAUDE.md docs,
regression test, Pi 4B + Pi 400 hardware bench tests, open PR.

Plan supersedes Phase 2 PR #131 (closed). Phase 2 helper FastAPI carries
forward unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-ui/square): scaffold kiosk/ package skeleton (ref #127)

* feat(remote-ui/square/kiosk): theme.py + modules_table.py with TDD (ref #127)

* feat(remote-ui/square/kiosk): sim.py drift generator with TDD (ref #127)

* feat(remote-ui/square/kiosk): framebuffer.py mmap helper with TDD (ref #127)

* feat(remote-ui/square/kiosk): touch_input.py evdev reader + classify (ref #127)

* feat(remote-ui/square/kiosk): transport_manager.py with TDD (ref #127)

* feat(remote-ui/square/kiosk): helper_client.py sync httpx UDS with TDD (ref #127)

* feat(remote-ui/square/kiosk): tabs/alerts.py with TDD (ref #127)

* feat(remote-ui/square/kiosk): tabs/module_detail.py with TDD (ref #127)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-ui/square/kiosk): tabs/console.py with TDD (ref #127)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-ui/square/kiosk): tabs/mode_controls.py with TDD (ref #127)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-ui/square/kiosk): right_panel.py tab manager with TDD (ref #127)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-ui/square/kiosk): ring_dashboard.py with easing + alerts ribbon (ref #127)

The implementer adapted update_metrics() to store raw metric values in
_target/_current (e.g. cpu_percent=80.0) rather than the 0..1 extracted
ratio from the plan's code block — this matches the test's assertion
(_target["cpu_percent"] == 80.0). Module.extract() is now applied at
draw() time. Behaviour is identical from the renderer's perspective.

Also added 'if m.metric in metrics' guard so partial-metric updates
preserve existing targets for unspecified keys (kiosk-friendly).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-ui/square/kiosk): __main__.py event loop + smoke tests (ref #127)

Wires HelperClient + TransportManager + SimState + RingDashboard +
RightPanel + FrameBuffer into a 30 FPS event loop. Probes transport
every 30s, fetches metrics every 2s (falls back to SIM), composes
800x480 frame (dashboard + panel), blits to /dev/fb0.

Touch input integration deferred; modules from Task 6 are available
for the bench iteration to wire when the hardware ships.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-ui/square): secubox-square-kiosk.service unit + fb0 udev rule (ref #127)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(secubox-eye-square): debian/control drops Qt/X/Chromium deps for Phase 3 (ref #127)

Phase 3 replaces the Phase 2 Chromium+PySide6 stack with a single-process
Pillow+/dev/fb0 kiosk. Dependency cleanup:

 dropped: chromium, openbox, xserver-xorg, xinit, unclutter, nginx-light,
          python3-pip, python3-pyside6 (Recommends), python3-qasync
 added:   python3-pil, python3-evdev

Helper FastAPI deps unchanged (carried forward from Phase 2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(remote-ui/square): build-eye-square-image.sh slimmed for Phase 3 (ref #127)

Apt install list trimmed: dropped chromium, openbox, xserver-xorg, xinit,
unclutter, 12 libxcb-* libraries, nginx-light, python3-pip; added
python3-pil + python3-evdev. The PySide6 pip install is gone.

Removed Phase 2 vestiges:
- nginx + round/ html copy + square-bridge.js injection
- openbox autostart, xinitrc chmod
- render group (no /dev/dri access needed without Chromium)

Systemd enable list updated:
- Dropped: nginx, secubox-kiosk-x, secubox-square-chromium,
           secubox-square-right-panel
- Added:   secubox-square-kiosk
- Default target now multi-user.target (was graphical.target)

Python install copies kiosk package (was right_panel package).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(remote-ui/square): firstboot.sh enables kiosk service (drops nginx + 3 Phase 2 units) (ref #127)

Phase 3 firstboot only enables secubox-otg-gadget, secubox-eye-square-helper,
and secubox-square-kiosk. The X / Chromium / right-panel / nginx units no
longer exist on the image.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(remote-ui/square): deploy.sh targets kiosk service (drops chromium+right-panel+nginx) (ref #127)

Hot-update flow simplified for Phase 3:
- Rsync helper + kiosk Python packages only (no /var/www/, no
  square-bridge.js, no nginx content).
- Restart secubox-eye-square-helper + secubox-square-kiosk
  (was chromium + right-panel + nginx reload).
- Health check via systemctl is-active (was curl http://localhost/).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(remote-ui/square): README + CLAUDE.md for Phase 3 (ref #127)

README rewritten for the Phase 3 architecture (Pillow+/dev/fb0 kiosk, no
Chromium/Qt/X/nginx). CLAUDE.md added with file map, stack inventory, run +
debug recipes, and the round/ untouched constraint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 05:59:42 +02:00

82 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
# Source-Disclosed License — All rights reserved except as expressly granted.
# See LICENCE-CMSD-1.0.md for terms.
# SecuBox-Deb :: remote-ui/square — deploy.sh (SSH hot-update)
# Phase 3: pushes helper + kiosk Python packages, applies optional config
# overrides, restarts secubox-eye-square-helper + secubox-square-kiosk.
set -euo pipefail
readonly MODULE="square-deploy"
HOST=""
USER="secubox"
PORT=22
API_URL=""
API_PASS=""
SIMULATE=""
usage() {
cat <<EOF
Usage: $0 -h HOST [options]
-h HOST Pi 4B IP or hostname
-u USER SSH user (default: secubox)
-p PORT SSH port (default: 22)
--api-url URL Override transport.api_otg_base in eye-square.toml
--api-pass PASS Override transport.login_pass
--sim transport.simulate = true
--no-sim transport.simulate = false
EOF
exit 1
}
while [ $# -gt 0 ]; do
case $1 in
-h) HOST="$2"; shift 2 ;;
-u) USER="$2"; shift 2 ;;
-p) PORT="$2"; shift 2 ;;
--api-url) API_URL="$2"; shift 2 ;;
--api-pass) API_PASS="$2"; shift 2 ;;
--sim) SIMULATE="true"; shift ;;
--no-sim) SIMULATE="false"; shift ;;
*) usage ;;
esac
done
[ -z "$HOST" ] && usage
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$(dirname "$SCRIPT_DIR")/.." && pwd)"
echo "[$MODULE] Rsync helper + kiosk Python packages..."
rsync -avz --delete -e "ssh -p $PORT" \
"$REPO_ROOT/packages/secubox-eye-square/helper/eye_square_helper/" \
"${USER}@${HOST}:/tmp/eye_square_helper/"
rsync -avz --delete -e "ssh -p $PORT" \
"$REPO_ROOT/packages/secubox-eye-square/kiosk/secubox_eye_square_kiosk/" \
"${USER}@${HOST}:/tmp/secubox_eye_square_kiosk/"
ssh -p "$PORT" "${USER}@${HOST}" "bash -s" <<REMOTE_SCRIPT
set -e
sudo rm -rf /usr/lib/python3/dist-packages/eye_square_helper
sudo mv /tmp/eye_square_helper /usr/lib/python3/dist-packages/
sudo rm -rf /usr/lib/python3/dist-packages/secubox_eye_square_kiosk
sudo mv /tmp/secubox_eye_square_kiosk /usr/lib/python3/dist-packages/
TOML=/etc/secubox/eye-square.toml
[ -f "\$TOML" ] || { echo "ERROR: \$TOML missing"; exit 1; }
${API_URL:+sudo sed -i "s|^api_otg_base.*|api_otg_base = \\\"$API_URL\\\"|" \$TOML}
${API_PASS:+sudo sed -i "s|^login_pass.*|login_pass = \\\"$API_PASS\\\"|" \$TOML}
${SIMULATE:+sudo sed -i "s|^simulate.*|simulate = $SIMULATE|" \$TOML}
sudo systemctl restart secubox-eye-square-helper.service
sudo systemctl restart secubox-square-kiosk.service
systemctl is-active secubox-square-kiosk.service || true
echo "[remote] deploy complete"
REMOTE_SCRIPT
echo "[$MODULE] Done."