feat(station): ausgelieferte Firmware-Version auf dem Panel
Welche Version die Station gerade ausliefert, war am Geraet nur per SSH zu beantworten. Jetzt sagt es die Anzeige selbst, je nach Bild an der Stelle, wo Platz ist: - Aus-Zustand: rechtsbuendig neben der Adresse (neue Rolle ROLE_DETAIL_RIGHT, nach dem Muster der Phase neben dem Modus) - Rettung: rechtsbuendig in der Kopfzeile, dort steht keine Phase im Weg - Migration: als beschriftete Zeile "liefert: <version>" direkt hinter "FW:", NICHT in der Kopfzeile - daneben bleiben nur rund 112 px, daran ist schon "FREIGEGEBEN: <ziel>" gescheitert, und zwei nackte Versionsnummern nebeneinander waeren eine Verwechslungsfalle (FW: ist die der Lampe) Quelle ist der GEWAEHLTE Bildsatz: im Migrationsmodus manifest.json (die Migrationsabzuege tragen keinen ninlight-Anhaenger), im Rettungsmodus der Anhaenger der _ota.img. Weichen die Eintraege voneinander ab, steht da "gemischt" - ein zusammengefuehrter Deploy kann Alt-Eintraege mit anderer Version enthalten, und eine davon herauszugreifen waere geraten. Fehlt die Quelle, bleibt die Angabe weg statt eine Version zu erfinden.
This commit is contained in:
@@ -76,6 +76,11 @@ TEXT_MARGIN_PX = 2
|
||||
# Migration.
|
||||
ROLE_HEADER = "header"
|
||||
ROLE_HEADER_PHASE = "header_phase"
|
||||
# Rechtsbuendig in DERSELBEN Zeile wie die zuletzt gezeichnete Detailzeile.
|
||||
# Genau ein Block benutzt sie: die ausgelieferte Firmware-Version neben der
|
||||
# Adresse im Aus-Zustand. Eigene Rolle statt einer zweiten Zeile, weil der
|
||||
# Platz auf 250x122 knapp ist und beide Angaben zusammengehoeren.
|
||||
ROLE_DETAIL_RIGHT = "detail_right"
|
||||
ROLE_HERO = "hero"
|
||||
ROLE_DETAIL = "detail"
|
||||
ROLE_BIG = "big"
|
||||
@@ -187,6 +192,9 @@ PHASE_LABELS = {"auto": "AUTO", "listen": "LISTEN"}
|
||||
# verloren geht - der Zustand "freigegeben" ist der einzige mit diesem Label) und der
|
||||
# Zielname als eigene Beiwerkzeile ueber die volle Panelbreite.
|
||||
ARMED_LABEL = "ARMED"
|
||||
# Praefix der Zeile mit der ausgelieferten Version - grenzt sie von "FW:" ab, das die
|
||||
# Version der erkannten Lampe traegt.
|
||||
DELIVERS_PREFIX = "liefert: "
|
||||
ARMED_DETAIL_PREFIX = "Armed: "
|
||||
|
||||
# Der Bildsatz-Umschalter in der Fusszeile. Bewusst als Aussage ueber die LAMPE formuliert
|
||||
@@ -334,6 +342,7 @@ def blocks(state: dict) -> list[tuple[str, str]]:
|
||||
out.append((f"{geraet}: {ip}", ROLE_DETAIL))
|
||||
else:
|
||||
out.append((f"IP: {ip}", ROLE_DETAIL))
|
||||
out += _delivered_version_blocks(state, ROLE_DETAIL_RIGHT)
|
||||
return out + footer
|
||||
|
||||
if mode not in ("recovery", "migration"):
|
||||
@@ -348,6 +357,7 @@ def blocks(state: dict) -> list[tuple[str, str]]:
|
||||
ssid = state.get("ssid")
|
||||
|
||||
if mode == "recovery":
|
||||
out += _delivered_version_blocks(state, ROLE_HEADER_PHASE)
|
||||
# Im Rettungsmodus gibt es keine Phase - kein ROLE_HEADER_PHASE-Block, die
|
||||
# Kopfzeile bleibt also nur der Modus (compose() zentriert sie dann weiterhin
|
||||
# wie vor der zweiten Hardware-Abnahme, siehe dort).
|
||||
@@ -409,6 +419,13 @@ def blocks(state: dict) -> list[tuple[str, str]]:
|
||||
lamp_version = last_seen.get("lamp_version")
|
||||
if lamp_version:
|
||||
out.append((f"FW: {lamp_version}", ROLE_DETAIL))
|
||||
# Beschriftet und unmittelbar hinter "FW:", weil beides Versionsnummern sind
|
||||
# und nebeneinander sonst niemand weiss, welche wem gehoert: "FW:" ist die
|
||||
# der erkannten LAMPE, "liefert:" die des Bildsatzes. Nicht in die Kopfzeile:
|
||||
# dort steht im Migrationsmodus die Phase, und neben dem Modus bleiben nur
|
||||
# rund 112 px - daran ist schon "FREIGEGEBEN: <ziel>" gescheitert.
|
||||
for text, _ in _delivered_version_blocks(state, ROLE_DETAIL):
|
||||
out.append((f"{DELIVERS_PREFIX}{text}", ROLE_DETAIL))
|
||||
if ssid and armed_target is None:
|
||||
# Nur hier, wo die grosse Zeile schon vergeben ist - sonst stuende die
|
||||
# SSID zweimal auf dem Panel.
|
||||
@@ -462,6 +479,19 @@ def blocks(state: dict) -> list[tuple[str, str]]:
|
||||
return out + footer
|
||||
|
||||
|
||||
def _delivered_version_blocks(state: dict, role: str) -> list[tuple[str, str]]:
|
||||
"""Die Version, die die Station ausliefert - oder [], wenn sie unbekannt ist.
|
||||
|
||||
Bewusst keine Ersatzanzeige: eine erfundene oder veraltete Versionsnummer ist
|
||||
schlimmer als keine, denn sie beantwortet genau die Frage falsch, fuer die sie da
|
||||
ist. display.py liefert "" , wenn manifest.json bzw. die Abzuege fehlen.
|
||||
"""
|
||||
version = state.get("image_version")
|
||||
if not isinstance(version, str) or not version:
|
||||
return []
|
||||
return [(version, role)]
|
||||
|
||||
|
||||
def _imageset_blocks(state: dict) -> list[tuple[str, str]]:
|
||||
"""Die Fusszeile mit dem Bildsatz-Umschalter, oder [] wenn der Zustand keinen traegt
|
||||
(aeltere display.py-Fassung, von Hand zusammengesetzter Zustand in einem Test).
|
||||
@@ -636,6 +666,7 @@ def compose(state: dict):
|
||||
ROLE_HEADER_PHASE: (HEADER_PHASE_FONT_PX, True),
|
||||
ROLE_HERO: (HERO_FONT_PX, True),
|
||||
ROLE_DETAIL: (DETAIL_FONT_PX, False),
|
||||
ROLE_DETAIL_RIGHT: (DETAIL_FONT_PX, False),
|
||||
ROLE_BIG: (BIG_FONT_PX, True),
|
||||
ROLE_FOOTER: (FOOTER_FONT_PX, True),
|
||||
}
|
||||
@@ -700,6 +731,10 @@ def compose(state: dict):
|
||||
fill=0, align="center")
|
||||
|
||||
y = TEXT_MARGIN_PX
|
||||
# Oberkante und rechter Rand der zuletzt gezeichneten Flusszeile - ROLE_DETAIL_RIGHT
|
||||
# setzt seinen Text daneben.
|
||||
letzte_zeile_oben: int | None = None
|
||||
letzte_zeile_rechts = 0.0
|
||||
for raw_text, role in block_list:
|
||||
font = font_for(role)
|
||||
|
||||
@@ -740,6 +775,22 @@ def compose(state: dict):
|
||||
# Ueberlappen mit dem Modus ist nicht erlaubt.
|
||||
continue
|
||||
|
||||
if role == ROLE_DETAIL_RIGHT:
|
||||
# Rechtsbuendig neben der zuletzt gezeichneten Zeile, gemessen ab deren
|
||||
# tatsaechlichem rechten Rand - wie die Phase neben dem Modus. Ohne
|
||||
# vorangehende Zeile (von Hand zusammengesetzter Zustand im Test) oder bei
|
||||
# zu wenig Platz entfaellt die Angabe: ein Ueberlappen waere schlimmer als
|
||||
# eine fehlende Zusatzauskunft.
|
||||
if letzte_zeile_oben is None:
|
||||
continue
|
||||
frei_px = (PANEL_WIDTH_PX - TEXT_MARGIN_PX - letzte_zeile_rechts
|
||||
- HEADER_PHASE_GAP_PX)
|
||||
if frei_px >= HEADER_PHASE_MIN_WIDTH_PX:
|
||||
text = fit(raw_text, font, width_px=frei_px)
|
||||
_draw_text(draw, text, font, top=letzte_zeile_oben,
|
||||
height=DETAIL_LINE_HEIGHT_PX, fill=0, align="right")
|
||||
continue
|
||||
|
||||
text = fit(raw_text, font)
|
||||
line_height = HERO_LINE_HEIGHT_PX if role == ROLE_HERO else DETAIL_LINE_HEIGHT_PX
|
||||
if y + line_height > flow_limit_px:
|
||||
@@ -748,6 +799,8 @@ def compose(state: dict):
|
||||
# Reihenfolge in blocks() ist deshalb nach Wichtigkeit sortiert.
|
||||
break
|
||||
draw.text((TEXT_MARGIN_PX, y), text, font=font, fill=0)
|
||||
letzte_zeile_oben = y
|
||||
letzte_zeile_rechts = TEXT_MARGIN_PX + draw.textlength(text, font=font)
|
||||
y += line_height
|
||||
|
||||
return image
|
||||
|
||||
Reference in New Issue
Block a user