Affiche la batterie de la Kobo (push via params /image.png) en pied de page
This commit is contained in:
@@ -14,6 +14,7 @@ from fastapi.responses import HTMLResponse
|
|||||||
|
|
||||||
import render
|
import render
|
||||||
from config import config
|
from config import config
|
||||||
|
from integrations import kobo
|
||||||
|
|
||||||
app = FastAPI(title="Monitorink", docs_url=None, redoc_url=None)
|
app = FastAPI(title="Monitorink", docs_url=None, redoc_url=None)
|
||||||
|
|
||||||
@@ -26,7 +27,9 @@ async def health() -> dict:
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/image.png")
|
@app.get("/image.png")
|
||||||
async def image(fresh: int = 0) -> Response:
|
async def image(fresh: int = 0, bat: int | None = None, chg: int = 0) -> Response:
|
||||||
|
# La Kobo pousse sa batterie ici (bat=0-100, chg=1 si en charge) à chaque fetch.
|
||||||
|
kobo.record(bat, bool(chg))
|
||||||
now = time.time()
|
now = time.time()
|
||||||
cached = _cache["png"]
|
cached = _cache["png"]
|
||||||
age = now - float(_cache["ts"])
|
age = now - float(_cache["ts"])
|
||||||
|
|||||||
53
backend/integrations/kobo.py
Normal file
53
backend/integrations/kobo.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
"""État de la liseuse Kobo (batterie), POUSSÉ par le script monitorinkloop.sh.
|
||||||
|
|
||||||
|
Contrairement aux autres intégrations qui *tirent* leurs données depuis une API, ici la
|
||||||
|
Kobo *pousse* son niveau de batterie en paramètres de l'URL /image.png à chaque fetch
|
||||||
|
(elle seule connaît sa charge). On conserve la dernière valeur en mémoire, horodatée,
|
||||||
|
pour la rendre au prochain dessin du dashboard.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import time
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
# Au-delà de ce délai sans nouvelle de la Kobo, la valeur est jugée périmée.
|
||||||
|
STALE_AFTER_SECONDS = 1800
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class KoboState:
|
||||||
|
percent: int | None = None
|
||||||
|
charging: bool = False
|
||||||
|
updated_ts: float = 0.0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ok(self) -> bool:
|
||||||
|
return self.percent is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def age_seconds(self) -> float:
|
||||||
|
return time.time() - self.updated_ts if self.updated_ts else float("inf")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stale(self) -> bool:
|
||||||
|
return self.age_seconds > STALE_AFTER_SECONDS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def low(self) -> bool:
|
||||||
|
return self.percent is not None and self.percent <= 15 and not self.charging
|
||||||
|
|
||||||
|
|
||||||
|
_state = KoboState()
|
||||||
|
|
||||||
|
|
||||||
|
def record(percent: int | None, charging: bool) -> None:
|
||||||
|
"""Enregistre le dernier niveau rapporté par la Kobo (borné à 0-100)."""
|
||||||
|
if percent is None:
|
||||||
|
return
|
||||||
|
_state.percent = max(0, min(100, percent))
|
||||||
|
_state.charging = charging
|
||||||
|
_state.updated_ts = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def current() -> KoboState:
|
||||||
|
return _state
|
||||||
@@ -13,7 +13,7 @@ from PIL import Image
|
|||||||
from playwright.async_api import async_playwright
|
from playwright.async_api import async_playwright
|
||||||
|
|
||||||
from config import config
|
from config import config
|
||||||
from integrations import claude_usage, codex, homeassistant, nas, weather
|
from integrations import claude_usage, codex, homeassistant, kobo, nas, weather
|
||||||
|
|
||||||
TEMPLATES = Path(__file__).parent / "templates"
|
TEMPLATES = Path(__file__).parent / "templates"
|
||||||
|
|
||||||
@@ -75,6 +75,7 @@ async def build_context() -> dict:
|
|||||||
"ha_states": ha,
|
"ha_states": ha,
|
||||||
"nas": nas_status,
|
"nas": nas_status,
|
||||||
"codex": codex_status,
|
"codex": codex_status,
|
||||||
|
"kobo": kobo.current(),
|
||||||
"updated": now.strftime("%H:%M"),
|
"updated": now.strftime("%H:%M"),
|
||||||
"stale": False,
|
"stale": False,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,6 +189,7 @@
|
|||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<span>Monitorink · 3 taps = redémarrer</span>
|
<span>Monitorink · 3 taps = redémarrer</span>
|
||||||
|
{% if kobo.ok %}<span class="{% if kobo.low %}stale{% endif %}">{% if kobo.charging %}⚡{% else %}🔋{% endif %} Kobo {{ kobo.percent }}%{% if kobo.stale %} · ?{% endif %}</span>{% endif %}
|
||||||
<span class="{% if stale %}stale{% endif %}">maj {{ updated }}{% if stale %} · DONNÉE PÉRIMÉE{% endif %}</span>
|
<span class="{% if stale %}stale{% endif %}">maj {{ updated }}{% if stale %} · DONNÉE PÉRIMÉE{% endif %}</span>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,36 @@ BUSYBOX="./bin/busybox_kobo"
|
|||||||
|
|
||||||
log() { echo "[$(date '+%H:%M:%S')] $*"; sync; }
|
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 ""
|
||||||
|
}
|
||||||
|
|
||||||
fetch() {
|
fetch() {
|
||||||
|
# On pousse la batterie de la Kobo en paramètres d'URL (le backend la mémorise).
|
||||||
|
url="$IMAGE_URL"
|
||||||
|
bat="$(read_battery)"
|
||||||
|
if [ -n "$bat" ]; then
|
||||||
|
cap="${bat%%|*}"; chg="${bat##*|}"
|
||||||
|
case "$url" in *\?*) sep="&" ;; *) sep="?" ;; esac
|
||||||
|
url="${url}${sep}bat=${cap}&chg=${chg}"
|
||||||
|
fi
|
||||||
# busybox wget (toujours présent), fallback curl si dispo dans le PATH.
|
# busybox wget (toujours présent), fallback curl si dispo dans le PATH.
|
||||||
"$BUSYBOX" wget -q -T 30 -O "$TMP" "$IMAGE_URL" 2>/dev/null && return 0
|
"$BUSYBOX" wget -q -T 30 -O "$TMP" "$url" 2>/dev/null && return 0
|
||||||
command -v curl >/dev/null 2>&1 && curl -fsSL -m 30 -o "$TMP" "$IMAGE_URL" 2>/dev/null && return 0
|
command -v curl >/dev/null 2>&1 && curl -fsSL -m 30 -o "$TMP" "$url" 2>/dev/null && return 0
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user