diff --git a/opt/pincabos/installer-gui/i18n.json b/opt/pincabos/installer-gui/i18n.json index ffa1ef8a..e59b80b8 100755 --- a/opt/pincabos/installer-gui/i18n.json +++ b/opt/pincabos/installer-gui/i18n.json @@ -131,12 +131,12 @@ "dof_strip_note": "rubans et matrices LED (Teensy, Wemos) à déclarer à l'étape Toys / LED.", "sound3d_0": "Stéréo — 2 canaux, haut-parleurs à l'avant (recommandé pour une sortie stéréo)", "sound3d_1": "Stéréo inversée — 2 canaux, haut-parleurs à l'arrière (backbox)", - "sound3d_2": "SSF 6 canaux — exciters à l'arrière du meuble (lockbar)", - "sound3d_3": "SSF 6 canaux — exciters à l'avant du meuble (lockbar)", + "sound3d_2": "SSF 6 canaux — exciters câblés sur les canaux ARRIÈRE de la carte son", + "sound3d_3": "SSF 6 canaux — exciters câblés sur les canaux AVANT de la carte son", "sound3d_4": "SSF 6 canaux latéraux + arrière — mixage historique", "sound3d_5": "SSF 6 canaux latéraux + arrière — nouveau mixage (VPX 10.8)", "toys_title": "Toys et LED", - "toys_hint": "Les cartes reconnues sont listées. Les contrôleurs de rubans adressables (Teensy, Wemos) se déclarent ici : matrice du backboard ou rubans, avec le nombre de LED par sortie tel que vous les avez câblés.", + "toys_hint": "Les cartes reconnues sont listées. Rien n'est prérempli : PinCabOS ne connaît pas votre backboard. Allumez le contrôleur qui pilote vos LED et décrivez-les — sinon, laissez éteint et faites-le plus tard depuis la page DOF du cabinet. Un contrôleur laissé éteint ne bloque pas l'installation.", "toys_inputs": "Boutons, plunger, nudge", "toys_inputs_none": "aucune carte de boutons vue (clavier seul)", "toys_inputs_note": "Les boutons se mappent au premier démarrage, dans Map Commander : il faut appuyer sur chacun. Sans carte, vous pourrez passer cette étape.", @@ -148,10 +148,10 @@ "toys_mode": "Fonction", "toys_mode_matrix": "Matrice (backboard)", "toys_mode_strips": "Rubans", - "toys_size": "Largeur × hauteur", + "toys_size": "LED par rangée × nombre de rangées", "toys_arrangement": "Arrangement", "toys_outputs": "LED par sortie", - "toys_outputs_hint_matrix": "Une valeur par sortie du contrôleur, dans l'ordre du câblage ; le total doit faire largeur × hauteur.", + "toys_outputs_hint_matrix": "Une valeur par sortie du contrôleur, dans l'ordre du câblage. Le total doit faire LED par rangée × nombre de rangées. Une sortie de Teensy pilote au plus 512 LED : au-delà, la matrice est répartie sur plusieurs sorties — c'est ainsi qu'elle doit être câblée.", "toys_outputs_hint_strips": "Une valeur par sortie : chaque sortie devient un ruban (0 = sortie libre).", "toys_total": "Total :", "toys_color": "Ordre des couleurs", @@ -175,8 +175,8 @@ "lecture_hint": "Tourne l'affichage pendant l'installation seulement, pour une dalle montée en portrait. Rien n'est conservé.", "sound3d_hint_0": "Convient à toute sortie stéréo (jack, HDMI 2 canaux).", "sound3d_hint_1": "Sortie stéréo dont les haut-parleurs sont derrière le joueur.", - "sound3d_hint_2": "Nécessite une carte son 5.1/7.1 réglée en surround et des exciters câblés sur les canaux arrière. Sur une sortie stéréo, les sons seraient muets : le premier démarrage retombe alors en stéréo.", - "sound3d_hint_3": "Nécessite une carte son 5.1/7.1 réglée en surround et des exciters câblés sur les canaux avant. Sur une sortie stéréo, le premier démarrage retombe en stéréo.", + "sound3d_hint_2": "La question porte sur le CÂBLAGE, pas sur la place des exciters dans le meuble : sur quelle prise de la carte son sont-ils branchés ? Ici, la prise arrière (souvent noire). Nécessite une carte 5.1/7.1 réglée en surround ; sur une sortie stéréo le premier démarrage retombe en stéréo. Dans le doute, l'étape Haut-parleurs fait parler chaque canal l'un après l'autre.", + "sound3d_hint_3": "Même question, autre prise : ici les exciters sont sur la sortie avant (souvent verte), celle qui sert d'ordinaire au fronton. Nécessite une carte 5.1/7.1 réglée en surround ; sur une sortie stéréo le premier démarrage retombe en stéréo. Dans le doute, l'étape Haut-parleurs fait parler chaque canal l'un après l'autre.", "sound3d_hint_4": "Nécessite une carte 5.1/7.1. Le bas de table (flips, lockbar) part sur les canaux arrière, le milieu sur les latéraux, le fronton sur l'avant. Sur une sortie stéréo, le premier démarrage retombe en stéréo.", "sound3d_hint_5": "Nécessite une carte 5.1/7.1, mixage VPX 10.8. Le bas de table (flips, lockbar) part sur les canaux arrière, le milieu sur les latéraux, le fronton sur l'avant. Sur une sortie stéréo, le premier démarrage retombe en stéréo.", "snd_mode_hint": "Le mode dit à VPX sur quels canaux jouer les sons de table (SSF = Surround Sound Feedback).", @@ -336,12 +336,12 @@ "dof_strip_note": "LED strips and matrices (Teensy, Wemos) are declared in the Toys / LED step.", "sound3d_0": "Stereo — 2 channels, front speakers (recommended for a stereo output)", "sound3d_1": "Reversed stereo — 2 channels, rear speakers (backbox)", - "sound3d_2": "SSF 6 channels — exciters at the rear of the cabinet (lockbar)", - "sound3d_3": "SSF 6 channels — exciters at the front of the cabinet (lockbar)", + "sound3d_2": "SSF 6 channels — exciters wired to the sound card's REAR channels", + "sound3d_3": "SSF 6 channels — exciters wired to the sound card's FRONT channels", "sound3d_4": "SSF 6 channels side + rear — legacy mix", "sound3d_5": "SSF 6 channels side + rear — new mix (VPX 10.8)", "toys_title": "Toys and LEDs", - "toys_hint": "Recognised boards are listed. Addressable strip controllers (Teensy, Wemos) are declared here: backboard matrix or strips, with the LED count per output as you wired them.", + "toys_hint": "Detected boards are listed. Nothing is pre-filled: PinCabOS does not know your backboard. Switch on the controller that drives your LEDs and describe them — otherwise leave it off and do it later from the cabinet's DOF page. A controller left off does not block the installation.", "toys_inputs": "Buttons, plunger, nudge", "toys_inputs_none": "no button board seen (keyboard only)", "toys_inputs_note": "Buttons are mapped at first boot, in Map Commander: each one has to be pressed. Without a board you will be able to skip that step.", @@ -353,10 +353,10 @@ "toys_mode": "Role", "toys_mode_matrix": "Matrix (backboard)", "toys_mode_strips": "Strips", - "toys_size": "Width × height", + "toys_size": "LEDs per row × number of rows", "toys_arrangement": "Arrangement", "toys_outputs": "LEDs per output", - "toys_outputs_hint_matrix": "One value per controller output, in wiring order; the total must equal width × height.", + "toys_outputs_hint_matrix": "One value per controller output, in wiring order. The total must equal LEDs per row × number of rows. A Teensy output drives at most 512 LEDs: beyond that the matrix spans several outputs — and it must be physically wired that way.", "toys_outputs_hint_strips": "One value per output: each output becomes a strip (0 = unused output).", "toys_total": "Total:", "toys_color": "Colour order", @@ -380,8 +380,8 @@ "lecture_hint": "Rotates the display during installation only, for a panel mounted in portrait. Nothing is kept.", "sound3d_hint_0": "Fits any stereo output (jack, 2-channel HDMI).", "sound3d_hint_1": "Stereo output whose speakers sit behind the player.", - "sound3d_hint_2": "Needs a 5.1/7.1 sound card set to surround and exciters wired on the rear channels. On a stereo output sounds would be silent: first boot falls back to stereo.", - "sound3d_hint_3": "Needs a 5.1/7.1 sound card set to surround and exciters wired on the front channels. On a stereo output first boot falls back to stereo.", + "sound3d_hint_2": "This is about WIRING, not where the exciters sit in the cabinet: which sound card jack are they plugged into? Here, the rear jack (often black). Needs a 5.1/7.1 card set to surround; on a stereo output first boot falls back to stereo. If unsure, the Speakers step makes each channel speak in turn.", + "sound3d_hint_3": "Same question, other jack: here the exciters are on the front output (often green), the one usually used for the backbox. Needs a 5.1/7.1 card set to surround; on a stereo output first boot falls back to stereo. If unsure, the Speakers step makes each channel speak in turn.", "sound3d_hint_4": "Needs a 5.1/7.1 card. The bottom of the table (flippers, lockbar) goes to the rear channels, the middle to the side channels, the backbox to the front. On a stereo output first boot falls back to stereo.", "sound3d_hint_5": "Needs a 5.1/7.1 card, VPX 10.8 mix. The bottom of the table (flippers, lockbar) goes to the rear channels, the middle to the side channels, the backbox to the front. On a stereo output first boot falls back to stereo.", "snd_mode_hint": "The mode tells VPX which channels play the table sounds (SSF = Surround Sound Feedback).", @@ -541,12 +541,12 @@ "dof_strip_note": "LED-Streifen und Matrizen (Teensy, Wemos) werden im Schritt Toys / LED angegeben.", "sound3d_0": "Stereo — 2 Kanäle, Lautsprecher vorne (empfohlen für einen Stereo-Ausgang)", "sound3d_1": "Stereo vertauscht — 2 Kanäle, Lautsprecher hinten (Backbox)", - "sound3d_2": "SSF 6 Kanäle — Exciter hinten im Gehäuse (Lockbar)", - "sound3d_3": "SSF 6 Kanäle — Exciter vorne im Gehäuse (Lockbar)", + "sound3d_2": "SSF 6 Kanäle — Exciter an den HINTEREN Kanälen der Soundkarte", + "sound3d_3": "SSF 6 Kanäle — Exciter an den VORDEREN Kanälen der Soundkarte", "sound3d_4": "SSF 6 Kanäle seitlich + hinten — alter Mix", "sound3d_5": "SSF 6 Kanäle seitlich + hinten — neuer Mix (VPX 10.8)", "toys_title": "Toys und LEDs", - "toys_hint": "Erkannte Karten werden aufgelistet. Controller für adressierbare Streifen (Teensy, Wemos) werden hier angegeben: Backboard-Matrix oder Streifen, mit der LED-Zahl pro Ausgang wie verkabelt.", + "toys_hint": "Erkannte Karten werden aufgelistet. Nichts ist vorausgefüllt: PinCabOS kennt Ihr Backboard nicht. Schalten Sie den Controller ein, der Ihre LEDs steuert, und beschreiben Sie sie — sonst lassen Sie ihn aus und erledigen es später auf der DOF-Seite. Ein ausgeschalteter Controller blockiert die Installation nicht.", "toys_inputs": "Tasten, Plunger, Nudge", "toys_inputs_none": "keine Tastenkarte erkannt (nur Tastatur)", "toys_inputs_note": "Die Tasten werden beim ersten Start im Map Commander zugeordnet: jede muss gedrückt werden. Ohne Karte lässt sich dieser Schritt überspringen.", @@ -558,10 +558,10 @@ "toys_mode": "Funktion", "toys_mode_matrix": "Matrix (Backboard)", "toys_mode_strips": "Streifen", - "toys_size": "Breite × Höhe", + "toys_size": "LEDs pro Reihe × Anzahl Reihen", "toys_arrangement": "Anordnung", "toys_outputs": "LEDs pro Ausgang", - "toys_outputs_hint_matrix": "Ein Wert pro Controller-Ausgang in Verkabelungsreihenfolge; die Summe muss Breite × Höhe ergeben.", + "toys_outputs_hint_matrix": "Ein Wert pro Controller-Ausgang, in Verkabelungsreihenfolge. Die Summe muss LEDs pro Reihe × Reihen ergeben. Ein Teensy-Ausgang treibt höchstens 512 LEDs: darüber verteilt sich die Matrix auf mehrere Ausgänge — und muss auch so verkabelt sein.", "toys_outputs_hint_strips": "Ein Wert pro Ausgang: jeder Ausgang wird ein Streifen (0 = frei).", "toys_total": "Summe:", "toys_color": "Farbreihenfolge", @@ -585,8 +585,8 @@ "lecture_hint": "Dreht die Anzeige nur während der Installation, für einen im Hochformat montierten Bildschirm. Nichts wird gespeichert.", "sound3d_hint_0": "Passt zu jedem Stereo-Ausgang (Klinke, HDMI 2 Kanäle).", "sound3d_hint_1": "Stereo-Ausgang, dessen Lautsprecher hinter dem Spieler stehen.", - "sound3d_hint_2": "Braucht eine 5.1/7.1-Soundkarte im Surround-Modus und Exciter auf den hinteren Kanälen. Auf Stereo wären Töne stumm: der erste Start fällt auf Stereo zurück.", - "sound3d_hint_3": "Braucht eine 5.1/7.1-Soundkarte im Surround-Modus und Exciter auf den vorderen Kanälen. Auf Stereo fällt der erste Start auf Stereo zurück.", + "sound3d_hint_2": "Es geht um die VERKABELUNG, nicht um den Platz im Gehäuse: an welcher Buchse der Soundkarte hängen sie? Hier die hintere Buchse (oft schwarz). Erfordert eine 5.1/7.1-Karte im Surround-Modus; bei Stereo-Ausgang fällt der erste Start auf Stereo zurück. Im Zweifel lässt der Schritt Lautsprecher jeden Kanal nacheinander sprechen.", + "sound3d_hint_3": "Gleiche Frage, andere Buchse: hier hängen die Exciter am vorderen Ausgang (oft grün), sonst für den Backbox-Ton. Erfordert eine 5.1/7.1-Karte im Surround-Modus; bei Stereo-Ausgang fällt der erste Start auf Stereo zurück. Im Zweifel lässt der Schritt Lautsprecher jeden Kanal nacheinander sprechen.", "sound3d_hint_4": "Braucht eine 5.1/7.1-Karte. Der untere Tischbereich (Flipper, Lockbar) geht auf die hinteren Kanäle, die Mitte auf die Seitenkanäle, die Backbox nach vorne. Auf Stereo fällt der erste Start auf Stereo zurück.", "sound3d_hint_5": "Braucht eine 5.1/7.1-Karte, Mix von VPX 10.8. Der untere Tischbereich (Flipper, Lockbar) geht auf die hinteren Kanäle, die Mitte auf die Seitenkanäle, die Backbox nach vorne. Auf Stereo fällt der erste Start auf Stereo zurück.", "snd_mode_hint": "Der Modus sagt VPX, auf welchen Kanälen die Tischgeräusche spielen (SSF = Surround Sound Feedback).", @@ -746,12 +746,12 @@ "dof_strip_note": "strisce e matrici LED (Teensy, Wemos) si dichiarano nel passo Toys / LED.", "sound3d_0": "Stereo — 2 canali, altoparlanti davanti (consigliato per un'uscita stereo)", "sound3d_1": "Stereo invertito — 2 canali, altoparlanti dietro (backbox)", - "sound3d_2": "SSF 6 canali — exciter sul retro del mobile (lockbar)", - "sound3d_3": "SSF 6 canali — exciter davanti al mobile (lockbar)", + "sound3d_2": "SSF 6 canali — exciter cablati sui canali POSTERIORI della scheda audio", + "sound3d_3": "SSF 6 canali — exciter cablati sui canali ANTERIORI della scheda audio", "sound3d_4": "SSF 6 canali laterali + posteriori — mix storico", "sound3d_5": "SSF 6 canali laterali + posteriori — nuovo mix (VPX 10.8)", "toys_title": "Toy e LED", - "toys_hint": "Le schede riconosciute sono elencate. I controller di strisce indirizzabili (Teensy, Wemos) si dichiarano qui: matrice del backboard o strisce, con il numero di LED per uscita come cablato.", + "toys_hint": "Le schede riconosciute sono elencate. Nulla è precompilato: PinCabOS non conosce la tua backboard. Accendi il controller che pilota i tuoi LED e descrivili — altrimenti lascialo spento e fallo più tardi dalla pagina DOF del cabinato. Un controller spento non blocca l'installazione.", "toys_inputs": "Pulsanti, plunger, nudge", "toys_inputs_none": "nessuna scheda pulsanti vista (solo tastiera)", "toys_inputs_note": "I pulsanti si mappano al primo avvio, in Map Commander: bisogna premerli uno a uno. Senza scheda potrai saltare quel passo.", @@ -763,10 +763,10 @@ "toys_mode": "Funzione", "toys_mode_matrix": "Matrice (backboard)", "toys_mode_strips": "Strisce", - "toys_size": "Larghezza × altezza", + "toys_size": "LED per riga × numero di righe", "toys_arrangement": "Disposizione", "toys_outputs": "LED per uscita", - "toys_outputs_hint_matrix": "Un valore per uscita del controller, nell'ordine del cablaggio; il totale deve essere larghezza × altezza.", + "toys_outputs_hint_matrix": "Un valore per uscita del controller, nell'ordine del cablaggio. Il totale deve essere LED per riga × righe. Un'uscita Teensy pilota al massimo 512 LED: oltre, la matrice si distribuisce su più uscite — e va cablata così.", "toys_outputs_hint_strips": "Un valore per uscita: ogni uscita diventa una striscia (0 = uscita libera).", "toys_total": "Totale:", "toys_color": "Ordine dei colori", @@ -790,8 +790,8 @@ "lecture_hint": "Ruota la visualizzazione solo durante l'installazione, per uno schermo montato in verticale. Nulla viene conservato.", "sound3d_hint_0": "Adatto a qualsiasi uscita stereo (jack, HDMI 2 canali).", "sound3d_hint_1": "Uscita stereo con gli altoparlanti dietro il giocatore.", - "sound3d_hint_2": "Richiede una scheda audio 5.1/7.1 in surround ed exciter cablati sui canali posteriori. Su un'uscita stereo i suoni sarebbero muti: il primo avvio torna in stereo.", - "sound3d_hint_3": "Richiede una scheda audio 5.1/7.1 in surround ed exciter cablati sui canali anteriori. Su un'uscita stereo il primo avvio torna in stereo.", + "sound3d_hint_2": "La domanda riguarda il CABLAGGIO, non la posizione nel mobile: a quale presa della scheda audio sono collegati? Qui, la presa posteriore (spesso nera). Richiede una scheda 5.1/7.1 in surround; su un'uscita stereo il primo avvio torna in stereo. Nel dubbio, il passo Altoparlanti fa parlare ogni canale a turno.", + "sound3d_hint_3": "Stessa domanda, altra presa: qui gli exciter sono sull'uscita anteriore (spesso verde), quella di solito usata per il backbox. Richiede una scheda 5.1/7.1 in surround; su un'uscita stereo il primo avvio torna in stereo. Nel dubbio, il passo Altoparlanti fa parlare ogni canale a turno.", "sound3d_hint_4": "Richiede una scheda 5.1/7.1. La parte bassa del tavolo (flipper, lockbar) va sui canali posteriori, il centro sui laterali, la backbox sull'anteriore. Su un'uscita stereo il primo avvio torna in stereo.", "sound3d_hint_5": "Richiede una scheda 5.1/7.1, mix VPX 10.8. La parte bassa del tavolo (flipper, lockbar) va sui canali posteriori, il centro sui laterali, la backbox sull'anteriore. Su un'uscita stereo il primo avvio torna in stereo.", "snd_mode_hint": "La modalità dice a VPX su quali canali suonare i suoni del tavolo (SSF = Surround Sound Feedback).", @@ -951,12 +951,12 @@ "dof_strip_note": "tiras y matrices LED (Teensy, Wemos) se declaran en el paso Toys / LED.", "sound3d_0": "Estéreo — 2 canales, altavoces delante (recomendado para una salida estéreo)", "sound3d_1": "Estéreo invertido — 2 canales, altavoces detrás (backbox)", - "sound3d_2": "SSF 6 canales — exciters en la parte trasera del mueble (lockbar)", - "sound3d_3": "SSF 6 canales — exciters en la parte delantera del mueble (lockbar)", + "sound3d_2": "SSF 6 canales — exciters conectados a los canales TRASEROS de la tarjeta", + "sound3d_3": "SSF 6 canales — exciters conectados a los canales DELANTEROS de la tarjeta", "sound3d_4": "SSF 6 canales laterales + traseros — mezcla clásica", "sound3d_5": "SSF 6 canales laterales + traseros — nueva mezcla (VPX 10.8)", "toys_title": "Toys y LED", - "toys_hint": "Las tarjetas reconocidas se listan. Los controladores de tiras direccionables (Teensy, Wemos) se declaran aquí: matriz del backboard o tiras, con el número de LED por salida tal como se cablearon.", + "toys_hint": "Se listan las tarjetas reconocidas. Nada está rellenado: PinCabOS no conoce tu backboard. Enciende el controlador que gobierna tus LED y descríbelos — si no, déjalo apagado y hazlo más tarde desde la página DOF del mueble. Un controlador apagado no bloquea la instalación.", "toys_inputs": "Botones, plunger, nudge", "toys_inputs_none": "ninguna tarjeta de botones vista (solo teclado)", "toys_inputs_note": "Los botones se asignan en el primer arranque, en Map Commander: hay que pulsar cada uno. Sin tarjeta podrás saltar ese paso.", @@ -968,10 +968,10 @@ "toys_mode": "Función", "toys_mode_matrix": "Matriz (backboard)", "toys_mode_strips": "Tiras", - "toys_size": "Ancho × alto", + "toys_size": "LED por fila × número de filas", "toys_arrangement": "Disposición", "toys_outputs": "LED por salida", - "toys_outputs_hint_matrix": "Un valor por salida del controlador, en el orden del cableado; el total debe ser ancho × alto.", + "toys_outputs_hint_matrix": "Un valor por salida del controlador, en orden de cableado. El total debe ser LED por fila × filas. Una salida Teensy gobierna como mucho 512 LED: por encima, la matriz se reparte en varias salidas — y así debe estar cableada.", "toys_outputs_hint_strips": "Un valor por salida: cada salida se convierte en una tira (0 = salida libre).", "toys_total": "Total:", "toys_color": "Orden de colores", @@ -995,8 +995,8 @@ "lecture_hint": "Gira la pantalla solo durante la instalación, para un panel montado en vertical. No se conserva nada.", "sound3d_hint_0": "Vale para cualquier salida estéreo (jack, HDMI 2 canales).", "sound3d_hint_1": "Salida estéreo con los altavoces detrás del jugador.", - "sound3d_hint_2": "Necesita una tarjeta 5.1/7.1 en surround y exciters cableados en los canales traseros. En una salida estéreo los sonidos quedarían mudos: el primer arranque vuelve a estéreo.", - "sound3d_hint_3": "Necesita una tarjeta 5.1/7.1 en surround y exciters cableados en los canales delanteros. En una salida estéreo el primer arranque vuelve a estéreo.", + "sound3d_hint_2": "La pregunta es sobre el CABLEADO, no sobre dónde están en el mueble: ¿en qué toma de la tarjeta están conectados? Aquí, la toma trasera (a menudo negra). Requiere una tarjeta 5.1/7.1 en surround; en una salida estéreo el primer arranque vuelve a estéreo. Ante la duda, el paso Altavoces hace hablar cada canal por turno.", + "sound3d_hint_3": "Misma pregunta, otra toma: aquí los exciters están en la salida delantera (a menudo verde), la que suele usarse para el backbox. Requiere una tarjeta 5.1/7.1 en surround; en una salida estéreo el primer arranque vuelve a estéreo. Ante la duda, el paso Altavoces hace hablar cada canal por turno.", "sound3d_hint_4": "Necesita una tarjeta 5.1/7.1. La parte baja de la mesa (flippers, lockbar) va a los canales traseros, el centro a los laterales, la backbox al delantero. En una salida estéreo el primer arranque vuelve a estéreo.", "sound3d_hint_5": "Necesita una tarjeta 5.1/7.1, mezcla VPX 10.8. La parte baja de la mesa (flippers, lockbar) va a los canales traseros, el centro a los laterales, la backbox al delantero. En una salida estéreo el primer arranque vuelve a estéreo.", "snd_mode_hint": "El modo indica a VPX en qué canales sonar los sonidos de la mesa (SSF = Surround Sound Feedback).", diff --git a/opt/pincabos/tests/test_installer_toys.py b/opt/pincabos/tests/test_installer_toys.py index eebf422f..4e0bdef4 100644 --- a/opt/pincabos/tests/test_installer_toys.py +++ b/opt/pincabos/tests/test_installer_toys.py @@ -21,20 +21,48 @@ ] +def decrit(): + """Ce qu un utilisateur SAISIT : une matrice sur la Teensy, des rubans sur la + Wemos. La proposition, elle, n avance plus rien + (PINCABOS_TOYS_SANS_SUPPOSITION_V1).""" + p = pd.proposer_toys(DET) + p["controllers"][0].update({"enabled": True, "mode": "matrice", "width": 144, + "height": 16, "strips": pd.repartir(144 * 16)}) + p["controllers"][1].update({"enabled": True, "mode": "rubans", "strips": [144]}) + return p + + class Declaration(unittest.TestCase): def test_tri_des_cartes(self): self.assertEqual([c["type"] for c in pd.controleurs_de_rubans(DET)], ["TeensyStripController", "WemosD1MPStripController"]) self.assertEqual([c["kind"] for c in pd.cartes_auto(DET)], ["DudesCab"]) def test_repartition_et_proposition(self): - self.assertEqual(pd.repartir(144 * 16), [512, 512, 512, 512, 256]) # le backboard de Yann + # la decoupe en sorties pleines sert toujours, quand l utilisateur + # decrit VRAIMENT sa matrice : une sortie de Teensy s arrete a 512 LED + self.assertEqual(pd.repartir(144 * 16), [512, 512, 512, 512, 256]) self.assertEqual(pd.repartir(0), []) + # PINCABOS_TOYS_SANS_SUPPOSITION_V1 : la proposition ne decrit plus le + # cabinet a la place de l utilisateur. Avant, le premier controleur + # arrivait en « matrice 144x16 » avec les sorties qui vont avec : les + # deux valeurs etant coherentes entre elles, la validation passait et on + # emportait la description du cabinet de quelqu un d autre (backboard de + # Patrick a 512 LED allumees sur 1368, 09/09/2026). p = pd.proposer_toys(DET)["controllers"] - self.assertEqual((p[0]["mode"], p[0]["width"], p[0]["height"], p[0]["ledwiz_number"]), ("matrice", 144, 16, 30)) - self.assertEqual((p[1]["mode"], p[1]["strips"], p[1]["ledwiz_number"]), ("rubans", [144], 31)) + self.assertEqual(len(p), 2, "les cartes detectees restent listees") + for c in p: + self.assertFalse(c["enabled"], "un controleur inconnu arrive eteint") + self.assertEqual((c["width"], c["height"], c["strips"]), (0, 0, [])) + self.assertEqual((p[0]["ledwiz_number"], p[1]["ledwiz_number"]), (30, 31)) + + def test_un_controleur_eteint_ne_bloque_pas(self): + # le navigateur sautait deja les controleurs eteints (toysProblems) ; + # le validateur du serveur, lui, refusait width=0. L ecart ne se voyait + # pas tant que la proposition arrivait allumee ET valide. + self.assertEqual(pd.valider_toys(pd.proposer_toys(DET), DET)[0], []) def test_validation(self): - p = pd.proposer_toys(DET) + p = decrit() self.assertEqual(pd.valider_toys(p, DET)[0], []) mauvais = json.loads(json.dumps(p)); mauvais["controllers"][0]["strips"] = [512, 512] self.assertTrue(any("2304" in e for e in pd.valider_toys(mauvais, DET)[0])) @@ -47,7 +75,7 @@ def test_validation(self): self.assertTrue(pd.valider_toys("rien", DET)[0]) def test_inventaire(self): - _, ok = pd.valider_toys(pd.proposer_toys(DET), DET) + _, ok = pd.valider_toys(decrit(), DET) inv = pd.inventaire_json(ok, DET) d0, d1 = inv["devices"] self.assertEqual((d0["type"], d0["serial"], d0["leds_per_strip"][:5], d0["toy"]["name"], d0["ledwiz_outputs"]), @@ -89,7 +117,7 @@ def test_premier_demarrage(self): tmp = Path(tempfile.mkdtemp()) try: mod = pd.outil(R / "opt/pincabos/tools/dof-cabinet/dof-cabinet.py") - _, ok = pd.valider_toys(pd.proposer_toys(DET), DET) + _, ok = pd.valider_toys(decrit(), DET) inv = pd.inventaire_json(ok, DET) for dev in inv["devices"]: dev["com_port"] = "/dev/ttyACM9" @@ -181,7 +209,12 @@ def test_installation_ecrit_l_inventaire(self): self.assertIn("PCO_ANS_TOYS_FILE=" + str(self.tmp / "gui-toys.json"), (self.tmp / "gui-answers.env").read_text(encoding="utf-8")) inv = json.loads((self.tmp / "gui-toys.json").read_text(encoding="utf-8")) self.assertEqual(len(inv["devices"]), 2); self.assertEqual(inv["source"], "PinCabOS installer") - mauvais = d["proposition"]; mauvais["controllers"][0]["strips"] = [1] + # un controleur ETEINT n est plus valide (il n a rien a decrire) : pour + # verifier que le serveur refuse bien une saisie incoherente, il faut + # l allumer — c est le cas reel, celui de l utilisateur qui se trompe. + mauvais = d["proposition"] + mauvais["controllers"][0].update({"enabled": True, "mode": "matrice", + "width": 144, "height": 16, "strips": [1]}) r = self.client.post("/api/install", json={"lang": "fr", "mode": "1", "disk": "/dev/nvme0n1", "confirm": "INSTALL PINCABOS", "network": False, "toys": mauvais}) self.assertEqual(r.status_code, 400); self.assertEqual(r.get_json()["error"], "bad-toys") diff --git a/opt/pincabos/tests/test_toys_sans_supposition.py b/opt/pincabos/tests/test_toys_sans_supposition.py new file mode 100755 index 00000000..64f78a3a --- /dev/null +++ b/opt/pincabos/tests/test_toys_sans_supposition.py @@ -0,0 +1,121 @@ +"""L'assistant ne decrit pas le cabinet a votre place (PINCABOS_TOYS_SANS_SUPPOSITION_V1). + +Remonte par Patrick le 09/09/2026 : sa backboard n allumait que 512 LED sur +1368. L etape Toys proposait d office, pour le premier controleur detecte : + + mode = matrice, width = 144, height = 16, strips = repartir(2304) + -> [512, 512, 512, 512, 256] + +Les deux valeurs etant coherentes ENTRE ELLES, le controle « le total doit +faire largeur x hauteur » passait sans rien dire. On validait, sans le voir, la +description du cabinet de quelqu un d autre — et la premiere sortie n eclairait +que ses 512 premieres LED. + +512 n est pas arbitraire : c est le maximum qu une sortie de Teensy pilote. Une +matrice plus grande DOIT etre repartie sur plusieurs sorties, et cablee ainsi. +""" +import importlib.util +import json +import unittest +from pathlib import Path + +from _charge import RACINE + +R = Path(RACINE) +OUTIL = R / "opt/pincabos/tools/pincabos_dof.py" +I18N = R / "opt/pincabos/installer-gui/i18n.json" +LANGUES = ("fr", "en", "de", "it", "es") + + +def _module(): + spec = importlib.util.spec_from_file_location("pincabos_dof", OUTIL) + m = importlib.util.module_from_spec(spec) + spec.loader.exec_module(m) + return m + + +# une Teensy et une Wemos, comme chez Patrick +DETECTES = [ + {"dev": "/dev/ttyACM1", "serial": "TEENSY123", "auto_config": False, + "kind": "TeensyStripController (strip adressable)"}, + {"dev": "/dev/ttyUSB0", "serial": "", "auto_config": False, + "kind": "Wemos D1 / ESP via CH340 (WemosD1MPStripController possible)"}, +] + + +class RienNEstInvente(unittest.TestCase): + def setUp(self): + self.m = _module() + self.ctrls = self.m.proposer_toys(DETECTES)["controllers"] + + def test_les_controleurs_sont_bien_listes(self): + self.assertEqual(len(self.ctrls), 2, "les cartes detectees doivent apparaitre") + + def test_aucun_n_arrive_allume(self): + # un controleur allume mais mal decrit part sur le disque en silence + for c in self.ctrls: + self.assertFalse(c["enabled"], "%s ne doit pas etre allume d office" % c.get("type")) + + def test_aucune_dimension_inventee(self): + for c in self.ctrls: + self.assertEqual(c["width"], 0) + self.assertEqual(c["height"], 0) + self.assertEqual(c["strips"], [], "aucune LED ne doit etre supposee") + + def test_plus_de_matrice_144x16_par_defaut(self): + texte = OUTIL.read_text(encoding="utf-8") + corps = texte.split("def proposer_toys", 1)[1].split("\ndef ", 1)[0] + for invente in ('"width": 144', '"height": 16', "repartir(144"): + self.assertNotIn(invente, corps, "l assistant suppose encore " + invente) + + +class LaDecoupeEnSortiesResteJuste(unittest.TestCase): + """`repartir` sert toujours, quand l utilisateur decrit VRAIMENT sa matrice.""" + + def test_512_par_sortie(self): + m = _module() + self.assertEqual(m.repartir(2304), [512, 512, 512, 512, 256]) + self.assertEqual(m.repartir(1368), [512, 512, 344], "cas de Patrick : 19 x 72") + self.assertEqual(m.repartir(0), []) + + +class LesLibellesDisentCeQuOnDemande(unittest.TestCase): + def setUp(self): + self.d = json.loads(I18N.read_text(encoding="utf-8")) + + def test_toutes_les_langues_suivent(self): + tailles = {k: len(v) for k, v in self.d.items()} + self.assertEqual(len(set(tailles.values())), 1, "les langues ont divergé : %s" % tailles) + + def test_l_audio_parle_de_cablage_pas_de_place(self): + # Patrick : « Exciters à l'arrière du meuble ou à l'avant du meuble ? » + # Les deux libelles disaient « (lockbar) » — or la lockbar est un seul + # endroit, a l avant. La vraie question est : sur quels canaux de la + # carte son les exciters sont-ils cables ? + for lang in LANGUES: + for cle in ("sound3d_2", "sound3d_3"): + v = self.d[lang][cle] + self.assertNotIn("lockbar", v.lower(), + "%s/%s decrit encore une place dans le meuble" % (lang, cle)) + self.assertIn("ARRIÈRE", self.d["fr"]["sound3d_2"]) + self.assertIn("AVANT", self.d["fr"]["sound3d_3"]) + self.assertIn("REAR", self.d["en"]["sound3d_2"]) + self.assertIn("FRONT", self.d["en"]["sound3d_3"]) + + def test_les_aides_audio_renvoient_au_test_des_haut_parleurs(self): + for lang in LANGUES: + for cle in ("sound3d_hint_2", "sound3d_hint_3"): + self.assertGreater(len(self.d[lang][cle]), 120, + "%s/%s reste trop court pour lever le doute" % (lang, cle)) + + def test_l_aide_matrice_annonce_la_limite_de_512(self): + # personne ne devine qu une sortie de Teensy s arrete a 512 LED + for lang in LANGUES: + self.assertIn("512", self.d[lang]["toys_outputs_hint_matrix"], + "%s : la limite par sortie n est pas dite" % lang) + + def test_la_page_toys_previent_que_rien_n_est_prerempli(self): + for lang, mot in (("fr", "prérempli"), ("en", "pre-filled"), ("de", "vorausgefüllt"), + ("it", "precompilato"), ("es", "rellenado")): + self.assertIn(mot, self.d[lang]["toys_hint"], + "%s : l utilisateur n est pas prevenu" % lang) diff --git a/opt/pincabos/tools/pincabos_dof.py b/opt/pincabos/tools/pincabos_dof.py index 6a3261a0..4cd867ab 100644 --- a/opt/pincabos/tools/pincabos_dof.py +++ b/opt/pincabos/tools/pincabos_dof.py @@ -187,17 +187,26 @@ def repartir(total: int, par_sortie: int = 512) -> list: def proposer_toys(detectes: list) -> dict: - """Premier contrôleur = backboard (matrice 144×16, le cas courant), les suivants = rubans.""" + """Liste les contrôleurs détectés, sans rien préremplir. + + PINCABOS_TOYS_SANS_SUPPOSITION_V1 — remonté par Patrick le 09/09/2026 : sa + backboard n'allumait que 512 LED sur 1368. L'assistant proposait d'office + « premier contrôleur = matrice 144×16 » et répartissait ces 2304 LED en + sorties pleines (512, 512, 512, 512, 256). Les deux valeurs étant cohérentes + ENTRE ELLES, le contrôle « le total doit faire largeur × hauteur » passait : + on validait, sans le voir, la description du cabinet de quelqu'un d'autre. + + Un contrôleur dont on ne sait rien arrive donc ÉTEINT, sans dimensions. + Celui qui a une backboard l'allume et décrit son matériel ; celui qui ne + sait pas encore laisse éteint et le fera depuis la page DOF du cabinet. + Dans les deux cas, aucune configuration inventée ne part sur le disque. + """ ctrls = [] for i, c in enumerate(controleurs_de_rubans(detectes)): - base = {"serial": c["serial"], "type": c["type"], "enabled": True, "ledwiz_number": 30 + i, - "brightness": 25, "color_order": "GRB"} - if i == 0: - base.update({"mode": "matrice", "width": 144, "height": 16, "arrangement": "TopDownAlternateLeftRight", - "strips": repartir(144 * 16)}) - else: - base.update({"mode": "rubans", "width": 0, "height": 0, "arrangement": "LeftRightTopDown", "strips": [144]}) - ctrls.append(base) + ctrls.append({"serial": c["serial"], "type": c["type"], "enabled": False, + "ledwiz_number": 30 + i, "brightness": 25, "color_order": "GRB", + "mode": "matrice", "width": 0, "height": 0, + "arrangement": "TopDownAlternateLeftRight", "strips": []}) return {"controllers": ctrls} @@ -246,9 +255,16 @@ def valider_toys(choix, detectes: list) -> tuple[list, dict]: if prop["arrangement"] not in ARRANGEMENTS: erreurs.append(f"contrôleur {i + 1} : arrangement inconnu") if mode == "matrice": - prop["width"], b1 = _entier(c.get("width", 144), 144, 1, 1024) - prop["height"], b2 = _entier(c.get("height", 16), 16, 1, 1024) - if not (b1 and b2): + # PINCABOS_TOYS_SANS_SUPPOSITION_V1 : zero est desormais une reponse + # valide — c est celle d un controleur qu on laisse eteint parce + # qu on ne connait pas encore son cablage. La contrainte 1..1024 ne + # vaut que s il est allume. Avant, le navigateur sautait bien les + # controleurs eteints (toysProblems) mais pas le validateur du + # serveur : l ecart ne se voyait pas tant que la proposition + # arrivait allumee ET valide. + prop["width"], b1 = _entier(c.get("width", 0), 0, 0, 1024) + prop["height"], b2 = _entier(c.get("height", 0), 0, 0, 1024) + if prop["enabled"] and not (b1 and b2 and prop["width"] and prop["height"]): erreurs.append(f"contrôleur {i + 1} : dimensions de matrice hors de 1..1024") elif prop["enabled"] and sum(strips_ok) != prop["width"] * prop["height"]: erreurs.append(f"contrôleur {i + 1} : {sum(strips_ok)} LEDs sur les sorties pour une matrice de {prop['width']}×{prop['height']} = {prop['width'] * prop['height']}")