Files
Monitorink/kobo/monitorinkloop.sh
jerem 5925b0f9d2 Reboot Kobo: 3 appuis bouton de page (evdev EV_KEY) au lieu du triple-tap tactile
L'ancien triple-tap via finger_trace dessinait des points noirs (outil de demo
FBInk), ne respawnait pas (mort definitif si le process tombait) et le tactile ne
reveille pas l'appareil. Le power, lui, n'emet que des scancodes MSC_SCAN parasites
(etat de charge USB). Les boutons de page emettent des EV_KEY propres (codes 193/194).

reboot_watcher.sh: lit l'evdev (FD persistant, pas de perte d'evenements), declenche
sur 3 press EV_KEY < 3 s, boucle de respawn. Plus de finger_trace.

Refresh: full force au (re)demarrage (reset=1 cote client -> oubli de prev_image cote
serveur) pour eviter un refresh partiel pose sur un ecran efface par le reboot.
2026-06-15 19:38:15 +02:00

212 lines
7.3 KiB
Bash
Executable File

#!/bin/sh
# Monitorink — boucle d'affichage e-ink sur Kobo Libra 2.
#
# Overlay sur https://github.com/usetrmnl/trmnl-kobo : réutilise ses binaires ARM
# (bin/fbink, bin/busybox_kobo) et ses helpers WiFi (scripts/*.sh). On remplace l'appel
# API TRMNL par un simple fetch de notre image de dashboard.
# Lancé par monitorink.sh (via NickelMenu). Logs -> ../monitorink.log
BASE="$(dirname "$0")"
cd "$BASE" || exit 1
IMAGE_URL="${MONITORINK_URL:-https://monitorink.homelab.nestor-server.fr/image.png}"
# Endpoints du refresh partiel, dérivés de l'URL image (.../image.png -> .../frame.meta|frame.png).
BASE_URL="${IMAGE_URL%/image.png}"
META_URL="$BASE_URL/frame.meta"
FRAME_URL="$BASE_URL/frame.png"
CLIENT="${MONITORINK_CLIENT:-kobo}"
REFRESH="${MONITORINK_REFRESH:-600}"
TMP="/tmp/monitorink.png"
FBINK="./bin/fbink/fbink"
BUSYBOX="./bin/busybox_kobo"
log() { echo "[$(date '+%H:%M:%S')] $*"; sync; }
read_battery() {
# Renvoie "CAP|CHG" (ex. "85|0"), CHG=1 si en charge. Vide si introuvable.
# On lit capacity + status dans le même dossier /sys/class/power_supply/*.
for d in /sys/class/power_supply/*/; do
[ -r "${d}capacity" ] || continue
cap=$(cat "${d}capacity" 2>/dev/null)
chg=0
if [ -r "${d}status" ]; then
case "$(cat "${d}status" 2>/dev/null)" in
Charging|Full) chg=1 ;;
esac
fi
echo "${cap}|${chg}"
return 0
done
echo ""
}
bat_query() {
# Renvoie "bat=CAP&chg=CHG" pour pousser la batterie au backend (vide si introuvable).
bat="$(read_battery)"
if [ -n "$bat" ]; then
cap="${bat%%|*}"; chg="${bat##*|}"
echo "bat=${cap}&chg=${chg}"
fi
}
http_get() {
# $1=url, $2=fichier de sortie ("-" = stdout). busybox wget puis fallback curl.
url="$1"; out="$2"
if [ "$out" = "-" ]; then
"$BUSYBOX" wget -q -T 30 -O - "$url" 2>/dev/null && return 0
command -v curl >/dev/null 2>&1 && curl -fsSL -m 30 "$url" 2>/dev/null && return 0
else
"$BUSYBOX" wget -q -T 30 -O "$out" "$url" 2>/dev/null && return 0
command -v curl >/dev/null 2>&1 && curl -fsSL -m 30 -o "$out" "$url" 2>/dev/null && return 0
fi
return 1
}
fetch_meta() {
# Récupère la ligne "MODE X Y W H SEQ" du backend (avec batterie + client). Vide si KO.
# Au 1er cycle après un (re)démarrage (FIRST=1), on demande reset=1 : l'écran a été effacé
# par le reboot, on force un full refresh côté serveur pour éviter un partiel sur base erronée.
murl="$META_URL?client=$CLIENT"
[ "${FIRST:-0}" = 1 ] && murl="$murl&reset=1"
q="$(bat_query)"; [ -n "$q" ] && murl="$murl&$q"
http_get "$murl" -
}
# Ferme les FD hérités pour ne pas bloquer l'éjection USB.
exec 3>&- 2>/dev/null
log "boucle démarrée — BASE=$BASE URL=$IMAGE_URL refresh=${REFRESH}s"
log "fbink présent: $([ -x "$FBINK" ] && echo oui || echo NON) ; busybox: $([ -x "$BUSYBOX" ] && echo oui || echo NON)"
display_full() {
# Full refresh : clear + waveform complète (le "flash" e-ink), efface le ghosting.
"$FBINK" -g file="$TMP",valign=CENTER,halign=CENTER -c -f
log "display full rc=$?"
}
display_partial() {
# Refresh partiel : dessine le crop à l'offset (x,y) ; ni -c ni -f -> seule cette zone est
# rafraîchie, en waveform partielle (sans flash). $1=x $2=y (coords portrait, origine HG).
"$FBINK" -g file="$TMP",x="$1",y="$2"
log "display partial x=$1 y=$2 rc=$?"
}
offline() {
log "hors ligne"
"$FBINK" -pmh "Monitorink hors ligne ($(date '+%H:%M'))"
}
show_frame() {
# Récupère le crop/full image stocké côté serveur et l'affiche selon le mode.
# $1=mode $2=x $3=y
if ! http_get "$FRAME_URL?client=$CLIENT" "$TMP"; then
log "frame.png KO (mode=$1)"
[ "$1" = "full" ] && offline
return 1
fi
log "frame.png OK ($(wc -c < "$TMP" 2>/dev/null) octets)"
if [ "$1" = "partial" ]; then
display_partial "$2" "$3"
else
display_full
fi
}
frontlight_off() {
# Éteint le rétroéclairage (frontlight). Le nœud sysfs exact dépend du modèle Kobo,
# donc on écrit 0 dans tous les contrôleurs présents.
for b in /sys/class/backlight/*/brightness; do
[ -w "$b" ] && echo 0 > "$b" 2>/dev/null
done
}
suspend_for() {
# Séquence Kobo éprouvée (trmnl-kobo) : state-extended=1 AVANT rtcwake, puis
# suspend manuel (echo mem) si rtcwake n'a pas vraiment suspendu.
secs="$1"
sync
echo 1 > /sys/power/state-extended 2>/dev/null
start=$(date +%s)
"$BUSYBOX" rtcwake -a -s "$secs" -m mem 2>/dev/null
elapsed=$(( $(date +%s) - start ))
log "rtcwake elapsed=${elapsed}s"
if [ "$elapsed" -le 10 ]; then
log "suspend manuel via echo mem"
sleep 1; sync; sleep 2
m_start=$(date +%s)
echo mem > /sys/power/state 2>/dev/null
log "echo mem elapsed=$(( $(date +%s) - m_start ))s (≈${secs}=OK, ≈0=USB branché/suspend bloqué)"
fi
echo 0 > /sys/power/state-extended 2>/dev/null
# Repli : si rien n'a réellement suspendu (USB branché, etc.), on attend le temps
# restant en sleep pour ne pas boucler en continu (martèlement backend + batterie).
total=$(( $(date +%s) - start ))
if [ "$total" -lt "$secs" ]; then
rem=$(( secs - total ))
log "suspend incomplet (${total}s) -> sleep ${rem}s"
sleep "$rem"
fi
}
has_ip() { ip addr show 2>/dev/null | grep -o 'inet [0-9.]*' | grep -qv '127.0'; }
wifi_up() {
./scripts/enable-wifi.sh >/dev/null 2>&1
./scripts/force-wifi-connection.sh >/dev/null 2>&1
# Attend l'association + bail DHCP (jusqu'à ~24 s).
i=0
while [ "$i" -lt 12 ]; do
./scripts/obtain-ip.sh >/dev/null 2>&1
has_ip && return 0
sleep 2; i=$((i + 1))
done
return 1
}
wifi_down() {
# Coupe la radio avant le suspend (helpers trmnl, env Nickel siphonné en amont).
./scripts/release-ip.sh >/dev/null 2>&1
./scripts/disable-wifi.sh >/dev/null 2>&1
}
# MODE PROD : frontlight éteint, WiFi cyclé (off pendant le suspend), rtcwake mem.
frontlight_off
FIRST=1 # 1er cycle après lancement -> demande un full refresh (reset=1) au backend
while true; do
log "--- itération ---"
frontlight_off # réaffirme après chaque réveil
./scripts/ledToggle.sh on 2>/dev/null
wifi_up
meta="$(fetch_meta)"
if [ -z "$meta" ]; then
# WiFi peut-être tombé : on tente une reconnexion puis un re-essai.
log "meta KO -> reconnexion WiFi"
wifi_up
meta="$(fetch_meta)"
fi
if [ -n "$meta" ]; then
# shellcheck disable=SC2086
set -- $meta # MODE X Y W H SEQ
mode="$1"; mx="$2"; my="$3"
log "meta: mode=$mode x=$mx y=$my w=$4 h=$5 seq=$6"
case "$mode" in
noop) log "aucun changement -> pas de refresh" ;;
partial) show_frame partial "$mx" "$my" ;;
*) show_frame full ;; # full ou valeur inattendue -> full refresh sûr
esac
FIRST=0 # meta obtenue : le reset n'a plus lieu d'être pour les cycles suivants
else
log "meta ECHEC"
offline
fi
./scripts/ledToggle.sh off 2>/dev/null
wifi_down # coupe la radio avant le suspend
suspend_for "$REFRESH" # rtcwake -m mem, WiFi éteint pendant ~5 min
done