first commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Affiche les actions image photobooth-app en detail.
|
||||
Usage: echo JSON | python3 display_actions.py [--file backup.json]
|
||||
"""
|
||||
|
||||
import json, sys
|
||||
|
||||
def display_actions(actions):
|
||||
defaults_proc = {
|
||||
"remove_background": False,
|
||||
"fill_background_enable": False,
|
||||
"fill_background_color": "#ededed",
|
||||
"img_background_enable": False,
|
||||
"img_background_file": None,
|
||||
"image_filter": "original",
|
||||
"img_frame_enable": False,
|
||||
"img_frame_file": None,
|
||||
"texts_enable": False,
|
||||
}
|
||||
defaults_ui = {
|
||||
"show_button": False,
|
||||
"title": "",
|
||||
"icon": "photo_camera",
|
||||
"use_custom_color": False,
|
||||
"custom_color": "#196cb0",
|
||||
}
|
||||
|
||||
for i, a in enumerate(actions):
|
||||
name = a["name"]
|
||||
proc = a.get("processing", {})
|
||||
ui = a.get("trigger", {}).get("ui_trigger", {})
|
||||
|
||||
print(f" [{i}] {name}")
|
||||
|
||||
proc_diffs = []
|
||||
for key, default in defaults_proc.items():
|
||||
val = proc.get(key, default)
|
||||
if val != default and val is not None:
|
||||
if key.endswith("_file") and val:
|
||||
val = val.split("/")[-1]
|
||||
proc_diffs.append(f"{key}: {val}")
|
||||
|
||||
if proc_diffs:
|
||||
print(f" processing: {', '.join(proc_diffs)}")
|
||||
|
||||
show_button = ui.get("show_button", False)
|
||||
if show_button:
|
||||
ui_diffs = []
|
||||
for key, default in defaults_ui.items():
|
||||
val = ui.get(key, default)
|
||||
if val != default:
|
||||
ui_diffs.append(f"{key}: {val}")
|
||||
|
||||
if ui_diffs:
|
||||
print(f" ui_trigger: {', '.join(ui_diffs)}")
|
||||
|
||||
print()
|
||||
|
||||
print(f" Total: {len(actions)} action(s)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--file" in sys.argv:
|
||||
filepath = sys.argv[sys.argv.index("--file") + 1]
|
||||
with open(filepath) as f:
|
||||
data = json.load(f)
|
||||
display_actions(data["image"])
|
||||
else:
|
||||
data = json.load(sys.stdin)
|
||||
if "actions" in data:
|
||||
display_actions(data["actions"]["image"])
|
||||
elif "image" in data:
|
||||
display_actions(data["image"])
|
||||
@@ -0,0 +1,242 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# health-check.sh - Surveillance de sante du photomaton
|
||||
# =============================================================================
|
||||
# Usage: bash health-check.sh
|
||||
# Appele regulierement par le timer systemd photobooth-watchdog.timer
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
LOG_TAG="photomaton-watchdog"
|
||||
PHOTOBOOTH_URL="http://localhost:8083"
|
||||
MAX_RESTART_ATTEMPTS=3
|
||||
RESTART_COUNT_FILE="/tmp/photomaton-restart-count"
|
||||
|
||||
log_info() { logger -t "$LOG_TAG" "[INFO] $1"; echo "[INFO] $1"; }
|
||||
log_warn() { logger -t "$LOG_TAG" "[WARN] $1"; echo "[WARN] $1"; }
|
||||
log_error() { logger -t "$LOG_TAG" "[ERROR] $1"; echo "[ERROR] $1"; }
|
||||
|
||||
# Initialiser le compteur de redemarrage
|
||||
if [ ! -f "$RESTART_COUNT_FILE" ]; then
|
||||
echo "0" > "$RESTART_COUNT_FILE"
|
||||
fi
|
||||
|
||||
restart_service() {
|
||||
local service="$1"
|
||||
local count
|
||||
count=$(cat "$RESTART_COUNT_FILE" 2>/dev/null || echo 0)
|
||||
|
||||
if [ "$count" -ge "$MAX_RESTART_ATTEMPTS" ]; then
|
||||
log_error "Service $service: trop de redemarrages ($count). Intervention manuelle requise."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_warn "Redemarrage de $service (tentative $((count + 1))/$MAX_RESTART_ATTEMPTS)..."
|
||||
echo "$((count + 1))" > "$RESTART_COUNT_FILE"
|
||||
|
||||
systemctl --user restart "$service" 2>/dev/null || \
|
||||
systemctl restart "$service" 2>/dev/null || true
|
||||
|
||||
sleep 5
|
||||
}
|
||||
|
||||
# Reset le compteur si l'app tourne bien depuis plus de 10 minutes
|
||||
reset_restart_counter() {
|
||||
local uptime_check
|
||||
uptime_check=$(systemctl --user show photobooth-app --property=ActiveEnterTimestamp 2>/dev/null | cut -d= -f2 || true)
|
||||
if [ -n "$uptime_check" ]; then
|
||||
local now
|
||||
now=$(date +%s)
|
||||
local started
|
||||
started=$(date -d "$uptime_check" +%s 2>/dev/null || echo "$now")
|
||||
local diff=$((now - started))
|
||||
if [ "$diff" -gt 600 ]; then
|
||||
echo "0" > "$RESTART_COUNT_FILE"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. VERIFIER LA MEMOIRE
|
||||
# -------------------------------------------------------------------
|
||||
check_memory() {
|
||||
local mem_available
|
||||
mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
|
||||
local mem_total
|
||||
mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
|
||||
|
||||
# Alerte si moins de 100 Mo disponibles
|
||||
if [ "$mem_available" -lt 102400 ]; then
|
||||
log_warn "Memoire faible: ${mem_available}kB disponible sur ${mem_total}kB total"
|
||||
|
||||
# Liberer les caches
|
||||
sync
|
||||
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
|
||||
log_info "Caches memoire vides"
|
||||
|
||||
# Re-verifier
|
||||
mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
|
||||
if [ "$mem_available" -lt 51200 ]; then
|
||||
log_error "Memoire critique: ${mem_available}kB - intervention necessaire"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
log_info "Memoire OK: ${mem_available}kB disponible"
|
||||
return 0
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. VERIFIER LA TEMPERATURE CPU
|
||||
# -------------------------------------------------------------------
|
||||
check_temperature() {
|
||||
local temp_file="/sys/class/thermal/thermal_zone0/temp"
|
||||
if [ -f "$temp_file" ]; then
|
||||
local temp
|
||||
temp=$(cat "$temp_file")
|
||||
local temp_c=$((temp / 1000))
|
||||
|
||||
if [ "$temp_c" -ge 80 ]; then
|
||||
log_error "Temperature CPU critique: ${temp_c}C - throttling probable!"
|
||||
return 1
|
||||
elif [ "$temp_c" -ge 70 ]; then
|
||||
log_warn "Temperature CPU elevee: ${temp_c}C"
|
||||
else
|
||||
log_info "Temperature CPU OK: ${temp_c}C"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 3. VERIFIER PHOTOBOOTH-APP
|
||||
# -------------------------------------------------------------------
|
||||
check_photobooth() {
|
||||
# Verifier si le service tourne
|
||||
if ! systemctl --user is-active photobooth-app &>/dev/null; then
|
||||
log_error "photobooth-app n'est pas actif!"
|
||||
restart_service "photobooth-app"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verifier si l'interface web repond
|
||||
local http_code
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$PHOTOBOOTH_URL" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "$http_code" = "000" ] || [ "$http_code" = "502" ] || [ "$http_code" = "503" ]; then
|
||||
log_error "photobooth-app ne repond pas (HTTP $http_code)"
|
||||
restart_service "photobooth-app"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "photobooth-app OK (HTTP $http_code)"
|
||||
reset_restart_counter
|
||||
return 0
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 4. VERIFIER CUPS / IMPRIMANTES
|
||||
# -------------------------------------------------------------------
|
||||
check_printers() {
|
||||
if ! systemctl is-active cups &>/dev/null; then
|
||||
log_error "CUPS n'est pas actif!"
|
||||
systemctl restart cups 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verifier les imprimantes
|
||||
local disabled_printers
|
||||
disabled_printers=$(lpstat -p 2>/dev/null | grep -c "disabled" || echo "0")
|
||||
|
||||
if [ "$disabled_printers" -gt 0 ]; then
|
||||
log_warn "$disabled_printers imprimante(s) desactivee(s), reactivation..."
|
||||
# Reactiver les imprimantes
|
||||
for printer in $(lpstat -p 2>/dev/null | grep "disabled" | awk '{print $2}'); do
|
||||
cupsenable "$printer" 2>/dev/null || true
|
||||
log_info "Imprimante reactivee: $printer"
|
||||
done
|
||||
fi
|
||||
|
||||
# Nettoyer les jobs bloques (plus de 10 minutes)
|
||||
local stuck_jobs
|
||||
stuck_jobs=$(lpstat -o 2>/dev/null | wc -l || echo "0")
|
||||
if [ "$stuck_jobs" -gt 5 ]; then
|
||||
log_warn "$stuck_jobs jobs d'impression en attente, nettoyage..."
|
||||
cancel -a 2>/dev/null || true
|
||||
fi
|
||||
|
||||
log_info "Imprimantes OK"
|
||||
return 0
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 5. VERIFIER L'ESPACE DISQUE
|
||||
# -------------------------------------------------------------------
|
||||
check_disk() {
|
||||
local usage
|
||||
usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
|
||||
|
||||
if [ "$usage" -ge 90 ]; then
|
||||
log_error "Espace disque critique: ${usage}% utilise"
|
||||
|
||||
# Nettoyage d'urgence
|
||||
# Supprimer les anciens logs
|
||||
find /var/log -name "*.gz" -mtime +1 -delete 2>/dev/null || true
|
||||
find /var/log -name "*.[0-9]" -mtime +1 -delete 2>/dev/null || true
|
||||
# Vider le cache apt
|
||||
apt-get clean 2>/dev/null || true
|
||||
|
||||
log_info "Nettoyage d'urgence effectue"
|
||||
return 1
|
||||
elif [ "$usage" -ge 80 ]; then
|
||||
log_warn "Espace disque: ${usage}% utilise"
|
||||
else
|
||||
log_info "Espace disque OK: ${usage}% utilise"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 6. VERIFIER LA CAMERA
|
||||
# -------------------------------------------------------------------
|
||||
check_camera() {
|
||||
# Verifier que la camera est detectee
|
||||
if command -v libcamera-hello &>/dev/null; then
|
||||
# Test rapide sans afficher d'image
|
||||
if ! libcamera-hello --list-cameras 2>/dev/null | grep -q "Available cameras"; then
|
||||
log_warn "Camera non detectee par libcamera"
|
||||
return 1
|
||||
fi
|
||||
elif [ -e /dev/video0 ]; then
|
||||
log_info "Camera detectee: /dev/video0"
|
||||
else
|
||||
log_warn "Aucun peripherique camera detecte"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Camera OK"
|
||||
return 0
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# EXECUTION
|
||||
# -------------------------------------------------------------------
|
||||
echo "=== Health Check Photomaton - $(date) ==="
|
||||
|
||||
ERRORS=0
|
||||
|
||||
check_memory || ((ERRORS++))
|
||||
check_temperature || ((ERRORS++))
|
||||
check_photobooth || ((ERRORS++))
|
||||
check_printers || ((ERRORS++))
|
||||
check_disk || ((ERRORS++))
|
||||
check_camera || ((ERRORS++))
|
||||
|
||||
echo ""
|
||||
if [ "$ERRORS" -eq 0 ]; then
|
||||
log_info "Tous les checks OK"
|
||||
else
|
||||
log_warn "$ERRORS probleme(s) detecte(s)"
|
||||
fi
|
||||
|
||||
exit "$ERRORS"
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# maintenance.sh - Maintenance automatique du photomaton
|
||||
# =============================================================================
|
||||
# Usage: bash maintenance.sh
|
||||
# Appele quotidiennement par photobooth-maintenance.timer
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
LOG_TAG="photomaton-maintenance"
|
||||
PHOTOBOOTH_DATA="${HOME}/photobooth-data"
|
||||
PHOTOS_DIR="${PHOTOBOOTH_DATA}/media"
|
||||
LOG_DIR="${PHOTOBOOTH_DATA}/log"
|
||||
|
||||
# Nombre de jours de retention des photos (0 = garder indefiniment)
|
||||
PHOTO_RETENTION_DAYS=${PHOTO_RETENTION_DAYS:-0}
|
||||
# Nombre de jours de retention des logs
|
||||
LOG_RETENTION_DAYS=7
|
||||
# Taille max du dossier photos en Mo (0 = pas de limite)
|
||||
MAX_PHOTOS_SIZE_MB=${MAX_PHOTOS_SIZE_MB:-0}
|
||||
|
||||
log_info() { logger -t "$LOG_TAG" "[INFO] $1"; echo "[INFO] $1"; }
|
||||
log_warn() { logger -t "$LOG_TAG" "[WARN] $1"; echo "[WARN] $1"; }
|
||||
|
||||
echo "=== Maintenance Photomaton - $(date) ==="
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. NETTOYAGE DES LOGS
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Nettoyage des logs..."
|
||||
|
||||
# Logs photobooth-app
|
||||
if [ -d "$LOG_DIR" ]; then
|
||||
deleted=$(find "$LOG_DIR" -name "*.log" -mtime +${LOG_RETENTION_DAYS} -delete -print 2>/dev/null | wc -l)
|
||||
log_info " $deleted ancien(s) log(s) photobooth supprime(s)"
|
||||
fi
|
||||
|
||||
# Logs systeme comprimes
|
||||
find /var/log -name "*.gz" -mtime +3 -delete 2>/dev/null || true
|
||||
find /var/log -name "*.[0-9]" -mtime +3 -delete 2>/dev/null || true
|
||||
|
||||
# Journald - garder seulement 50 Mo
|
||||
journalctl --vacuum-size=50M 2>/dev/null || true
|
||||
|
||||
log_info "Logs nettoyes"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. NETTOYAGE DES PHOTOS (si retention configuree)
|
||||
# -------------------------------------------------------------------
|
||||
if [ "$PHOTO_RETENTION_DAYS" -gt 0 ] && [ -d "$PHOTOS_DIR" ]; then
|
||||
log_info "Nettoyage des photos de plus de ${PHOTO_RETENTION_DAYS} jours..."
|
||||
deleted=$(find "$PHOTOS_DIR" -type f \( -name "*.jpg" -o -name "*.png" -o -name "*.gif" \) \
|
||||
-mtime +${PHOTO_RETENTION_DAYS} -delete -print 2>/dev/null | wc -l)
|
||||
log_info " $deleted photo(s) supprimee(s)"
|
||||
fi
|
||||
|
||||
# Limite de taille
|
||||
if [ "$MAX_PHOTOS_SIZE_MB" -gt 0 ] && [ -d "$PHOTOS_DIR" ]; then
|
||||
current_size=$(du -sm "$PHOTOS_DIR" 2>/dev/null | awk '{print $1}')
|
||||
if [ "${current_size:-0}" -gt "$MAX_PHOTOS_SIZE_MB" ]; then
|
||||
log_warn "Dossier photos: ${current_size}Mo > limite ${MAX_PHOTOS_SIZE_MB}Mo"
|
||||
log_warn "Suppression des photos les plus anciennes..."
|
||||
# Supprimer les plus vieux fichiers jusqu'a etre sous la limite
|
||||
while [ "$(du -sm "$PHOTOS_DIR" | awk '{print $1}')" -gt "$MAX_PHOTOS_SIZE_MB" ]; do
|
||||
oldest=$(find "$PHOTOS_DIR" -type f \( -name "*.jpg" -o -name "*.png" \) -printf '%T+ %p\n' 2>/dev/null | sort | head -1 | awk '{print $2}')
|
||||
if [ -n "$oldest" ]; then
|
||||
rm -f "$oldest"
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 3. NETTOYAGE CUPS
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Nettoyage des jobs d'impression..."
|
||||
|
||||
# Supprimer les jobs termines/en erreur
|
||||
cancel -a 2>/dev/null || true
|
||||
|
||||
# Reactiver les imprimantes si necessaire
|
||||
for printer in $(lpstat -p 2>/dev/null | grep "disabled" | awk '{print $2}'); do
|
||||
cupsenable "$printer" 2>/dev/null || true
|
||||
log_info " Imprimante reactivee: $printer"
|
||||
done
|
||||
|
||||
log_info "Jobs d'impression nettoyes"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 4. NETTOYAGE CACHE SYSTEME
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Nettoyage du cache systeme..."
|
||||
|
||||
# Cache apt
|
||||
apt-get clean 2>/dev/null || true
|
||||
apt-get autoclean 2>/dev/null || true
|
||||
|
||||
# Cache thumbnails
|
||||
rm -rf ~/.cache/thumbnails/* 2>/dev/null || true
|
||||
|
||||
# Fichiers temporaires
|
||||
find /tmp -type f -mtime +1 -delete 2>/dev/null || true
|
||||
|
||||
log_info "Cache systeme nettoye"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 5. VERIFICATION DE L'INTEGRITE
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Verification de l'integrite du systeme..."
|
||||
|
||||
# Verifier le filesystem (sans corriger, juste signaler)
|
||||
disk_errors=$(dmesg | grep -c "EXT4-fs error" 2>/dev/null || echo "0")
|
||||
if [ "$disk_errors" -gt 0 ]; then
|
||||
log_warn " $disk_errors erreur(s) filesystem detectee(s) dans dmesg!"
|
||||
fi
|
||||
|
||||
# Espace disque
|
||||
disk_usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
|
||||
log_info " Espace disque utilise: ${disk_usage}%"
|
||||
|
||||
# Uptime
|
||||
uptime_info=$(uptime -p)
|
||||
log_info " Uptime: $uptime_info"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 6. RAPPORT
|
||||
# -------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "=== Rapport de maintenance ==="
|
||||
echo " Date: $(date)"
|
||||
echo " Espace disque: ${disk_usage}%"
|
||||
if [ -d "$PHOTOS_DIR" ]; then
|
||||
photo_count=$(find "$PHOTOS_DIR" -type f \( -name "*.jpg" -o -name "*.png" -o -name "*.gif" \) 2>/dev/null | wc -l)
|
||||
photo_size=$(du -sh "$PHOTOS_DIR" 2>/dev/null | awk '{print $1}')
|
||||
echo " Photos: ${photo_count} fichiers (${photo_size})"
|
||||
fi
|
||||
echo " Uptime: $uptime_info"
|
||||
echo ""
|
||||
|
||||
log_info "Maintenance terminee"
|
||||
@@ -0,0 +1,252 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# optimize-desktop.sh - Desactiver les composants desktop inutiles en mode kiosk
|
||||
# =============================================================================
|
||||
# Usage: bash optimize-desktop.sh
|
||||
# Gain estime: ~200 Mo de RAM
|
||||
#
|
||||
# Ce script configure labwc pour ne lancer QUE Chromium en mode kiosk,
|
||||
# sans barre de taches, gestionnaire de fichiers, portails desktop, etc.
|
||||
#
|
||||
# REVERSIBLE: un fichier de backup est cree, et on peut restaurer avec:
|
||||
# bash optimize-desktop.sh --restore
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || whoami)}"
|
||||
USER_HOME=$(eval echo "~$REAL_USER")
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Mode restauration
|
||||
# -------------------------------------------------------------------
|
||||
if [ "${1:-}" = "--restore" ]; then
|
||||
log_info "Restauration du desktop original..."
|
||||
|
||||
if [ -f "$USER_HOME/.config/labwc/autostart.backup" ]; then
|
||||
cp "$USER_HOME/.config/labwc/autostart.backup" "$USER_HOME/.config/labwc/autostart"
|
||||
log_info "autostart restaure"
|
||||
fi
|
||||
|
||||
# Reactiver les services
|
||||
XDG_RUNTIME_DIR="/run/user/$(id -u "$REAL_USER")"
|
||||
DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"
|
||||
for service in xdg-desktop-portal xdg-desktop-portal-gtk xdg-desktop-portal-wlr; do
|
||||
runuser -u "$REAL_USER" -- env \
|
||||
XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
|
||||
DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
|
||||
systemctl --user unmask "$service" 2>/dev/null || true
|
||||
done
|
||||
|
||||
log_info "Restauration terminee. Redemarrer pour appliquer."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "============================================="
|
||||
echo " Optimisation desktop pour mode kiosk"
|
||||
echo " Gain estime: ~200 Mo de RAM"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. LABWC AUTOSTART - Ne lancer que Chromium
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration de labwc autostart..."
|
||||
|
||||
LABWC_DIR="$USER_HOME/.config/labwc"
|
||||
mkdir -p "$LABWC_DIR"
|
||||
|
||||
# Backup de l'autostart original
|
||||
if [ -f "$LABWC_DIR/autostart" ] && [ ! -f "$LABWC_DIR/autostart.backup" ]; then
|
||||
cp "$LABWC_DIR/autostart" "$LABWC_DIR/autostart.backup"
|
||||
log_info "Backup cree: $LABWC_DIR/autostart.backup"
|
||||
fi
|
||||
|
||||
# Creer un autostart minimal pour le mode kiosk
|
||||
cat > "$LABWC_DIR/autostart" << 'EOF'
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# labwc autostart - Mode kiosk photomaton
|
||||
# =============================================================================
|
||||
# Ne lance que le strict necessaire:
|
||||
# - pipewire (audio, requis par certains composants)
|
||||
# - Chromium en mode kiosk
|
||||
#
|
||||
# Composants DESACTIVES (gain ~200 Mo RAM):
|
||||
# - pcmanfm (gestionnaire fichiers desktop)
|
||||
# - wf-panel-pi (barre de taches)
|
||||
# - xdg-desktop-portal* (portails desktop)
|
||||
# - polkit-mate-authentication-agent
|
||||
# - gvfsd (systeme fichiers virtuels)
|
||||
# =============================================================================
|
||||
|
||||
# Desactiver le screen blanking / DPMS via wlr-randr si disponible
|
||||
if command -v wlr-randr &>/dev/null; then
|
||||
wlr-randr --output HDMI-A-1 --on 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Demarrer pipewire (necessaire pour certains composants systeme)
|
||||
/usr/bin/pipewire &
|
||||
sleep 1
|
||||
/usr/bin/pipewire -c filter-chain.conf &
|
||||
/usr/bin/wireplumber &
|
||||
/usr/bin/pipewire-pulse &
|
||||
|
||||
# Attendre que photobooth-app soit pret
|
||||
MAX_WAIT=60
|
||||
WAITED=0
|
||||
while ! curl -s -o /dev/null --max-time 2 http://127.0.0.1:8083/ 2>/dev/null; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
if [ "$WAITED" -ge "$MAX_WAIT" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Lancer Chromium en mode kiosk
|
||||
chromium \
|
||||
--kiosk \
|
||||
--accept-lang=fr-FR \
|
||||
--incognito \
|
||||
--password-store=basic \
|
||||
--ozone-platform=wayland \
|
||||
--enable-features=OverlayScrollbar \
|
||||
--disable-features=Translate,OverscrollHistoryNavigation \
|
||||
--noerrdialogs \
|
||||
--disable-infobars \
|
||||
--disable-session-crashed-bubble \
|
||||
--disable-component-update \
|
||||
--disable-background-networking \
|
||||
--disable-sync \
|
||||
--disable-extensions \
|
||||
--disable-dev-shm-usage \
|
||||
--no-first-run \
|
||||
--start-maximized \
|
||||
--process-per-site \
|
||||
--js-flags="--max-old-space-size=128" \
|
||||
--renderer-process-limit=1 \
|
||||
"http://127.0.0.1:8083/" &
|
||||
|
||||
EOF
|
||||
|
||||
chmod +x "$LABWC_DIR/autostart"
|
||||
chown "$REAL_USER:$REAL_USER" "$LABWC_DIR/autostart"
|
||||
log_info "labwc autostart kiosk cree"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. DESACTIVER LES PORTAILS DESKTOP (services utilisateur)
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Desactivation des portails desktop..."
|
||||
|
||||
# Ces services sont lances automatiquement par D-Bus
|
||||
# On les masque pour qu'ils ne demarrent plus
|
||||
# Utiliser machinectl ou runuser pour eviter les demandes de mot de passe
|
||||
# quand le script est lance avec sudo
|
||||
XDG_RUNTIME_DIR="/run/user/$(id -u "$REAL_USER")"
|
||||
DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"
|
||||
|
||||
for portal_svc in xdg-desktop-portal.service xdg-desktop-portal-gtk.service xdg-desktop-portal-wlr.service; do
|
||||
runuser -u "$REAL_USER" -- env \
|
||||
XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
|
||||
DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
|
||||
systemctl --user mask "$portal_svc" 2>/dev/null || true
|
||||
runuser -u "$REAL_USER" -- env \
|
||||
XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
|
||||
DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
|
||||
systemctl --user stop "$portal_svc" 2>/dev/null || true
|
||||
done
|
||||
|
||||
log_info "Portails desktop desactives"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 3. DESACTIVER LES SERVICES SYSTEME INUTILES
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Desactivation des services systeme inutiles..."
|
||||
|
||||
SERVICES_TO_DISABLE=(
|
||||
# ModemManager - pas de modem
|
||||
"ModemManager.service"
|
||||
# accounts-daemon - gestion comptes, inutile en kiosk
|
||||
"accounts-daemon.service"
|
||||
# colord - gestion couleurs, inutile
|
||||
"colord.service"
|
||||
# rpcbind - NFS/RPC, inutile
|
||||
"rpcbind.service"
|
||||
"rpcbind.socket"
|
||||
# udisks2 - gestion disques amovibles, inutile en kiosk
|
||||
# "udisks2.service" # Garder si on branche des cles USB
|
||||
# upower - gestion alimentation, inutile sur Pi fixe
|
||||
"upower.service"
|
||||
)
|
||||
|
||||
for service in "${SERVICES_TO_DISABLE[@]}"; do
|
||||
if systemctl is-enabled "$service" &>/dev/null 2>&1; then
|
||||
systemctl disable "$service" 2>/dev/null || true
|
||||
systemctl stop "$service" 2>/dev/null || true
|
||||
log_info " Desactive: $service"
|
||||
elif systemctl is-active "$service" &>/dev/null 2>&1; then
|
||||
systemctl stop "$service" 2>/dev/null || true
|
||||
log_info " Arrete: $service"
|
||||
fi
|
||||
done
|
||||
|
||||
# blkmapd - NFS block mapping, inutile
|
||||
systemctl disable blkmapd.service 2>/dev/null || true
|
||||
systemctl stop blkmapd.service 2>/dev/null || true
|
||||
|
||||
log_info "Services systeme inutiles desactives"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 4. DESACTIVER PHP-FPM SI RASPAP N'EN A PAS BESOIN IMMEDIAT
|
||||
# -------------------------------------------------------------------
|
||||
# Note: RaspAP utilise php-fpm via lighttpd
|
||||
# On le garde car RaspAP en a besoin pour l'interface admin
|
||||
# Mais on peut reduire les workers
|
||||
if [ -f /etc/php/8.4/fpm/pool.d/www.conf ]; then
|
||||
log_info "Optimisation php-fpm (reduction des workers)..."
|
||||
sed -i 's/^pm.max_children = .*/pm.max_children = 2/' /etc/php/8.4/fpm/pool.d/www.conf 2>/dev/null || true
|
||||
sed -i 's/^pm.start_servers = .*/pm.start_servers = 1/' /etc/php/8.4/fpm/pool.d/www.conf 2>/dev/null || true
|
||||
sed -i 's/^pm.min_spare_servers = .*/pm.min_spare_servers = 1/' /etc/php/8.4/fpm/pool.d/www.conf 2>/dev/null || true
|
||||
sed -i 's/^pm.max_spare_servers = .*/pm.max_spare_servers = 1/' /etc/php/8.4/fpm/pool.d/www.conf 2>/dev/null || true
|
||||
systemctl restart php8.4-fpm 2>/dev/null || true
|
||||
log_info "php-fpm optimise (max 2 workers)"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# RESUME
|
||||
# -------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "============================================="
|
||||
echo " Optimisation desktop terminee !"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
echo "Composants desactives:"
|
||||
echo " - pcmanfm (gestionnaire fichiers) ~38 Mo"
|
||||
echo " - wf-panel-pi (barre de taches) ~37 Mo"
|
||||
echo " - xdg-desktop-portal* (portails) ~77 Mo"
|
||||
echo " - polkit-mate-auth-agent ~13 Mo"
|
||||
echo " - gvfsd + volume monitors ~30 Mo"
|
||||
echo " - ModemManager ~6 Mo"
|
||||
echo " - accounts-daemon ~7 Mo"
|
||||
echo " - colord ~10 Mo"
|
||||
echo " - rpcbind + blkmapd ~4 Mo"
|
||||
echo " - upower ~9 Mo"
|
||||
echo " ----------------------------------------"
|
||||
echo " Total estime: ~231 Mo"
|
||||
echo ""
|
||||
echo -e "${YELLOW}IMPORTANT: Redemarrer pour appliquer les changements${NC}"
|
||||
echo " sudo reboot"
|
||||
echo ""
|
||||
echo "Pour restaurer le desktop original:"
|
||||
echo " bash $0 --restore"
|
||||
echo " sudo reboot"
|
||||
echo ""
|
||||
@@ -0,0 +1,374 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# optimize-system.sh - Optimisation Raspberry Pi 4 (2 Go RAM) pour photomaton
|
||||
# =============================================================================
|
||||
# Usage: sudo bash optimize-system.sh
|
||||
# A executer UNE SEULE FOIS lors du setup initial, puis reboot
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Ce script doit etre execute en root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "============================================="
|
||||
echo " Optimisation systeme pour Photomaton"
|
||||
echo " Raspberry Pi 4 - 2 Go RAM"
|
||||
echo " Raspberry Pi OS / Debian 13 Trixie"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 0. DETECTION DE L'ENVIRONNEMENT
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Detection de l'environnement..."
|
||||
|
||||
# Config.txt : priorite a /boot/firmware/config.txt (Trixie)
|
||||
CONFIG_FILE=""
|
||||
if [ -f /boot/firmware/config.txt ]; then
|
||||
CONFIG_FILE="/boot/firmware/config.txt"
|
||||
elif [ -f /boot/config.txt ]; then
|
||||
CONFIG_FILE="/boot/config.txt"
|
||||
fi
|
||||
log_info " config.txt: ${CONFIG_FILE:-'non trouve'}"
|
||||
|
||||
# Chromium : s'appelle 'chromium' sous Trixie (pas 'chromium-browser')
|
||||
CHROMIUM_BIN=""
|
||||
if command -v chromium &>/dev/null; then
|
||||
CHROMIUM_BIN="chromium"
|
||||
elif command -v chromium-browser &>/dev/null; then
|
||||
CHROMIUM_BIN="chromium-browser"
|
||||
fi
|
||||
log_info " Chromium: ${CHROMIUM_BIN:-'non trouve'}"
|
||||
|
||||
# OS
|
||||
OS_PRETTY=$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'"' -f2 || echo "inconnu")
|
||||
log_info " OS: $OS_PRETTY"
|
||||
echo ""
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. SWAP - Augmenter pour compenser les 2 Go de RAM
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration du swap..."
|
||||
|
||||
# Verifier si zram est deja actif
|
||||
if zramctl 2>/dev/null | grep -q "zram"; then
|
||||
log_info "zram deja actif, on le conserve"
|
||||
else
|
||||
# Installer zram si pas present (voir section 5)
|
||||
log_info "zram non detecte, sera installe plus bas"
|
||||
fi
|
||||
|
||||
# Ajouter un swap fichier sur disque en COMPLEMENT de zram
|
||||
# C'est un filet de securite: si zram est plein, le systeme utilise le disque
|
||||
# au lieu de faire un OOM kill (crash)
|
||||
if ! swapon --show | grep -q "/swapfile"; then
|
||||
if [ ! -f /swapfile ]; then
|
||||
log_info "Creation d'un swapfile de 1 Go (filet de securite en complement de zram)..."
|
||||
fallocate -l 1G /swapfile 2>/dev/null || dd if=/dev/zero of=/swapfile bs=1M count=1024
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
log_info "Swapfile 1 Go cree"
|
||||
fi
|
||||
swapon -p 10 /swapfile 2>/dev/null || true
|
||||
# Ajouter au fstab si pas deja present
|
||||
if ! grep -q '/swapfile' /etc/fstab; then
|
||||
echo '/swapfile none swap sw,pri=10 0 0' >> /etc/fstab
|
||||
fi
|
||||
log_info "Swapfile active avec priorite 10 (zram priorite 100 = utilise en premier)"
|
||||
else
|
||||
log_info "Swapfile deja actif"
|
||||
fi
|
||||
|
||||
# Reduire le swappiness (60 par defaut est trop eleve pour 2 Go)
|
||||
# 10 = le systeme utilise la RAM au maximum avant de swapper
|
||||
echo 'vm.swappiness=10' > /etc/sysctl.d/99-photomaton-swap.conf
|
||||
sysctl -p /etc/sysctl.d/99-photomaton-swap.conf 2>/dev/null || true
|
||||
log_info "Swappiness regle a 10 (etait probablement a 60)"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. GPU MEMORY - Allouer assez pour la camera sans gaspiller
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration memoire GPU..."
|
||||
|
||||
if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
|
||||
# 128 Mo pour la camera Module 3 (minimum recommande)
|
||||
# Ne pas mettre plus, chaque Mo compte avec 2 Go de RAM
|
||||
if grep -q '^gpu_mem=' "$CONFIG_FILE"; then
|
||||
sed -i 's/^gpu_mem=.*/gpu_mem=128/' "$CONFIG_FILE"
|
||||
else
|
||||
echo 'gpu_mem=128' >> "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
# S'assurer que la camera est activee (pour libcamera)
|
||||
# Sur Bookworm+, camera_auto_detect est par defaut
|
||||
if ! grep -q 'camera_auto_detect' "$CONFIG_FILE"; then
|
||||
echo 'camera_auto_detect=1' >> "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
log_info "GPU memory = 128 Mo, camera auto-detect active"
|
||||
else
|
||||
log_warn "Fichier config.txt non trouve, configuration GPU manuelle requise"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 3. DESACTIVER LES SERVICES INUTILES pour liberer la RAM
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Desactivation des services inutiles..."
|
||||
|
||||
# Liste des services a desactiver (adapter selon tes besoins)
|
||||
SERVICES_TO_DISABLE=(
|
||||
# Bluetooth (inutile pour le photomaton)
|
||||
"bluetooth.service"
|
||||
"hciuart.service"
|
||||
# ModemManager (pas de modem)
|
||||
"ModemManager.service"
|
||||
# Triggerhappy (raccourcis clavier inutiles)
|
||||
"triggerhappy.service"
|
||||
# Avahi (decouverte reseau, peut interrerer)
|
||||
# "avahi-daemon.service" # Garder si CUPS en a besoin
|
||||
# Apt services automatiques (consomment RAM/CPU en arriere-plan)
|
||||
"apt-daily.timer"
|
||||
"apt-daily-upgrade.timer"
|
||||
"apt-daily.service"
|
||||
"apt-daily-upgrade.service"
|
||||
# Man-db auto update
|
||||
"man-db.timer"
|
||||
# Packagekit (si installe)
|
||||
"packagekit.service"
|
||||
# RPC bind (NFS/RPC, totalement inutile pour un photomaton)
|
||||
"rpcbind.service"
|
||||
"rpcbind.socket"
|
||||
)
|
||||
|
||||
for service in "${SERVICES_TO_DISABLE[@]}"; do
|
||||
if systemctl is-enabled "$service" &>/dev/null; then
|
||||
systemctl disable "$service" 2>/dev/null || true
|
||||
systemctl stop "$service" 2>/dev/null || true
|
||||
log_info " Desactive: $service"
|
||||
fi
|
||||
done
|
||||
|
||||
# Desactiver le Bluetooth au niveau kernel aussi
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
if ! grep -q 'dtoverlay=disable-bt' "$CONFIG_FILE"; then
|
||||
echo 'dtoverlay=disable-bt' >> "$CONFIG_FILE"
|
||||
log_info " Bluetooth desactive au niveau kernel"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 4. OPTIMISATION MEMOIRE - Parametres kernel
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Optimisation parametres memoire kernel..."
|
||||
|
||||
cat > /etc/sysctl.d/99-photomaton-memory.conf << 'EOF'
|
||||
# Reduire la pression memoire cache
|
||||
vm.vfs_cache_pressure=200
|
||||
# Ecriture sur disque plus frequente (eviter accumulation en RAM)
|
||||
vm.dirty_ratio=10
|
||||
vm.dirty_background_ratio=5
|
||||
# Limiter la reserve memoire minimale
|
||||
vm.min_free_kbytes=16384
|
||||
# OOM killer plus agressif sur les gros processus
|
||||
vm.oom_kill_allocating_task=1
|
||||
EOF
|
||||
|
||||
sysctl -p /etc/sysctl.d/99-photomaton-memory.conf 2>/dev/null || true
|
||||
log_info "Parametres memoire appliques"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 5. ZRAM - Compression memoire (doublement effectif de la RAM)
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Verification de zram..."
|
||||
|
||||
if zramctl 2>/dev/null | grep -q "zram"; then
|
||||
log_info "zram deja actif et fonctionnel ($(zramctl --output NAME,ALGORITHM,DISKSIZE --noheadings))"
|
||||
log_info "Aucune modification necessaire"
|
||||
else
|
||||
log_info "zram non detecte, installation..."
|
||||
if ! dpkg -l | grep -q zram-tools; then
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq zram-tools
|
||||
fi
|
||||
|
||||
cat > /etc/default/zramswap << 'EOF'
|
||||
# Utiliser 50% de la RAM pour zram (compresse ~2x, donc ~2Go supplementaires)
|
||||
ALGO=zstd
|
||||
PERCENT=50
|
||||
PRIORITY=100
|
||||
EOF
|
||||
fi
|
||||
|
||||
systemctl enable zramswap 2>/dev/null || true
|
||||
systemctl restart zramswap 2>/dev/null || true
|
||||
log_info "zram configure (compression memoire active)"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 6. TMPFS - Reduire les ecritures disque et accelerer
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration tmpfs..."
|
||||
|
||||
# Monter /tmp en RAM (accelere les fichiers temporaires)
|
||||
if ! grep -q 'tmpfs.*/tmp' /etc/fstab; then
|
||||
echo 'tmpfs /tmp tmpfs defaults,noatime,nosuid,nodev,size=256M 0 0' >> /etc/fstab
|
||||
log_info " /tmp monte en tmpfs (256 Mo)"
|
||||
fi
|
||||
|
||||
# Logs en RAM pour reduire les ecritures SD (optionnel mais recommande)
|
||||
if ! grep -q 'tmpfs.*/var/log' /etc/fstab; then
|
||||
echo 'tmpfs /var/log tmpfs defaults,noatime,nosuid,nodev,size=64M 0 0' >> /etc/fstab
|
||||
log_info " /var/log monte en tmpfs (64 Mo) - ATTENTION: logs perdus au reboot"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 7. CHROMIUM / NAVIGATEUR - Optimisation kiosk mode
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Creation du script de lancement Chromium kiosk..."
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KIOSK_SCRIPT="${SCRIPT_DIR}/../config/chromium-kiosk.sh"
|
||||
|
||||
cat > "$KIOSK_SCRIPT" << 'KIOSK_EOF'
|
||||
#!/bin/bash
|
||||
# Lancement de Chromium en mode kiosk pour photomaton
|
||||
# Raspberry Pi OS Trixie (Wayland)
|
||||
# A utiliser dans un fichier .desktop dans ~/Desktop/
|
||||
|
||||
# Attendre que photobooth-app soit pret
|
||||
MAX_WAIT=60
|
||||
WAITED=0
|
||||
while ! curl -s -o /dev/null --max-time 2 http://127.0.0.1:8083/ 2>/dev/null; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
if [ "$WAITED" -ge "$MAX_WAIT" ]; then
|
||||
echo "photobooth-app non pret apres ${MAX_WAIT}s, lancement quand meme"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Detecter le binaire Chromium (chromium sous Trixie, chromium-browser avant)
|
||||
CHROMIUM=$(command -v chromium || command -v chromium-browser || echo "chromium")
|
||||
|
||||
# Lancer Chromium en mode kiosk
|
||||
# - Wayland natif (ozone-platform=wayland)
|
||||
# - Optimisations memoire pour Pi 4 2Go
|
||||
"$CHROMIUM" \
|
||||
--kiosk \
|
||||
--accept-lang=fr-FR \
|
||||
--incognito \
|
||||
--password-store=basic \
|
||||
--ozone-platform=wayland \
|
||||
--enable-features=OverlayScrollbar \
|
||||
--disable-features=Translate,OverscrollHistoryNavigation \
|
||||
--noerrdialogs \
|
||||
--disable-infobars \
|
||||
--disable-session-crashed-bubble \
|
||||
--disable-component-update \
|
||||
--disable-background-networking \
|
||||
--disable-sync \
|
||||
--disable-extensions \
|
||||
--disable-dev-shm-usage \
|
||||
--no-first-run \
|
||||
--start-maximized \
|
||||
--process-per-site \
|
||||
--js-flags="--max-old-space-size=128" \
|
||||
--renderer-process-limit=1 \
|
||||
"http://127.0.0.1:8083/"
|
||||
KIOSK_EOF
|
||||
|
||||
chmod +x "$KIOSK_SCRIPT"
|
||||
log_info "Script kiosk Chromium cree"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 8. OVERCLOCK LEGER (optionnel, stable)
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration overclock leger..."
|
||||
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
# Overclock leger et stable pour le Pi 4
|
||||
# over_voltage=2 est tres conservateur
|
||||
if ! grep -q '^arm_freq=' "$CONFIG_FILE"; then
|
||||
cat >> "$CONFIG_FILE" << 'EOF'
|
||||
|
||||
# Overclock leger pour photomaton (stable)
|
||||
# Decommenter pour activer:
|
||||
#arm_freq=1800
|
||||
#over_voltage=2
|
||||
EOF
|
||||
log_info "Overclock leger prepare (desactive par defaut, decommenter si souhaite)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 9. CGROUPS - Limiter la memoire de Node-RED si encore utilise
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration des limites memoire..."
|
||||
|
||||
# Si Node-RED est installe, creer un override pour limiter sa RAM
|
||||
if systemctl is-enabled nodered.service &>/dev/null 2>&1; then
|
||||
mkdir -p /etc/systemd/system/nodered.service.d/
|
||||
cat > /etc/systemd/system/nodered.service.d/memory-limit.conf << 'EOF'
|
||||
[Service]
|
||||
MemoryMax=256M
|
||||
MemorySwapMax=128M
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
log_info "Node-RED limite a 256 Mo RAM max"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 10. RESEAU - Optimiser pour le WiFi hotspot
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Optimisation reseau..."
|
||||
|
||||
cat > /etc/sysctl.d/99-photomaton-network.conf << 'EOF'
|
||||
# Optimiser pour servir les photos via WiFi
|
||||
net.core.somaxconn=256
|
||||
net.ipv4.tcp_fastopen=3
|
||||
net.core.netdev_max_backlog=1000
|
||||
EOF
|
||||
|
||||
sysctl -p /etc/sysctl.d/99-photomaton-network.conf 2>/dev/null || true
|
||||
log_info "Parametres reseau optimises"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# RESUME
|
||||
# -------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "============================================="
|
||||
echo " Optimisation terminee !"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
echo "Environnement: $OS_PRETTY"
|
||||
echo "config.txt: ${CONFIG_FILE:-'non trouve'}"
|
||||
echo ""
|
||||
echo "Modifications appliquees:"
|
||||
echo " - Swapfile 1 Go ajoute (filet de securite en complement de zram)"
|
||||
echo " - Swappiness reduit a 10"
|
||||
echo " - GPU memory = 128 Mo (si config.txt present)"
|
||||
echo " - Services inutiles desactives"
|
||||
echo " - Bluetooth desactive"
|
||||
echo " - zram conserve/installe (compression memoire)"
|
||||
echo " - tmpfs pour /tmp et /var/log"
|
||||
echo " - Parametres memoire kernel optimises"
|
||||
echo " - Script kiosk Chromium cree (${CHROMIUM_BIN:-chromium})"
|
||||
echo " - Limites memoire Node-RED (si present)"
|
||||
echo ""
|
||||
echo -e "${YELLOW}IMPORTANT: Redemarrer le Raspberry Pi pour appliquer tous les changements${NC}"
|
||||
echo " sudo reboot"
|
||||
echo ""
|
||||
echo -e "${YELLOW}NOTE: /var/log est en tmpfs - les logs seront perdus au reboot.${NC}"
|
||||
echo " Si vous voulez conserver les logs, commentez la ligne tmpfs /var/log dans /etc/fstab"
|
||||
echo ""
|
||||
@@ -0,0 +1,761 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# photobooth-config.sh - Gestion rapide de la config photobooth-app
|
||||
# =============================================================================
|
||||
# Usage:
|
||||
# bash photobooth-config.sh quality high|low|status
|
||||
# bash photobooth-config.sh livestream on|off|status
|
||||
# bash photobooth-config.sh frontpage manuel|direct|noprint|status
|
||||
# bash photobooth-config.sh restart
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
API_URL="http://127.0.0.1:8083"
|
||||
API_USER="admin"
|
||||
API_PASS="PhotoBooth2026!"
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# --- Auth ---
|
||||
get_token() {
|
||||
curl -s -X POST "${API_URL}/api/admin/auth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=password&username=${API_USER}&password=${API_PASS}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])'
|
||||
}
|
||||
|
||||
get_config() {
|
||||
local token="$1"
|
||||
curl -s "${API_URL}/api/admin/config/app" \
|
||||
-H "Authorization: Bearer ${token}"
|
||||
}
|
||||
|
||||
set_config() {
|
||||
local token="$1"
|
||||
local config="$2"
|
||||
curl -s -X PATCH "${API_URL}/api/admin/config/app" \
|
||||
-H "Authorization: Bearer ${token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "${config}" > /dev/null
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# QUALITE
|
||||
# =============================================================================
|
||||
quality_status() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
local config
|
||||
config=$(get_config "$token")
|
||||
|
||||
local capture_w quality
|
||||
capture_w=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["backends"]["group_backends"][0]["backend_config"]["CAPTURE_CAM_RESOLUTION_WIDTH"])')
|
||||
quality=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["backends"]["group_backends"][0]["backend_config"]["original_still_quality"])')
|
||||
|
||||
if [ "$capture_w" -ge 4000 ]; then
|
||||
echo -e "Qualite: ${GREEN}HAUTE${NC} (${capture_w}px, quality ${quality})"
|
||||
else
|
||||
echo -e "Qualite: ${YELLOW}BASSE${NC} (${capture_w}px, quality ${quality})"
|
||||
fi
|
||||
}
|
||||
|
||||
quality_high() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
set_config "$token" '{
|
||||
"backends": {
|
||||
"group_backends": [{
|
||||
"enabled": true,
|
||||
"description": "pi camera module V3",
|
||||
"backend_config": {
|
||||
"orientation": "1: 0°",
|
||||
"backend_type": "Picamera2",
|
||||
"camera_num": 0,
|
||||
"CAPTURE_CAM_RESOLUTION_WIDTH": 4608,
|
||||
"CAPTURE_CAM_RESOLUTION_HEIGHT": 3072,
|
||||
"PREVIEW_CAM_RESOLUTION_WIDTH": 2304,
|
||||
"PREVIEW_CAM_RESOLUTION_HEIGHT": 1536,
|
||||
"LIVEVIEW_RESOLUTION_WIDTH": 750,
|
||||
"LIVEVIEW_RESOLUTION_HEIGHT": 500,
|
||||
"framerate_still_mode": 20,
|
||||
"framerate_video_mode": 25,
|
||||
"frame_skip_count": 4,
|
||||
"optimized_lowlight_short_exposure": false,
|
||||
"videostream_quality": "MEDIUM",
|
||||
"original_still_quality": 100
|
||||
}
|
||||
}]
|
||||
},
|
||||
"mediaprocessing": {
|
||||
"full_still_length": 4608,
|
||||
"preview_still_length": 2304,
|
||||
"thumbnail_still_length": 960
|
||||
}
|
||||
}'
|
||||
|
||||
echo -e "${GREEN}Qualite HAUTE activee${NC} (4608x3072, quality 100)"
|
||||
echo " -> systemctl --user restart photobooth-app"
|
||||
}
|
||||
|
||||
quality_low() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
set_config "$token" '{
|
||||
"backends": {
|
||||
"group_backends": [{
|
||||
"enabled": true,
|
||||
"description": "pi camera module V3",
|
||||
"backend_config": {
|
||||
"orientation": "1: 0°",
|
||||
"backend_type": "Picamera2",
|
||||
"camera_num": 0,
|
||||
"CAPTURE_CAM_RESOLUTION_WIDTH": 2304,
|
||||
"CAPTURE_CAM_RESOLUTION_HEIGHT": 1536,
|
||||
"PREVIEW_CAM_RESOLUTION_WIDTH": 1152,
|
||||
"PREVIEW_CAM_RESOLUTION_HEIGHT": 768,
|
||||
"LIVEVIEW_RESOLUTION_WIDTH": 750,
|
||||
"LIVEVIEW_RESOLUTION_HEIGHT": 500,
|
||||
"framerate_still_mode": 20,
|
||||
"framerate_video_mode": 25,
|
||||
"frame_skip_count": 4,
|
||||
"optimized_lowlight_short_exposure": false,
|
||||
"videostream_quality": "MEDIUM",
|
||||
"original_still_quality": 90
|
||||
}
|
||||
}]
|
||||
},
|
||||
"mediaprocessing": {
|
||||
"full_still_length": 2304,
|
||||
"preview_still_length": 1200,
|
||||
"thumbnail_still_length": 480
|
||||
}
|
||||
}'
|
||||
|
||||
echo -e "${YELLOW}Qualite BASSE activee${NC} (2304x1536, quality 90)"
|
||||
echo " -> systemctl --user restart photobooth-app"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# LIVESTREAM
|
||||
# =============================================================================
|
||||
livestream_status() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
local config
|
||||
config=$(get_config "$token")
|
||||
|
||||
local idle_stream
|
||||
idle_stream=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["uisettings"]["enable_livestream_when_idle"])')
|
||||
|
||||
if [ "$idle_stream" = "True" ]; then
|
||||
echo -e "Livestream idle: ${GREEN}ACTIVE${NC}"
|
||||
else
|
||||
echo -e "Livestream idle: ${RED}DESACTIVE${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
livestream_on() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
set_config "$token" '{
|
||||
"uisettings": {
|
||||
"enable_livestream_when_idle": true
|
||||
}
|
||||
}'
|
||||
|
||||
echo -e "${GREEN}Livestream idle ACTIVE${NC}"
|
||||
echo " Les visiteurs voient la camera en direct sur l'ecran d'accueil"
|
||||
}
|
||||
|
||||
livestream_off() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
set_config "$token" '{
|
||||
"uisettings": {
|
||||
"enable_livestream_when_idle": false
|
||||
}
|
||||
}'
|
||||
|
||||
echo -e "${RED}Livestream idle DESACTIVE${NC}"
|
||||
echo " L'ecran d'accueil affiche le slideshow"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# FRONTPAGE TEXT
|
||||
# =============================================================================
|
||||
frontpage_status() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
local config
|
||||
config=$(get_config "$token")
|
||||
|
||||
local text
|
||||
text=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);t=c["uisettings"]["FRONTPAGE_TEXT"];print("impression" if "Impression" in t else "noprint" if "Appui long" not in t else "unknown")')
|
||||
|
||||
echo -e "Frontpage: ${CYAN}${text}${NC}"
|
||||
}
|
||||
|
||||
# --- HTML templates ---
|
||||
FRONTPAGE_MANUEL='<div> <div class="fixed-top-right q-mt-lg q-mr-lg" style="pointer-events: none; max-width: 400px;"> <div class="q-pa-md" style="background: rgba(226,235,242,0.85); border-radius: 12px; backdrop-filter: blur(4px); border: 2px solid #89a9c2;"> <div class="text-h3 text-weight-bold" style="color: #335e7e;">Bienvenue !</div> <div style="font-size: 1.3rem; line-height: 1.6; color: #335e7e; margin-top: 10px;"> <div>Appui court = Photo</div> <div>Appui long (3s) = Demande d'\''impression</div> </div> </div> </div> <div class="fixed-bottom-left q-mb-sm q-ml-lg" style="pointer-events: none;"> <div class="q-pa-sm" style="background: rgba(226,235,242,0.75); border-radius: 8px; border: 1px solid #89a9c2; white-space: nowrap;"> <span style="font-size: 0.85rem; color: #335e7e;"> Photomaton propos\u00e9 par Les Sapins Du Web. En l'\''utilisant, vous acceptez que les photos puissent etre utilis\u00e9es par Les Sapins Du Web et ses partenaires. </span> </div> </div> <div class="fixed-bottom-right q-mb-lg q-mr-lg" style="pointer-events: none;"> <img src="/userdata/LSDW/logo/logo.jpeg" alt="Les Sapins Du Web" style="width:180px; height:180px; border-radius: 12px; box-shadow: 2px 2px 10px rgba(0,0,0,0.5);"> </div></div>'
|
||||
|
||||
FRONTPAGE_DIRECT='<div> <div class="fixed-top-right q-mt-lg q-mr-lg" style="pointer-events: none; max-width: 400px;"> <div class="q-pa-md" style="background: rgba(226,235,242,0.85); border-radius: 12px; backdrop-filter: blur(4px); border: 2px solid #89a9c2;"> <div class="text-h3 text-weight-bold" style="color: #335e7e;">Bienvenue !</div> <div style="font-size: 1.3rem; line-height: 1.6; color: #335e7e; margin-top: 10px;"> <div>Appui court = Photo</div> <div>Appui long (3s) = Impression</div> </div> </div> </div> <div class="fixed-bottom-left q-mb-sm q-ml-lg" style="pointer-events: none;"> <div class="q-pa-sm" style="background: rgba(226,235,242,0.75); border-radius: 8px; border: 1px solid #89a9c2; white-space: nowrap;"> <span style="font-size: 0.85rem; color: #335e7e;"> Photomaton propos\u00e9 par Les Sapins Du Web. En l'\''utilisant, vous acceptez que les photos puissent etre utilis\u00e9es par Les Sapins Du Web et ses partenaires. </span> </div> </div> <div class="fixed-bottom-right q-mb-lg q-mr-lg" style="pointer-events: none;"> <img src="/userdata/LSDW/logo/logo.jpeg" alt="Les Sapins Du Web" style="width:180px; height:180px; border-radius: 12px; box-shadow: 2px 2px 10px rgba(0,0,0,0.5);"> </div></div>'
|
||||
|
||||
FRONTPAGE_NOPRINT='<div> <div class="fixed-top-right q-mt-lg q-mr-lg" style="pointer-events: none; max-width: 400px;"> <div class="q-pa-md" style="background: rgba(226,235,242,0.85); border-radius: 12px; backdrop-filter: blur(4px); border: 2px solid #89a9c2;"> <div class="text-h3 text-weight-bold" style="color: #335e7e;">Bienvenue !</div> <div style="font-size: 1.3rem; line-height: 1.6; color: #335e7e; margin-top: 10px;"> <div>Appuyez sur le bouton pour prendre une photo</div> </div> </div> </div> <div class="fixed-bottom-left q-mb-sm q-ml-lg" style="pointer-events: none;"> <div class="q-pa-sm" style="background: rgba(226,235,242,0.75); border-radius: 8px; border: 1px solid #89a9c2; white-space: nowrap;"> <span style="font-size: 0.85rem; color: #335e7e;"> Photomaton propos\u00e9 par Les Sapins Du Web. En l'\''utilisant, vous acceptez que les photos puissent etre utilis\u00e9es par Les Sapins Du Web et ses partenaires. </span> </div> </div> <div class="fixed-bottom-right q-mb-lg q-mr-lg" style="pointer-events: none;"> <img src="/userdata/LSDW/logo/logo.jpeg" alt="Les Sapins Du Web" style="width:180px; height:180px; border-radius: 12px; box-shadow: 2px 2px 10px rgba(0,0,0,0.5);"> </div></div>'
|
||||
|
||||
frontpage_set() {
|
||||
local mode="$1"
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
local html=""
|
||||
case "$mode" in
|
||||
manuel)
|
||||
html="$FRONTPAGE_MANUEL"
|
||||
;;
|
||||
direct)
|
||||
html="$FRONTPAGE_DIRECT"
|
||||
;;
|
||||
noprint)
|
||||
html="$FRONTPAGE_NOPRINT"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Utiliser python3 pour construire le JSON proprement (gestion des quotes)
|
||||
python3 -c "
|
||||
import json, urllib.request
|
||||
data = json.dumps({'uisettings': {'FRONTPAGE_TEXT': '''${html}'''}}).encode()
|
||||
req = urllib.request.Request('${API_URL}/api/admin/config/app', data=data, method='PATCH')
|
||||
req.add_header('Authorization', 'Bearer ${token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
urllib.request.urlopen(req)
|
||||
"
|
||||
|
||||
case "$mode" in
|
||||
manuel)
|
||||
echo -e "${CYAN}Frontpage: MODE MANUEL${NC}"
|
||||
echo " Appui court = Photo / Appui long = Demande d'impression"
|
||||
;;
|
||||
direct)
|
||||
echo -e "${CYAN}Frontpage: MODE DIRECT${NC}"
|
||||
echo " Appui court = Photo / Appui long = Impression"
|
||||
;;
|
||||
noprint)
|
||||
echo -e "${CYAN}Frontpage: SANS IMPRESSION${NC}"
|
||||
echo " Appuyez sur le bouton pour prendre une photo"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# ACTIONS IMAGE
|
||||
# =============================================================================
|
||||
ACTIONS_DIR="${HOME}/photobooth-data/actions-backups"
|
||||
|
||||
actions_init() {
|
||||
mkdir -p "$ACTIONS_DIR"
|
||||
}
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DISPLAY_ACTIONS_PY="${SCRIPT_DIR}/display_actions.py"
|
||||
|
||||
actions_list() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
local config
|
||||
config=$(get_config "$token")
|
||||
|
||||
echo "=== Actions image actuelles ==="
|
||||
echo "$config" | python3 "$DISPLAY_ACTIONS_PY"
|
||||
}
|
||||
|
||||
actions_backup() {
|
||||
local name="$1"
|
||||
actions_init
|
||||
local token
|
||||
token=$(get_token)
|
||||
local config
|
||||
config=$(get_config "$token")
|
||||
|
||||
local filepath="${ACTIONS_DIR}/${name}.json"
|
||||
|
||||
echo "$config" | python3 -c "
|
||||
import sys, json
|
||||
c = json.load(sys.stdin)
|
||||
with open('${filepath}', 'w') as f:
|
||||
json.dump(c['actions'], f, indent=2, ensure_ascii=False)
|
||||
"
|
||||
|
||||
local count
|
||||
count=$(python3 -c "import json;print(len(json.load(open('${filepath}'))['image']))")
|
||||
echo -e "${GREEN}Backup sauvegarde: ${filepath}${NC}"
|
||||
echo " ${count} action(s) image"
|
||||
}
|
||||
|
||||
actions_restore() {
|
||||
local name="$1"
|
||||
actions_init
|
||||
local filepath="${ACTIONS_DIR}/${name}.json"
|
||||
|
||||
if [ ! -f "$filepath" ]; then
|
||||
echo -e "${RED}Backup introuvable: ${filepath}${NC}"
|
||||
echo "Backups disponibles:"
|
||||
actions_list_backups
|
||||
return 1
|
||||
fi
|
||||
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
python3 -c "
|
||||
import json, urllib.request
|
||||
with open('${filepath}') as f:
|
||||
actions = json.load(f)
|
||||
data = json.dumps({'actions': actions}).encode()
|
||||
req = urllib.request.Request('${API_URL}/api/admin/config/app', data=data, method='PATCH')
|
||||
req.add_header('Authorization', 'Bearer ${token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
urllib.request.urlopen(req)
|
||||
"
|
||||
|
||||
local count
|
||||
count=$(python3 -c "import json;print(len(json.load(open('${filepath}'))['image']))")
|
||||
echo -e "${GREEN}Backup restaure: ${name}${NC}"
|
||||
echo " ${count} action(s) image chargees"
|
||||
echo " -> systemctl --user restart photobooth-app"
|
||||
}
|
||||
|
||||
actions_list_backups() {
|
||||
actions_init
|
||||
echo "=== Backups disponibles ==="
|
||||
local found=false
|
||||
for f in "${ACTIONS_DIR}"/*.json; do
|
||||
if [ -f "$f" ]; then
|
||||
found=true
|
||||
local basename
|
||||
basename=$(basename "$f" .json)
|
||||
local count
|
||||
count=$(python3 -c "import json;print(len(json.load(open('${f}'))['image']))" 2>/dev/null || echo "?")
|
||||
local date
|
||||
date=$(stat -c '%y' "$f" 2>/dev/null | cut -d. -f1)
|
||||
echo " ${basename} (${count} actions, ${date})"
|
||||
fi
|
||||
done
|
||||
if ! $found; then
|
||||
echo " (aucun)"
|
||||
fi
|
||||
}
|
||||
|
||||
actions_show() {
|
||||
local name="$1"
|
||||
actions_init
|
||||
local filepath="${ACTIONS_DIR}/${name}.json"
|
||||
|
||||
if [ ! -f "$filepath" ]; then
|
||||
echo -e "${RED}Backup introuvable: ${filepath}${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "=== Backup: ${name} ==="
|
||||
python3 "$DISPLAY_ACTIONS_PY" --file "$filepath"
|
||||
}
|
||||
|
||||
actions_move() {
|
||||
local from="$1"
|
||||
local to="$2"
|
||||
local token
|
||||
token=$(get_token)
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp /tmp/photobooth-config-XXXXXX.json)
|
||||
get_config "$token" > "$tmpfile"
|
||||
|
||||
python3 -c "
|
||||
import json, urllib.request, sys
|
||||
with open('${tmpfile}') as f:
|
||||
config = json.load(f)
|
||||
actions = config['actions']['image']
|
||||
count = len(actions)
|
||||
f, t = int('${from}'), int('${to}')
|
||||
if f < 0 or f >= count or t < 0 or t >= count:
|
||||
print(f'Erreur: index invalide (0-{count-1})')
|
||||
sys.exit(1)
|
||||
item = actions.pop(f)
|
||||
actions.insert(t, item)
|
||||
config['actions']['image'] = actions
|
||||
data = json.dumps({'actions': config['actions']}).encode()
|
||||
req = urllib.request.Request('${API_URL}/api/admin/config/app', data=data, method='PATCH')
|
||||
req.add_header('Authorization', 'Bearer ${token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
urllib.request.urlopen(req)
|
||||
print(f'Action [{f}] deplacee vers [{t}]')
|
||||
for i, a in enumerate(actions):
|
||||
marker = ' <--' if i == t else ''
|
||||
print(f' [{i}] {a[\"name\"]}{marker}')
|
||||
"
|
||||
rm -f "$tmpfile"
|
||||
}
|
||||
|
||||
actions_enable() {
|
||||
local index="$1"
|
||||
local token
|
||||
token=$(get_token)
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp /tmp/photobooth-config-XXXXXX.json)
|
||||
get_config "$token" > "$tmpfile"
|
||||
|
||||
python3 -c "
|
||||
import json, urllib.request, sys
|
||||
with open('${tmpfile}') as f:
|
||||
config = json.load(f)
|
||||
actions = config['actions']['image']
|
||||
idx = int('${index}')
|
||||
if idx < 0 or idx >= len(actions):
|
||||
print(f'Erreur: index invalide (0-{len(actions)-1})')
|
||||
sys.exit(1)
|
||||
actions[idx]['trigger']['ui_trigger']['show_button'] = True
|
||||
data = json.dumps({'actions': config['actions']}).encode()
|
||||
req = urllib.request.Request('${API_URL}/api/admin/config/app', data=data, method='PATCH')
|
||||
req.add_header('Authorization', 'Bearer ${token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
urllib.request.urlopen(req)
|
||||
print(f'Action [{idx}] {actions[idx][\"name\"]} : bouton VISIBLE')
|
||||
"
|
||||
rm -f "$tmpfile"
|
||||
}
|
||||
|
||||
actions_disable() {
|
||||
local index="$1"
|
||||
local token
|
||||
token=$(get_token)
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp /tmp/photobooth-config-XXXXXX.json)
|
||||
get_config "$token" > "$tmpfile"
|
||||
|
||||
python3 -c "
|
||||
import json, urllib.request, sys
|
||||
with open('${tmpfile}') as f:
|
||||
config = json.load(f)
|
||||
actions = config['actions']['image']
|
||||
idx = int('${index}')
|
||||
if idx < 0 or idx >= len(actions):
|
||||
print(f'Erreur: index invalide (0-{len(actions)-1})')
|
||||
sys.exit(1)
|
||||
actions[idx]['trigger']['ui_trigger']['show_button'] = False
|
||||
data = json.dumps({'actions': config['actions']}).encode()
|
||||
req = urllib.request.Request('${API_URL}/api/admin/config/app', data=data, method='PATCH')
|
||||
req.add_header('Authorization', 'Bearer ${token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
urllib.request.urlopen(req)
|
||||
print(f'Action [{idx}] {actions[idx][\"name\"]} : bouton CACHE')
|
||||
"
|
||||
rm -f "$tmpfile"
|
||||
}
|
||||
|
||||
actions_duplicate() {
|
||||
local index="$1"
|
||||
local token
|
||||
token=$(get_token)
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp /tmp/photobooth-config-XXXXXX.json)
|
||||
get_config "$token" > "$tmpfile"
|
||||
|
||||
python3 -c "
|
||||
import json, urllib.request, copy, sys
|
||||
with open('${tmpfile}') as f:
|
||||
config = json.load(f)
|
||||
actions = config['actions']['image']
|
||||
idx = int('${index}')
|
||||
if idx < 0 or idx >= len(actions):
|
||||
print(f'Erreur: index invalide (0-{len(actions)-1})')
|
||||
sys.exit(1)
|
||||
new_action = copy.deepcopy(actions[idx])
|
||||
new_action['name'] = new_action['name'] + ' (copie)'
|
||||
new_action['trigger']['ui_trigger']['show_button'] = False
|
||||
actions.insert(idx + 1, new_action)
|
||||
data = json.dumps({'actions': config['actions']}).encode()
|
||||
req = urllib.request.Request('${API_URL}/api/admin/config/app', data=data, method='PATCH')
|
||||
req.add_header('Authorization', 'Bearer ${token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
urllib.request.urlopen(req)
|
||||
print(f'Action [{idx}] dupliquee vers [{idx+1}]')
|
||||
for i, a in enumerate(actions):
|
||||
marker = ' <-- copie' if i == idx + 1 else ''
|
||||
print(f' [{i}] {a[\"name\"]}{marker}')
|
||||
"
|
||||
rm -f "$tmpfile"
|
||||
}
|
||||
|
||||
actions_remove() {
|
||||
local indices="$@"
|
||||
local token
|
||||
token=$(get_token)
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp /tmp/photobooth-config-XXXXXX.json)
|
||||
get_config "$token" > "$tmpfile"
|
||||
|
||||
python3 -c "
|
||||
import json, urllib.request, sys
|
||||
with open('${tmpfile}') as f:
|
||||
config = json.load(f)
|
||||
actions = config['actions']['image']
|
||||
indices = sorted([int(x) for x in '${indices}'.split()], reverse=True)
|
||||
|
||||
for idx in indices:
|
||||
if idx < 0 or idx >= len(actions):
|
||||
print(f'Erreur: index {idx} invalide (0-{len(actions)-1})')
|
||||
sys.exit(1)
|
||||
|
||||
removed = []
|
||||
for idx in indices:
|
||||
removed.append(f'[{idx}] {actions[idx][\"name\"]}')
|
||||
actions.pop(idx)
|
||||
|
||||
data = json.dumps({'actions': config['actions']}).encode()
|
||||
req = urllib.request.Request('${API_URL}/api/admin/config/app', data=data, method='PATCH')
|
||||
req.add_header('Authorization', 'Bearer ${token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
urllib.request.urlopen(req)
|
||||
|
||||
print('Actions supprimees:')
|
||||
for r in removed:
|
||||
print(f' - {r}')
|
||||
print()
|
||||
print('Actions restantes:')
|
||||
for i, a in enumerate(actions):
|
||||
print(f' [{i}] {a[\"name\"]}')
|
||||
print(f' Total: {len(actions)} action(s)')
|
||||
"
|
||||
rm -f "$tmpfile"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# RESTART
|
||||
# =============================================================================
|
||||
do_restart() {
|
||||
echo "Redemarrage de photobooth-app..."
|
||||
systemctl --user restart photobooth-app
|
||||
sleep 3
|
||||
if systemctl --user is-active photobooth-app &>/dev/null; then
|
||||
echo -e "${GREEN}photobooth-app redemarre avec succes${NC}"
|
||||
else
|
||||
echo -e "${RED}ERREUR: photobooth-app n'a pas redemarre${NC}"
|
||||
systemctl --user status photobooth-app
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# AIDE
|
||||
# =============================================================================
|
||||
show_help() {
|
||||
cat << 'HELP'
|
||||
===============================================================================
|
||||
photobooth-config.sh - Gestion rapide de la config photobooth-app
|
||||
===============================================================================
|
||||
|
||||
USAGE: photobooth-config.sh <commande> [arguments]
|
||||
|
||||
COMMANDES GENERALES:
|
||||
status, s Afficher la config actuelle (qualite, livestream, frontpage)
|
||||
restart, r Redemarrer photobooth-app
|
||||
help, -h, --help Afficher cette aide
|
||||
|
||||
QUALITE D'IMAGE:
|
||||
quality status Voir la qualite actuelle
|
||||
quality high Haute qualite (4608x3072, JPEG 100%)
|
||||
Meilleure qualite d'impression mais consomme plus de RAM
|
||||
quality low Basse qualite (2304x1536, JPEG 90%)
|
||||
Economise la RAM, suffisant pour impression Selphy
|
||||
Note: necessite un restart apres changement
|
||||
|
||||
LIVESTREAM:
|
||||
livestream status Voir si le livestream idle est actif
|
||||
livestream on Activer le livestream sur l'ecran d'accueil
|
||||
Les visiteurs voient la camera en direct
|
||||
livestream off Desactiver le livestream idle
|
||||
L'ecran d'accueil affiche le slideshow
|
||||
|
||||
TEXTE D'ACCUEIL (FRONTPAGE):
|
||||
frontpage status Voir le mode actuel
|
||||
frontpage manuel "Appui long (3s) = Demande d'impression"
|
||||
Pour les events avec validation admin
|
||||
frontpage direct "Appui long (3s) = Impression"
|
||||
Pour les events avec impression automatique
|
||||
frontpage noprint "Appuyez sur le bouton pour prendre une photo"
|
||||
Pas de mention d'impression
|
||||
|
||||
ACTIONS IMAGE:
|
||||
actions list Lister toutes les actions avec leurs parametres
|
||||
L'index [0] = simple clic, [1] = double clic, etc.
|
||||
actions backup <nom> Sauvegarder les actions actuelles
|
||||
Ex: actions backup hopnbloc
|
||||
actions restore <nom> Restaurer un backup d'actions
|
||||
Ex: actions restore hopnbloc
|
||||
Note: necessite un restart apres
|
||||
actions backups Lister les backups disponibles
|
||||
actions show <nom> Voir le contenu d'un backup
|
||||
actions move <de> <vers> Deplacer une action dans la liste
|
||||
Ex: actions move 3 0 (met l'action 3 en premier)
|
||||
Important: l'ordre determine le bouton physique
|
||||
[0] = simple clic
|
||||
[1] = double clic
|
||||
[2] = triple clic
|
||||
[3] = quadruple clic
|
||||
actions enable <index> Rendre le bouton visible sur l'ecran tactile
|
||||
actions disable <index> Cacher le bouton de l'ecran tactile
|
||||
actions dup <index> Dupliquer une action (copie inseree apres)
|
||||
actions remove <i> [i...] Supprimer une ou plusieurs actions
|
||||
Ex: actions remove 5 (supprime l'action 5)
|
||||
Ex: actions remove 3 5 7 (supprime les actions 3, 5 et 7)
|
||||
|
||||
EXEMPLES:
|
||||
|
||||
# Preparer un event Hop'N Bloc
|
||||
photobooth-config.sh actions backup config-avant
|
||||
photobooth-config.sh actions restore hopnbloc
|
||||
photobooth-config.sh frontpage manuel
|
||||
photobooth-config.sh livestream on
|
||||
photobooth-config.sh quality low
|
||||
photobooth-config.sh restart
|
||||
|
||||
# Passer en haute qualite pour un event special
|
||||
photobooth-config.sh quality high
|
||||
photobooth-config.sh restart
|
||||
|
||||
# Apres l'event, tout remettre
|
||||
photobooth-config.sh actions restore config-avant
|
||||
photobooth-config.sh restart
|
||||
|
||||
===============================================================================
|
||||
HELP
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN
|
||||
# =============================================================================
|
||||
CMD="${1:-help}"
|
||||
ARG="${2:-status}"
|
||||
|
||||
case "$CMD" in
|
||||
quality|q)
|
||||
case "$ARG" in
|
||||
high|haute|hq) quality_high ;;
|
||||
low|basse|lq) quality_low ;;
|
||||
status|info) quality_status ;;
|
||||
*) echo "Usage: $0 quality {high|low|status}" ;;
|
||||
esac
|
||||
;;
|
||||
livestream|ls)
|
||||
case "$ARG" in
|
||||
on|true|1) livestream_on ;;
|
||||
off|false|0) livestream_off ;;
|
||||
status|info) livestream_status ;;
|
||||
*) echo "Usage: $0 livestream {on|off|status}" ;;
|
||||
esac
|
||||
;;
|
||||
frontpage|fp)
|
||||
case "$ARG" in
|
||||
manuel|manual) frontpage_set manuel ;;
|
||||
direct) frontpage_set direct ;;
|
||||
noprint|sans|off) frontpage_set noprint ;;
|
||||
status|info) frontpage_status ;;
|
||||
*) echo "Usage: $0 frontpage {manuel|direct|noprint|status}" ;;
|
||||
esac
|
||||
;;
|
||||
actions|a)
|
||||
case "$ARG" in
|
||||
list|ls) actions_list ;;
|
||||
backup|save)
|
||||
if [ -z "${3:-}" ]; then
|
||||
echo "Usage: $0 actions backup <nom>"
|
||||
echo " Exemple: $0 actions backup hopnbloc"
|
||||
exit 1
|
||||
fi
|
||||
actions_backup "$3"
|
||||
;;
|
||||
restore|load)
|
||||
if [ -z "${3:-}" ]; then
|
||||
echo "Usage: $0 actions restore <nom>"
|
||||
actions_list_backups
|
||||
exit 1
|
||||
fi
|
||||
actions_restore "$3"
|
||||
;;
|
||||
backups) actions_list_backups ;;
|
||||
show)
|
||||
if [ -z "${3:-}" ]; then
|
||||
echo "Usage: $0 actions show <nom>"
|
||||
actions_list_backups
|
||||
exit 1
|
||||
fi
|
||||
actions_show "$3"
|
||||
;;
|
||||
move|mv)
|
||||
if [ -z "${3:-}" ] || [ -z "${4:-}" ]; then
|
||||
echo "Usage: $0 actions move <from> <to>"
|
||||
echo " Exemple: $0 actions move 3 0 (deplace l'action 3 en position 0)"
|
||||
exit 1
|
||||
fi
|
||||
actions_move "$3" "$4"
|
||||
;;
|
||||
enable|on)
|
||||
if [ -z "${3:-}" ]; then
|
||||
echo "Usage: $0 actions enable <index>"
|
||||
actions_list
|
||||
exit 1
|
||||
fi
|
||||
actions_enable "$3"
|
||||
;;
|
||||
disable|off)
|
||||
if [ -z "${3:-}" ]; then
|
||||
echo "Usage: $0 actions disable <index>"
|
||||
actions_list
|
||||
exit 1
|
||||
fi
|
||||
actions_disable "$3"
|
||||
;;
|
||||
dup|duplicate|copy)
|
||||
if [ -z "${3:-}" ]; then
|
||||
echo "Usage: $0 actions dup <index>"
|
||||
actions_list
|
||||
exit 1
|
||||
fi
|
||||
actions_duplicate "$3"
|
||||
;;
|
||||
remove|rm|del|delete)
|
||||
if [ -z "${3:-}" ]; then
|
||||
echo "Usage: $0 actions remove <index> [index...]"
|
||||
echo " Exemple: $0 actions remove 5"
|
||||
echo " Exemple: $0 actions remove 3 5 7"
|
||||
actions_list
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
actions_remove "$@"
|
||||
;;
|
||||
*) echo "Usage: $0 actions {list|backup|restore|backups|show|move|enable|disable|dup|remove}" ;;
|
||||
esac
|
||||
;;
|
||||
restart|r)
|
||||
do_restart
|
||||
;;
|
||||
status|s)
|
||||
echo "=== Photobooth Config ==="
|
||||
quality_status
|
||||
livestream_status
|
||||
frontpage_status
|
||||
;;
|
||||
help|-h|--help)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# ram-watchdog.sh - Watchdog memoire pour le photomaton
|
||||
# =============================================================================
|
||||
# Usage: Lancer via cron toutes les minutes :
|
||||
# * * * * * /home/pi/opencode/scripts/ram-watchdog.sh
|
||||
#
|
||||
# Actions par seuil :
|
||||
# - Swap > 70% : vider les caches
|
||||
# - Swap > 85% : redemarrer photobooth-app
|
||||
# - Swap > 95% : reboot du Pi
|
||||
# =============================================================================
|
||||
|
||||
LOG_TAG="ram-watchdog"
|
||||
log_info() { logger -t "$LOG_TAG" "[INFO] $1"; }
|
||||
log_warn() { logger -t "$LOG_TAG" "[WARN] $1"; }
|
||||
log_error() { logger -t "$LOG_TAG" "[ERROR] $1"; }
|
||||
|
||||
# --- Lire les valeurs memoire ---
|
||||
SWAP_TOTAL=$(awk '/SwapTotal/ {print $2}' /proc/meminfo)
|
||||
SWAP_FREE=$(awk '/SwapFree/ {print $2}' /proc/meminfo)
|
||||
MEM_AVAILABLE=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
|
||||
|
||||
# Eviter division par zero
|
||||
if [ "$SWAP_TOTAL" -eq 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SWAP_USED=$((SWAP_TOTAL - SWAP_FREE))
|
||||
SWAP_PERCENT=$((SWAP_USED * 100 / SWAP_TOTAL))
|
||||
MEM_AVAILABLE_MB=$((MEM_AVAILABLE / 1024))
|
||||
|
||||
# --- Seuil 1 : Swap > 70% -> vider les caches ---
|
||||
if [ "$SWAP_PERCENT" -ge 70 ] && [ "$SWAP_PERCENT" -lt 85 ]; then
|
||||
log_warn "Swap a ${SWAP_PERCENT}% (RAM dispo: ${MEM_AVAILABLE_MB}Mo) - Vidage des caches"
|
||||
sync
|
||||
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# --- Seuil 2 : Swap > 85% -> redemarrer photobooth-app ---
|
||||
if [ "$SWAP_PERCENT" -ge 85 ] && [ "$SWAP_PERCENT" -lt 95 ]; then
|
||||
log_error "Swap a ${SWAP_PERCENT}% (RAM dispo: ${MEM_AVAILABLE_MB}Mo) - Redemarrage photobooth-app"
|
||||
|
||||
# Redemarrer photobooth-app en tant que l'utilisateur pi
|
||||
REAL_USER="pi"
|
||||
XDG_RUNTIME_DIR="/run/user/$(id -u "$REAL_USER")"
|
||||
DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"
|
||||
|
||||
runuser -u "$REAL_USER" -- env \
|
||||
XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
|
||||
DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
|
||||
systemctl --user restart photobooth-app 2>/dev/null || true
|
||||
|
||||
log_warn "photobooth-app redemarre"
|
||||
|
||||
# Attendre et vider les caches
|
||||
sleep 5
|
||||
sync
|
||||
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# --- Seuil 3 : Swap > 95% -> reboot ---
|
||||
if [ "$SWAP_PERCENT" -ge 95 ]; then
|
||||
log_error "CRITIQUE: Swap a ${SWAP_PERCENT}% (RAM dispo: ${MEM_AVAILABLE_MB}Mo) - REBOOT"
|
||||
sync
|
||||
sleep 2
|
||||
reboot
|
||||
fi
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# script_print.sh - Impression photo pour photomaton
|
||||
# =============================================================================
|
||||
# Usage: script_print.sh "<filename>" "<media_type>" "<action_config_name>" "<copies>"
|
||||
# Appele par photobooth-app via l'action Share "Impression"
|
||||
#
|
||||
# Imprimantes: Canon Selphy CP1300 x2 via WiFi (blanche + noire)
|
||||
# Load balancing: utilise l'imprimante idle, sinon file d'attente sur la 1ere
|
||||
#
|
||||
# Sortie stdout parsable par Node-RED:
|
||||
# PRINTED:<printer_name>:<copies> en cas de succes
|
||||
# PRINT_ERROR:<printer_name> en cas d'erreur
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# --- Parametres ---
|
||||
filename="${1:-}"
|
||||
media_type="${2:-}"
|
||||
action_config_name="${3:-}"
|
||||
copies="${4:-1}"
|
||||
|
||||
# --- Noms des imprimantes CUPS (WiFi) ---
|
||||
PRINTER_1="Selphy_Blanche_WiFi"
|
||||
PRINTER_2="Selphy_Noire_WiFi"
|
||||
|
||||
# --- Options d'impression Selphy ---
|
||||
# Mode raw : envoie le JPEG directement a la Selphy
|
||||
# Postcard.Fullbleed : impression sans bordures
|
||||
PRINT_OPTIONS="-o media=Postcard.Fullbleed -o raw"
|
||||
|
||||
# --- Calibrage bordures pour impression Fullbleed ---
|
||||
# La Selphy deborde de quelques mm en mode Fullbleed
|
||||
# On ajoute des marges noires pour compenser et eviter de couper la photo
|
||||
# Valeurs calibrees pour Canon Selphy CP1300 via WiFi
|
||||
PRINT_EXTENT="2114x1418"
|
||||
PRINT_ROLL="-12+8"
|
||||
|
||||
# --- Logging ---
|
||||
LOG_TAG="photomaton-print"
|
||||
log_info() { logger -t "$LOG_TAG" "[INFO] $1"; echo "[INFO] $1"; }
|
||||
log_error() { logger -t "$LOG_TAG" "[ERROR] $1"; echo "[ERROR] $1"; }
|
||||
|
||||
# --- Validation ---
|
||||
if [ -z "$filename" ]; then
|
||||
log_error "Aucun fichier specifie"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$filename" ]; then
|
||||
log_error "Fichier introuvable: $filename"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$copies" -lt 1 ] || [ "$copies" -gt 3 ]; then
|
||||
log_error "Nombre de copies invalide: $copies (doit etre entre 1 et 3)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Preparation de l'image pour impression ---
|
||||
# Resize a 2000x1333 (ratio 3:2), ajout de marges noires pour compenser
|
||||
# le debordement Fullbleed, puis decalage pour centrer correctement
|
||||
filename_no_ext="${filename%.jpg}"
|
||||
filename_print="${filename_no_ext}_print.jpg"
|
||||
|
||||
if command -v convert &>/dev/null; then
|
||||
log_info "Preparation de l'image pour impression Fullbleed..."
|
||||
if convert "$filename" \
|
||||
-resize 2000x1333 \
|
||||
-background black \
|
||||
-gravity center \
|
||||
-extent ${PRINT_EXTENT} \
|
||||
-roll ${PRINT_ROLL} \
|
||||
-quality 95 \
|
||||
"$filename_print" 2>/dev/null; then
|
||||
FILE_TO_PRINT="$filename_print"
|
||||
log_info "Image preparee: $filename_print"
|
||||
else
|
||||
log_error "Echec de la preparation, impression de l'original"
|
||||
FILE_TO_PRINT="$filename"
|
||||
fi
|
||||
else
|
||||
log_error "ImageMagick (convert) non installe, impression de l'original"
|
||||
FILE_TO_PRINT="$filename"
|
||||
fi
|
||||
|
||||
# --- Selection de l'imprimante (load balancing) ---
|
||||
printer_1_status=$(lpstat -p "$PRINTER_1" 2>/dev/null || echo "not found")
|
||||
printer_2_status=$(lpstat -p "$PRINTER_2" 2>/dev/null || echo "not found")
|
||||
|
||||
SELECTED_PRINTER=""
|
||||
|
||||
if [[ "$printer_1_status" == *"idle"* ]]; then
|
||||
SELECTED_PRINTER="$PRINTER_1"
|
||||
log_info "Imprimante selectionnee: $PRINTER_1 (idle)"
|
||||
elif [[ "$printer_2_status" == *"idle"* ]]; then
|
||||
SELECTED_PRINTER="$PRINTER_2"
|
||||
log_info "Imprimante selectionnee: $PRINTER_2 (idle)"
|
||||
else
|
||||
# Aucune idle : choisir celle avec le moins de jobs en attente
|
||||
jobs_1=$(lpstat -o "$PRINTER_1" 2>/dev/null | wc -l)
|
||||
jobs_2=$(lpstat -o "$PRINTER_2" 2>/dev/null | wc -l)
|
||||
|
||||
if [ "$jobs_1" -le "$jobs_2" ] 2>/dev/null; then
|
||||
SELECTED_PRINTER="$PRINTER_1"
|
||||
else
|
||||
SELECTED_PRINTER="$PRINTER_2"
|
||||
fi
|
||||
log_info "Aucune imprimante idle, file d'attente sur: $SELECTED_PRINTER (jobs: P1=$jobs_1, P2=$jobs_2)"
|
||||
fi
|
||||
|
||||
# --- Impression ---
|
||||
log_info "Impression x${copies} sur $SELECTED_PRINTER: $FILE_TO_PRINT"
|
||||
|
||||
if lp -n "$copies" -d "$SELECTED_PRINTER" $PRINT_OPTIONS "$FILE_TO_PRINT"; then
|
||||
log_info "Job d'impression envoye avec succes"
|
||||
|
||||
# Sortie parsable par Node-RED pour les compteurs
|
||||
echo "PRINTED:${SELECTED_PRINTER}:${copies}"
|
||||
|
||||
# Nettoyage du fichier temporaire (apres que CUPS l'ait lu)
|
||||
if [ "$FILE_TO_PRINT" = "$filename_print" ] && [ -f "$filename_print" ]; then
|
||||
(sleep 30 && rm -f "$filename_print" 2>/dev/null) &
|
||||
fi
|
||||
|
||||
exit 0
|
||||
else
|
||||
log_error "Echec de l'envoi du job d'impression"
|
||||
echo "PRINT_ERROR:${SELECTED_PRINTER}"
|
||||
|
||||
# Nettoyage meme en cas d'erreur
|
||||
if [ -f "$filename_print" ]; then
|
||||
rm -f "$filename_print" 2>/dev/null
|
||||
fi
|
||||
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,276 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# setup-photobooth.sh - Installation et deploiement complet du photomaton
|
||||
# =============================================================================
|
||||
# Usage: bash setup-photobooth.sh
|
||||
# A executer sur un Raspberry Pi 4 fraichement installe
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
log_step() { echo -e "\n${CYAN}=== $1 ===${NC}\n"; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || whoami)}"
|
||||
USER_HOME=$(eval echo "~$REAL_USER")
|
||||
|
||||
echo "============================================="
|
||||
echo " Installation complete du Photomaton"
|
||||
echo " Raspberry Pi 4 - 2 Go RAM"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
echo " Utilisateur: $REAL_USER"
|
||||
echo " Home: $USER_HOME"
|
||||
echo " Projet: $PROJECT_DIR"
|
||||
echo ""
|
||||
|
||||
# Verifier qu'on est root pour certaines operations
|
||||
NEED_SUDO=false
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_warn "Certaines etapes necessitent sudo. Vous serez peut-etre invite a entrer le mot de passe."
|
||||
NEED_SUDO=true
|
||||
fi
|
||||
|
||||
# ===================================================================
|
||||
# ETAPE 1: Optimisation systeme
|
||||
# ===================================================================
|
||||
log_step "1/7 - Optimisation systeme"
|
||||
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
bash "$SCRIPT_DIR/optimize-system.sh"
|
||||
else
|
||||
log_warn "Lancez optimize-system.sh separement avec sudo:"
|
||||
echo " sudo bash $SCRIPT_DIR/optimize-system.sh"
|
||||
fi
|
||||
|
||||
# ===================================================================
|
||||
# ETAPE 2: Installation des dependances
|
||||
# ===================================================================
|
||||
log_step "2/7 - Installation des dependances"
|
||||
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
python3-picamera2 \
|
||||
libcamera-apps \
|
||||
libcap-dev \
|
||||
curl \
|
||||
unclutter \
|
||||
chromium \
|
||||
git \
|
||||
cups \
|
||||
printer-driver-gutenprint \
|
||||
cups-client
|
||||
|
||||
log_info "Dependances installees"
|
||||
|
||||
# ===================================================================
|
||||
# ETAPE 3: Installation de photobooth-app
|
||||
# ===================================================================
|
||||
log_step "3/7 - Installation de photobooth-app"
|
||||
|
||||
# Creer le dossier de donnees
|
||||
su - "$REAL_USER" -c "mkdir -p $USER_HOME/photobooth-data"
|
||||
|
||||
# Installer photobooth-app via pip (mode utilisateur)
|
||||
su - "$REAL_USER" -c "pip install --user --upgrade photobooth-app" || {
|
||||
log_warn "pip install --user a echoue, essai avec pipx..."
|
||||
su - "$REAL_USER" -c "pip install --user pipx && pipx install photobooth-app" || {
|
||||
log_warn "pipx a echoue, essai avec venv..."
|
||||
su - "$REAL_USER" -c "
|
||||
python3 -m venv $USER_HOME/photobooth-venv
|
||||
$USER_HOME/photobooth-venv/bin/pip install photobooth-app
|
||||
"
|
||||
log_info "photobooth-app installe dans un venv: $USER_HOME/photobooth-venv"
|
||||
log_warn "Adapter le chemin dans le service systemd (ExecStart)"
|
||||
}
|
||||
}
|
||||
|
||||
log_info "photobooth-app installe"
|
||||
|
||||
# ===================================================================
|
||||
# ETAPE 4: Configuration du service systemd
|
||||
# ===================================================================
|
||||
log_step "4/7 - Configuration des services systemd"
|
||||
|
||||
# Service photobooth-app (service utilisateur)
|
||||
su - "$REAL_USER" -c "mkdir -p $USER_HOME/.config/systemd/user/"
|
||||
|
||||
# Copier le service utilisateur
|
||||
cp "$PROJECT_DIR/systemd/photobooth-app.service" \
|
||||
"$USER_HOME/.config/systemd/user/photobooth-app.service"
|
||||
chown "$REAL_USER:$REAL_USER" "$USER_HOME/.config/systemd/user/photobooth-app.service"
|
||||
|
||||
# Activer le linger pour que les services utilisateur demarrent au boot
|
||||
loginctl enable-linger "$REAL_USER" 2>/dev/null || true
|
||||
|
||||
# Activer le service
|
||||
su - "$REAL_USER" -c "
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable photobooth-app
|
||||
"
|
||||
|
||||
log_info "Service photobooth-app configure"
|
||||
|
||||
# Services systeme (watchdog + maintenance)
|
||||
# Adapter les chemins dans les services
|
||||
SCRIPTS_PATH="$PROJECT_DIR/scripts"
|
||||
|
||||
sed "s|/home/pi/photomaton/scripts|$SCRIPTS_PATH|g" \
|
||||
"$PROJECT_DIR/systemd/photobooth-watchdog.service" \
|
||||
> /etc/systemd/system/photobooth-watchdog.service
|
||||
|
||||
cp "$PROJECT_DIR/systemd/photobooth-watchdog.timer" \
|
||||
/etc/systemd/system/photobooth-watchdog.timer
|
||||
|
||||
sed "s|/home/pi/photomaton/scripts|$SCRIPTS_PATH|g" \
|
||||
"$PROJECT_DIR/systemd/photobooth-maintenance.service" \
|
||||
> /etc/systemd/system/photobooth-maintenance.service
|
||||
|
||||
cp "$PROJECT_DIR/systemd/photobooth-maintenance.timer" \
|
||||
/etc/systemd/system/photobooth-maintenance.timer
|
||||
|
||||
# Rendre les scripts executables
|
||||
chmod +x "$SCRIPTS_PATH"/*.sh
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable photobooth-watchdog.timer
|
||||
systemctl enable photobooth-maintenance.timer
|
||||
|
||||
log_info "Watchdog et maintenance configures"
|
||||
|
||||
# ===================================================================
|
||||
# ETAPE 5: Configuration de l'imprimante
|
||||
# ===================================================================
|
||||
log_step "5/7 - Configuration imprimante"
|
||||
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
bash "$SCRIPT_DIR/setup-printer.sh"
|
||||
else
|
||||
log_warn "Lancez setup-printer.sh separement avec sudo:"
|
||||
echo " sudo bash $SCRIPT_DIR/setup-printer.sh"
|
||||
fi
|
||||
|
||||
# ===================================================================
|
||||
# ETAPE 6: Configuration de l'auto-login et kiosk
|
||||
# ===================================================================
|
||||
log_step "6/7 - Configuration auto-login et mode kiosk"
|
||||
|
||||
# Activer l'auto-login en mode graphique
|
||||
# (depand de la config actuelle, peut necessiter raspi-config)
|
||||
if command -v raspi-config &>/dev/null; then
|
||||
# Mode desktop auto-login
|
||||
raspi-config nonint do_boot_behaviour B4 2>/dev/null || true
|
||||
log_info "Auto-login graphique configure via raspi-config"
|
||||
fi
|
||||
|
||||
# Creer le fichier .desktop pour le kiosk Chromium
|
||||
# Sur RPi OS Trixie (Wayland), le fichier va dans ~/Desktop/
|
||||
DESKTOP_DIR="$USER_HOME/Desktop"
|
||||
su - "$REAL_USER" -c "mkdir -p $DESKTOP_DIR"
|
||||
|
||||
# Detecter chromium
|
||||
CHROMIUM_BIN=$(command -v chromium || command -v chromium-browser || echo "chromium")
|
||||
|
||||
cat > "$DESKTOP_DIR/photobooth-app.desktop" << EOF
|
||||
[Desktop Entry]
|
||||
X-GNOME-Autostart-enabled=true
|
||||
X-GNOME-Autostart-Delay=120
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Name=Photobooth-App
|
||||
Exec=bash -c "sleep 60 && $CHROMIUM_BIN --kiosk --accept-lang=fr-FR --incognito --password-store=basic --disable-features=Translate,OverscrollHistoryNavigation --noerrdialogs --disable-infobars --no-first-run --ozone-platform=wayland --enable-features=OverlayScrollbar --start-maximized --process-per-site --js-flags='--max-old-space-size=128' --renderer-process-limit=1 --disable-dev-shm-usage http://127.0.0.1:8083/"
|
||||
StartupNotify=false
|
||||
EOF
|
||||
|
||||
chown "$REAL_USER:$REAL_USER" "$DESKTOP_DIR/photobooth-app.desktop"
|
||||
|
||||
log_info "Mode kiosk configure (Wayland, ~/Desktop/photobooth-app.desktop)"
|
||||
|
||||
# ===================================================================
|
||||
# ETAPE 7: Configuration finale et verification
|
||||
# ===================================================================
|
||||
log_step "7/7 - Verification finale"
|
||||
|
||||
echo ""
|
||||
echo "=== Verification des composants ==="
|
||||
echo ""
|
||||
|
||||
# Verifier photobooth-app
|
||||
if su - "$REAL_USER" -c "which photobooth" &>/dev/null || \
|
||||
[ -f "$USER_HOME/photobooth-venv/bin/photobooth" ]; then
|
||||
echo -e " photobooth-app: ${GREEN}INSTALLE${NC}"
|
||||
else
|
||||
echo -e " photobooth-app: ${RED}NON TROUVE${NC}"
|
||||
fi
|
||||
|
||||
# Verifier CUPS
|
||||
if systemctl is-active cups &>/dev/null; then
|
||||
echo -e " CUPS: ${GREEN}ACTIF${NC}"
|
||||
else
|
||||
echo -e " CUPS: ${YELLOW}INACTIF${NC}"
|
||||
fi
|
||||
|
||||
# Verifier les imprimantes
|
||||
printers=$(lpstat -p 2>/dev/null | grep -c "printer" || echo "0")
|
||||
echo -e " Imprimantes: ${printers} detectee(s)"
|
||||
|
||||
# Verifier la camera
|
||||
if libcamera-hello --list-cameras 2>/dev/null | grep -q "Available"; then
|
||||
echo -e " Camera: ${GREEN}DETECTEE${NC}"
|
||||
elif [ -e /dev/video0 ]; then
|
||||
echo -e " Camera: ${GREEN}/dev/video0${NC}"
|
||||
else
|
||||
echo -e " Camera: ${YELLOW}NON DETECTEE (verifier apres reboot)${NC}"
|
||||
fi
|
||||
|
||||
# Verifier la memoire
|
||||
mem_total=$(awk '/MemTotal/ {printf "%.0f", $2/1024}' /proc/meminfo)
|
||||
mem_available=$(awk '/MemAvailable/ {printf "%.0f", $2/1024}' /proc/meminfo)
|
||||
echo -e " RAM: ${mem_available}Mo disponible / ${mem_total}Mo total"
|
||||
|
||||
# Verifier le swap
|
||||
swap_total=$(awk '/SwapTotal/ {printf "%.0f", $2/1024}' /proc/meminfo)
|
||||
echo -e " Swap: ${swap_total}Mo"
|
||||
|
||||
echo ""
|
||||
echo "============================================="
|
||||
echo " Installation terminee !"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
echo "Prochaines etapes:"
|
||||
echo ""
|
||||
echo " 1. REBOOTER le Raspberry Pi:"
|
||||
echo " sudo reboot"
|
||||
echo ""
|
||||
echo " 2. Apres reboot, acceder a l'admin photobooth:"
|
||||
echo " http://localhost:8083/admin"
|
||||
echo ""
|
||||
echo " 3. Configurer dans l'admin:"
|
||||
echo " - Camera: Backends -> picamera2"
|
||||
echo " - GPIO: hardwareinput -> activer GPIO"
|
||||
echo " - Photo trigger: GPIO 27 (ou votre GPIO relay)"
|
||||
echo " - Impression: Share & Print -> ajouter une action Print"
|
||||
echo " Commande: /home/pi/photobooth-data/script/script_print.sh \"{filename}\" \"{media_type}\" \"{action_config_name}\" \"{copies}\""
|
||||
echo " - QR Share: configurer le service QR"
|
||||
echo ""
|
||||
echo " 4. Si vous utilisez encore Node-RED pour le bouton:"
|
||||
echo " -> Envisagez de passer au GPIO natif de photobooth-app"
|
||||
echo " -> Gain: ~200 Mo de RAM en desactivant Node-RED"
|
||||
echo " -> sudo systemctl disable nodered.service"
|
||||
echo ""
|
||||
echo " 5. Verifier que tout fonctionne:"
|
||||
echo " bash $SCRIPT_DIR/health-check.sh"
|
||||
echo ""
|
||||
@@ -0,0 +1,321 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# setup-printer.sh - Configuration CUPS + Canon Selphy CP1300
|
||||
# =============================================================================
|
||||
# Usage: sudo bash setup-printer.sh
|
||||
# Prerequis: imprimante branchee en USB et allumee
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Ce script doit etre execute en root (sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "============================================="
|
||||
echo " Configuration imprimante Canon Selphy CP1300"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. INSTALLATION DES PAQUETS
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Installation des paquets necessaires..."
|
||||
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq \
|
||||
cups \
|
||||
printer-driver-gutenprint \
|
||||
cups-client \
|
||||
system-config-printer \
|
||||
2>/dev/null || apt-get install -y -qq cups printer-driver-gutenprint cups-client
|
||||
|
||||
# Ajouter l'utilisateur au groupe lpadmin pour administrer CUPS
|
||||
REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || echo pi)}"
|
||||
usermod -aG lpadmin "$REAL_USER" 2>/dev/null || true
|
||||
log_info "Utilisateur $REAL_USER ajoute au groupe lpadmin"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. CONFIGURATION CUPS
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration de CUPS..."
|
||||
|
||||
# Backup de la config originale
|
||||
cp /etc/cups/cupsd.conf /etc/cups/cupsd.conf.backup.$(date +%Y%m%d) 2>/dev/null || true
|
||||
|
||||
# Autoriser l'acces local au panneau CUPS
|
||||
# (utile pour debug mais pas necessaire en production)
|
||||
cat > /etc/cups/cupsd.conf << 'EOF'
|
||||
# Configuration CUPS optimisee pour photomaton
|
||||
LogLevel warn
|
||||
MaxLogSize 0
|
||||
PageLogFormat
|
||||
|
||||
# Ecouter sur localhost et le reseau local
|
||||
Listen localhost:631
|
||||
Listen /run/cups/cups.sock
|
||||
|
||||
# Partage d'imprimante desactive (pas besoin)
|
||||
Browsing Off
|
||||
BrowseLocalProtocols none
|
||||
DefaultAuthType Basic
|
||||
WebInterface Yes
|
||||
|
||||
# Politique par defaut
|
||||
<Location />
|
||||
Order allow,deny
|
||||
Allow localhost
|
||||
Allow 10.0.0.*
|
||||
Allow 192.168.*
|
||||
</Location>
|
||||
|
||||
<Location /admin>
|
||||
Order allow,deny
|
||||
Allow localhost
|
||||
</Location>
|
||||
|
||||
<Location /admin/conf>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order allow,deny
|
||||
Allow localhost
|
||||
</Location>
|
||||
|
||||
<Policy default>
|
||||
JobPrivateAccess default
|
||||
JobPrivateValues default
|
||||
SubscriptionPrivateAccess default
|
||||
SubscriptionPrivateValues default
|
||||
|
||||
<Limit Create-Job Print-Job Print-URI Validate-Job>
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default CUPS-Get-Devices>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit All>
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
</Policy>
|
||||
EOF
|
||||
|
||||
systemctl restart cups
|
||||
systemctl enable cups
|
||||
log_info "CUPS configure et redemarre"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 3. DETECTION DE L'IMPRIMANTE
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Recherche des imprimantes Canon Selphy..."
|
||||
|
||||
sleep 3 # Laisser CUPS detecter les USB
|
||||
|
||||
# Chercher les imprimantes USB Canon
|
||||
USB_PRINTERS=$(lpinfo -v 2>/dev/null | grep -i "canon\|selphy\|usb" || true)
|
||||
|
||||
if [ -z "$USB_PRINTERS" ]; then
|
||||
log_warn "Aucune imprimante Canon detectee via USB."
|
||||
log_warn "Verifiez que l'imprimante est branchee, allumee, et prete."
|
||||
echo ""
|
||||
echo "Imprimantes detectees:"
|
||||
lpinfo -v 2>/dev/null || true
|
||||
echo ""
|
||||
echo "Vous pouvez relancer ce script une fois l'imprimante connectee,"
|
||||
echo "ou ajouter manuellement via: http://localhost:631"
|
||||
echo ""
|
||||
else
|
||||
echo ""
|
||||
echo -e "${CYAN}Imprimantes detectees:${NC}"
|
||||
echo "$USB_PRINTERS"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Chercher le driver Gutenprint pour Canon Selphy
|
||||
log_info "Recherche du driver Canon Selphy CP1300..."
|
||||
DRIVER=$(lpinfo -m 2>/dev/null | grep -i "selphy\|cp1300\|CP1300" | head -1 || true)
|
||||
|
||||
if [ -z "$DRIVER" ]; then
|
||||
# Essayer avec un pattern plus large
|
||||
DRIVER=$(lpinfo -m 2>/dev/null | grep -i "canon.*cp" | head -1 || true)
|
||||
fi
|
||||
|
||||
if [ -n "$DRIVER" ]; then
|
||||
DRIVER_URI=$(echo "$DRIVER" | awk '{print $1}')
|
||||
log_info "Driver trouve: $DRIVER"
|
||||
else
|
||||
log_warn "Driver Selphy CP1300 non trouve dans Gutenprint."
|
||||
log_warn "Essayez: sudo apt install printer-driver-gutenprint && sudo systemctl restart cups"
|
||||
DRIVER_URI=""
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 4. AJOUT AUTOMATIQUE DE L'IMPRIMANTE (si detectee)
|
||||
# -------------------------------------------------------------------
|
||||
USB_URI=$(echo "$USB_PRINTERS" | grep "usb://" | head -1 | awk '{print $2}' || true)
|
||||
|
||||
if [ -n "$USB_URI" ] && [ -n "$DRIVER_URI" ]; then
|
||||
log_info "Ajout automatique de l'imprimante..."
|
||||
|
||||
# Supprimer les anciennes configs si elles existent
|
||||
lpadmin -x Canon_SELPHY_CP1300_usb_blanche 2>/dev/null || true
|
||||
lpadmin -x Canon_SELPHY_CP1300_usb_noire 2>/dev/null || true
|
||||
|
||||
# Ajouter la premiere imprimante (blanche)
|
||||
lpadmin -p Canon_SELPHY_CP1300_usb_blanche \
|
||||
-v "$USB_URI" \
|
||||
-m "$DRIVER_URI" \
|
||||
-L "Photomaton" \
|
||||
-D "Canon Selphy CP1300 Blanche" \
|
||||
-E
|
||||
|
||||
# Configurer comme imprimante par defaut
|
||||
lpadmin -d Canon_SELPHY_CP1300_usb_blanche
|
||||
|
||||
log_info "Imprimante 'Canon_SELPHY_CP1300_usb_blanche' ajoutee et definie par defaut"
|
||||
|
||||
# Detecter une deuxieme imprimante si presente
|
||||
USB_URI_2=$(echo "$USB_PRINTERS" | grep "usb://" | sed -n '2p' | awk '{print $2}' || true)
|
||||
if [ -n "$USB_URI_2" ]; then
|
||||
lpadmin -p Canon_SELPHY_CP1300_usb_noire \
|
||||
-v "$USB_URI_2" \
|
||||
-m "$DRIVER_URI" \
|
||||
-L "Photomaton" \
|
||||
-D "Canon Selphy CP1300 Noire" \
|
||||
-E
|
||||
log_info "Deuxieme imprimante 'Canon_SELPHY_CP1300_usb_noire' ajoutee"
|
||||
fi
|
||||
else
|
||||
log_warn "Ajout automatique impossible. Ajoutez manuellement via http://localhost:631"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 5. OPTIONS D'IMPRESSION OPTIMALES POUR SELPHY CP1300
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Configuration des options d'impression..."
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cat > "${SCRIPT_DIR}/../config/cups-selphy.conf" << 'EOF'
|
||||
# =============================================================================
|
||||
# Options d'impression pour Canon Selphy CP1300
|
||||
# =============================================================================
|
||||
# Format: lpoptions -p PRINTER_NAME -o OPTION=VALUE
|
||||
#
|
||||
# La Selphy CP1300 supporte:
|
||||
# - Format carte postale : 100x148mm (4x6 pouces)
|
||||
# - Format carte : 54x86mm
|
||||
# - Format carre : 89x89mm (si media disponible)
|
||||
#
|
||||
# Noms CUPS des imprimantes:
|
||||
# Canon_SELPHY_CP1300_usb_blanche
|
||||
# Canon_SELPHY_CP1300_usb_noire
|
||||
#
|
||||
# Commande d'impression pour photobooth-app:
|
||||
# lp -d Canon_SELPHY_CP1300_usb_blanche -o landscape -o fit-to-page {filename}
|
||||
#
|
||||
# Options utilisees:
|
||||
# - fit-to-page : Ajuster l'image a la page sans crop
|
||||
# - landscape : Orientation paysage
|
||||
# - -n X : Nombre de copies
|
||||
#
|
||||
# Pour le load balancing entre 2 imprimantes:
|
||||
# Utiliser script_print.sh (gere automatiquement)
|
||||
# =============================================================================
|
||||
EOF
|
||||
|
||||
# Appliquer les options par defaut si les imprimantes sont configurees
|
||||
if lpstat -p Canon_SELPHY_CP1300_usb_blanche &>/dev/null; then
|
||||
lpoptions -p Canon_SELPHY_CP1300_usb_blanche -o fit-to-page -o landscape 2>/dev/null || true
|
||||
log_info "Options par defaut appliquees a Canon_SELPHY_CP1300_usb_blanche"
|
||||
fi
|
||||
|
||||
if lpstat -p Canon_SELPHY_CP1300_usb_noire &>/dev/null; then
|
||||
lpoptions -p Canon_SELPHY_CP1300_usb_noire -o fit-to-page -o landscape 2>/dev/null || true
|
||||
log_info "Options par defaut appliquees a Canon_SELPHY_CP1300_usb_noire"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 6. COPIER LE SCRIPT D'IMPRESSION OPTIMISE
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Verification du script d'impression..."
|
||||
|
||||
if [ -f "${SCRIPT_DIR}/script_print.sh" ]; then
|
||||
log_info "script_print.sh present dans le projet"
|
||||
log_info "Pour deployer: cp ${SCRIPT_DIR}/script_print.sh /home/pi/photobooth-data/script/"
|
||||
else
|
||||
log_warn "script_print.sh non trouve dans ${SCRIPT_DIR}/"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 7. SCRIPT DE NETTOYAGE AUTOMATIQUE DES JOBS BLOQUES
|
||||
# -------------------------------------------------------------------
|
||||
log_info "Creation du script de nettoyage jobs CUPS..."
|
||||
|
||||
cat > "${SCRIPT_DIR}/cups-cleanup.sh" << 'CLEANUP_EOF'
|
||||
#!/bin/bash
|
||||
# Nettoyer les jobs d'impression bloques/en erreur
|
||||
# A appeler periodiquement via cron ou le watchdog
|
||||
|
||||
# Annuler tous les jobs en erreur
|
||||
cancel -a 2>/dev/null || true
|
||||
|
||||
# Reactiver les imprimantes si elles sont en pause/erreur
|
||||
for printer in $(lpstat -p 2>/dev/null | grep -E "disabled|stopped" | awk '{print $2}'); do
|
||||
cupsenable "$printer" 2>/dev/null || true
|
||||
cupsdisable --release "$printer" 2>/dev/null || true
|
||||
cupsenable "$printer" 2>/dev/null || true
|
||||
echo "Imprimante reactivee: $printer"
|
||||
done
|
||||
CLEANUP_EOF
|
||||
|
||||
chmod +x "${SCRIPT_DIR}/cups-cleanup.sh"
|
||||
log_info "Script cups-cleanup.sh cree"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# RESUME
|
||||
# -------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "============================================="
|
||||
echo " Configuration imprimante terminee !"
|
||||
echo "============================================="
|
||||
echo ""
|
||||
echo "Imprimantes configurees:"
|
||||
lpstat -p 2>/dev/null || echo " (aucune)"
|
||||
echo ""
|
||||
echo "Commande d'impression pour photobooth-app:"
|
||||
echo " script_print.sh \"{filename}\" \"{media_type}\" \"{action_config_name}\" \"{copies}\""
|
||||
echo ""
|
||||
echo "Imprimantes CUPS:"
|
||||
echo " Canon_SELPHY_CP1300_usb_blanche"
|
||||
echo " Canon_SELPHY_CP1300_usb_noire"
|
||||
echo ""
|
||||
echo "Administration CUPS: http://localhost:631"
|
||||
echo ""
|
||||
echo "Pour tester l'impression:"
|
||||
echo " lp -d Canon_SELPHY_CP1300_usb_blanche -o landscape -o fit-to-page /chemin/vers/une/photo.jpg"
|
||||
echo ""
|
||||
@@ -0,0 +1,187 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# switch-quality.sh - Basculer la qualite d'image de photobooth-app
|
||||
# =============================================================================
|
||||
# Usage:
|
||||
# bash switch-quality.sh high # Qualite haute (4608x3072, quality 100)
|
||||
# bash switch-quality.sh low # Qualite basse (2304x1536, quality 90)
|
||||
# bash switch-quality.sh status # Afficher la config actuelle
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
API_URL="http://127.0.0.1:8083"
|
||||
API_USER="admin"
|
||||
API_PASS="PhotoBooth2026!"
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# --- Obtenir le token ---
|
||||
get_token() {
|
||||
curl -s -X POST "${API_URL}/api/admin/auth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=password&username=${API_USER}&password=${API_PASS}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])'
|
||||
}
|
||||
|
||||
# --- Lire la config actuelle ---
|
||||
get_config() {
|
||||
local token="$1"
|
||||
curl -s "${API_URL}/api/admin/config/app" \
|
||||
-H "Authorization: Bearer ${token}"
|
||||
}
|
||||
|
||||
# --- Appliquer une config ---
|
||||
set_config() {
|
||||
local token="$1"
|
||||
local config="$2"
|
||||
curl -s -X PATCH "${API_URL}/api/admin/config/app" \
|
||||
-H "Authorization: Bearer ${token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "${config}"
|
||||
}
|
||||
|
||||
# --- Afficher le statut ---
|
||||
show_status() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
local config
|
||||
config=$(get_config "$token")
|
||||
|
||||
local capture_w capture_h preview_w preview_h full_still preview_still quality
|
||||
capture_w=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["backends"]["group_backends"][0]["backend_config"]["CAPTURE_CAM_RESOLUTION_WIDTH"])')
|
||||
capture_h=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["backends"]["group_backends"][0]["backend_config"]["CAPTURE_CAM_RESOLUTION_HEIGHT"])')
|
||||
preview_w=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["backends"]["group_backends"][0]["backend_config"]["PREVIEW_CAM_RESOLUTION_WIDTH"])')
|
||||
preview_h=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["backends"]["group_backends"][0]["backend_config"]["PREVIEW_CAM_RESOLUTION_HEIGHT"])')
|
||||
full_still=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["mediaprocessing"]["full_still_length"])')
|
||||
preview_still=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["mediaprocessing"]["preview_still_length"])')
|
||||
quality=$(echo "$config" | python3 -c 'import sys,json;c=json.load(sys.stdin);print(c["backends"]["group_backends"][0]["backend_config"]["original_still_quality"])')
|
||||
|
||||
echo "=== Configuration actuelle ==="
|
||||
echo " Capture: ${capture_w}x${capture_h}"
|
||||
echo " Preview: ${preview_w}x${preview_h}"
|
||||
echo " Full still: ${full_still}"
|
||||
echo " Preview still: ${preview_still}"
|
||||
echo " JPEG quality: ${quality}"
|
||||
|
||||
if [ "$capture_w" -ge 4000 ]; then
|
||||
echo -e " Mode: ${GREEN}HAUTE QUALITE${NC}"
|
||||
else
|
||||
echo -e " Mode: ${YELLOW}BASSE QUALITE (economie RAM)${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Appliquer haute qualite ---
|
||||
set_high() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
echo "Passage en HAUTE QUALITE..."
|
||||
|
||||
# Config backends (camera)
|
||||
set_config "$token" '{
|
||||
"backends": {
|
||||
"group_backends": [{
|
||||
"enabled": true,
|
||||
"description": "pi camera module V3",
|
||||
"backend_config": {
|
||||
"orientation": "1: 0°",
|
||||
"backend_type": "Picamera2",
|
||||
"camera_num": 0,
|
||||
"CAPTURE_CAM_RESOLUTION_WIDTH": 4608,
|
||||
"CAPTURE_CAM_RESOLUTION_HEIGHT": 3072,
|
||||
"PREVIEW_CAM_RESOLUTION_WIDTH": 2304,
|
||||
"PREVIEW_CAM_RESOLUTION_HEIGHT": 1536,
|
||||
"LIVEVIEW_RESOLUTION_WIDTH": 750,
|
||||
"LIVEVIEW_RESOLUTION_HEIGHT": 500,
|
||||
"framerate_still_mode": 20,
|
||||
"framerate_video_mode": 25,
|
||||
"frame_skip_count": 4,
|
||||
"optimized_lowlight_short_exposure": false,
|
||||
"videostream_quality": "MEDIUM",
|
||||
"original_still_quality": 100
|
||||
}
|
||||
}]
|
||||
},
|
||||
"mediaprocessing": {
|
||||
"full_still_length": 4608,
|
||||
"preview_still_length": 2304,
|
||||
"thumbnail_still_length": 960
|
||||
}
|
||||
}' > /dev/null
|
||||
|
||||
echo -e "${GREEN}HAUTE QUALITE activee${NC}"
|
||||
echo " Capture: 4608x3072, Quality: 100"
|
||||
echo " ATTENTION: consomme plus de RAM"
|
||||
echo ""
|
||||
echo "Redemarrage de photobooth-app necessaire :"
|
||||
echo " systemctl --user restart photobooth-app"
|
||||
}
|
||||
|
||||
# --- Appliquer basse qualite ---
|
||||
set_low() {
|
||||
local token
|
||||
token=$(get_token)
|
||||
|
||||
echo "Passage en BASSE QUALITE (economie RAM)..."
|
||||
|
||||
set_config "$token" '{
|
||||
"backends": {
|
||||
"group_backends": [{
|
||||
"enabled": true,
|
||||
"description": "pi camera module V3",
|
||||
"backend_config": {
|
||||
"orientation": "1: 0°",
|
||||
"backend_type": "Picamera2",
|
||||
"camera_num": 0,
|
||||
"CAPTURE_CAM_RESOLUTION_WIDTH": 2304,
|
||||
"CAPTURE_CAM_RESOLUTION_HEIGHT": 1536,
|
||||
"PREVIEW_CAM_RESOLUTION_WIDTH": 1152,
|
||||
"PREVIEW_CAM_RESOLUTION_HEIGHT": 768,
|
||||
"LIVEVIEW_RESOLUTION_WIDTH": 750,
|
||||
"LIVEVIEW_RESOLUTION_HEIGHT": 500,
|
||||
"framerate_still_mode": 20,
|
||||
"framerate_video_mode": 25,
|
||||
"frame_skip_count": 4,
|
||||
"optimized_lowlight_short_exposure": false,
|
||||
"videostream_quality": "MEDIUM",
|
||||
"original_still_quality": 90
|
||||
}
|
||||
}]
|
||||
},
|
||||
"mediaprocessing": {
|
||||
"full_still_length": 2304,
|
||||
"preview_still_length": 1200,
|
||||
"thumbnail_still_length": 480
|
||||
}
|
||||
}' > /dev/null
|
||||
|
||||
echo -e "${YELLOW}BASSE QUALITE activee${NC}"
|
||||
echo " Capture: 2304x1536, Quality: 90"
|
||||
echo " Economie RAM optimale"
|
||||
echo ""
|
||||
echo "Redemarrage de photobooth-app necessaire :"
|
||||
echo " systemctl --user restart photobooth-app"
|
||||
}
|
||||
|
||||
# --- Main ---
|
||||
case "${1:-status}" in
|
||||
high|haute|hq)
|
||||
set_high
|
||||
;;
|
||||
low|basse|lq)
|
||||
set_low
|
||||
;;
|
||||
status|info)
|
||||
show_status
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {high|low|status}"
|
||||
echo " high - Haute qualite (4608x3072, quality 100)"
|
||||
echo " low - Basse qualite (2304x1536, quality 90)"
|
||||
echo " status - Afficher la configuration actuelle"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user