From b60d3ea63e924a88a220350bc05545e0493a4a85 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Wed, 19 Aug 2026 08:24:06 +0200 Subject: [PATCH] Implement led page --- evcharger-app/CMakeLists.txt | 1 + evcharger-app/i18n/qml_da.ts | 210 ++++++++++++- evcharger-app/i18n/qml_de.ts | 214 ++++++++++++- evcharger-app/i18n/qml_fi.ts | 210 ++++++++++++- evcharger-app/i18n/qml_nb.ts | 210 ++++++++++++- evcharger-app/i18n/qml_sv.ts | 210 ++++++++++++- evcharger-app/i18n/qml_tr.ts | 210 ++++++++++++- evcharger-app/qml/LedPage.qml | 431 ++++++++++++++++++++++++++- evcharger-app/qml/LedRingPreview.qml | 224 ++++++++++++++ evcharger-app/qml/qmldir | 1 + 10 files changed, 1910 insertions(+), 11 deletions(-) create mode 100644 evcharger-app/qml/LedRingPreview.qml diff --git a/evcharger-app/CMakeLists.txt b/evcharger-app/CMakeLists.txt index 6d615b7..a76fee8 100644 --- a/evcharger-app/CMakeLists.txt +++ b/evcharger-app/CMakeLists.txt @@ -121,6 +121,7 @@ qt_add_qml_module(evcharger-app qml/KwhLimitPage.qml qml/LanguageSelector.qml qml/LedPage.qml + qml/LedRingPreview.qml qml/LicensesPage.qml qml/LoadBalancingPage.qml qml/LogicModeButton.qml diff --git a/evcharger-app/i18n/qml_da.ts b/evcharger-app/i18n/qml_da.ts index 3e47b50..5e0c538 100644 --- a/evcharger-app/i18n/qml_da.ts +++ b/evcharger-app/i18n/qml_da.ts @@ -2878,10 +2878,218 @@ LedPage - + LED LED + + + + + + Ready + Klar + + + + Color shown while the charger is ready and no car is connected. + Farve, der vises, når laderen er klar, og ingen bil er tilsluttet. + + + + + + + Waiting for car + Venter på bil + + + + Color shown while the charger is waiting for the vehicle. + Farve, der vises, mens laderen venter på køretøjet. + + + + + + + Charging + Oplader + + + + Color of the rotating tails while the vehicle is charging. + Farven på de roterende lyshaler, mens køretøjet oplades. + + + + + + + Finished + Fuldført + + + + Color shown when charging has finished. + Farve, der vises, når opladningen er fuldført. + + + + Live device + Direkte fra enheden + + + + Live LED ring + LED-ring i realtid + + + + No animation data + Ingen animationsdata + + + + Waiting for authorization + Venter på godkendelse + + + + Rainbow + Regnbue + + + + Pulsating + Pulserende + + + + Firmware update + Firmwareopdatering + + + + Firmware update finished + Firmwareopdateringen er fuldført + + + + Firmware update failed + Firmwareopdateringen mislykkedes + + + + Configured preview + Konfigureret forhåndsvisning + + + + Adjust how the LED ring communicates charging states and preview the result before using it on the charger. + Juster, hvordan LED-ringen viser opladningstilstande, og forhåndsvis resultatet, før du bruger det på laderen. + + + + LED ring preview + Forhåndsvisning af LED-ring + + + + Preview + Forhåndsvisning + + + + The live preview follows the animation reported by the charger. Select another state to preview its configured color; the charging preview includes the rotating tails. + Forhåndsvisningen i realtid følger den animation, som laderen rapporterer. Vælg en anden tilstand for at forhåndsvise dens konfigurerede farve; opladningsforhåndsvisningen indeholder de roterende lyshaler. + + + + Brightness + Lysstyrke + + + + LED brightness + LED-lysstyrke + + + + + %1% + %1% + + + + Lower brightness is less distracting at night. Set it to zero to turn the ring off completely. + Lavere lysstyrke er mindre forstyrrende om natten. Indstil den til nul for at slukke ringen helt. + + + + Status colors + Statusfarver + + + + Tap a charging state to choose the color used by the LED ring. + Tryk på en opladningstilstand for at vælge den farve, LED-ringen bruger. + + + + Energy saving + Energibesparelse + + + + Energy saving on + Energibesparelse til + + + + Energy saving off + Energibesparelse fra + + + + Energy saving turns off status lighting when it is not needed. Charging and important warnings remain visible. + Energibesparelse slukker statusbelysningen, når den ikke er nødvendig. Opladning og vigtige advarsler forbliver synlige. + + + + Keep mode indicators on + Behold tilstandsindikatorerne tændt + + + + Keep the small charging-mode indicators visible while the LED ring is saving energy. + Behold de små opladningstilstandsindikatorer synlige, mens LED-ringen sparer energi. + + + + Service diagnostics + Servicediagnostik + + + + These service options can replace normal status animations. Leave them off unless you are diagnosing the LED hardware. + Disse serviceindstillinger kan erstatte normale statusanimationer. Lad dem være slået fra, medmindre du diagnosticerer LED-hardwaren. + + + + Factory LED test + Fabrikkens LED-test + + + + LED diagnostics + LED-diagnostik + + + + Choose color for %1 + Vælg farve til %1 + LicensesPage diff --git a/evcharger-app/i18n/qml_de.ts b/evcharger-app/i18n/qml_de.ts index 0649dd1..de4f5a9 100644 --- a/evcharger-app/i18n/qml_de.ts +++ b/evcharger-app/i18n/qml_de.ts @@ -3053,10 +3053,218 @@ LedPage - + LED LED + + + + + + Ready + Bereit + + + + Color shown while the charger is ready and no car is connected. + Farbe, die angezeigt wird, wenn der Charger bereit und kein Auto verbunden ist. + + + + + + + Waiting for car + Warte auf Auto + + + + Color shown while the charger is waiting for the vehicle. + Farbe, die angezeigt wird, während der Charger auf das Fahrzeug wartet. + + + + + + + Charging + Lädt + + + + Color of the rotating tails while the vehicle is charging. + Farbe der rotierenden Schweife, während das Fahrzeug lädt. + + + + + + + Finished + Beendet + + + + Color shown when charging has finished. + Farbe, die nach Abschluss des Ladevorgangs angezeigt wird. + + + + Live device + Live-Gerät + + + + Live LED ring + Live-LED-Ring + + + + No animation data + Keine Animationsdaten + + + + Waiting for authorization + Warte auf Autorisierung + + + + Rainbow + Regenbogen + + + + Pulsating + Pulsierend + + + + Firmware update + Firmware-Update + + + + Firmware update finished + Firmware-Update abgeschlossen + + + + Firmware update failed + Firmware-Update fehlgeschlagen + + + + Configured preview + Konfigurierte Vorschau + + + + Adjust how the LED ring communicates charging states and preview the result before using it on the charger. + Passe an, wie der LED-Ring Ladezustände anzeigt, und prüfe das Ergebnis in der Vorschau, bevor du es am Charger verwendest. + + + + LED ring preview + LED-Ring-Vorschau + + + + Preview + Vorschau + + + + The live preview follows the animation reported by the charger. Select another state to preview its configured color; the charging preview includes the rotating tails. + Die Live-Vorschau folgt der vom Charger gemeldeten Animation. Wähle einen anderen Zustand, um dessen konfigurierte Farbe zu prüfen; die Ladevorschau enthält die rotierenden Schweife. + + + + Brightness + Helligkeit + + + + LED brightness + LED-Helligkeit + + + + + %1% + %1% + + + + Lower brightness is less distracting at night. Set it to zero to turn the ring off completely. + Geringere Helligkeit stört nachts weniger. Stelle sie auf null, um den Ring vollständig auszuschalten. + + + + Status colors + Statusfarben + + + + Tap a charging state to choose the color used by the LED ring. + Tippe auf einen Ladezustand, um die vom LED-Ring verwendete Farbe auszuwählen. + + + + Energy saving + Energiesparen + + + + Energy saving on + Energiesparen ein + + + + Energy saving off + Energiesparen aus + + + + Energy saving turns off status lighting when it is not needed. Charging and important warnings remain visible. + Beim Energiesparen wird die Statusbeleuchtung ausgeschaltet, wenn sie nicht benötigt wird. Laden und wichtige Warnungen bleiben sichtbar. + + + + Keep mode indicators on + Modusanzeigen eingeschaltet lassen + + + + Keep the small charging-mode indicators visible while the LED ring is saving energy. + Lasse die kleinen Lademodus-Anzeigen sichtbar, während der LED-Ring Energie spart. + + + + Service diagnostics + Servicediagnose + + + + These service options can replace normal status animations. Leave them off unless you are diagnosing the LED hardware. + Diese Serviceoptionen können normale Statusanimationen ersetzen. Lasse sie ausgeschaltet, sofern du nicht die LED-Hardware diagnostizierst. + + + + Factory LED test + Werkseitiger LED-Test + + + + LED diagnostics + LED-Diagnose + + + + Choose color for %1 + Farbe für %1 wählen + LicensesPage @@ -3382,7 +3590,7 @@ Charging - + Lädt @@ -4771,7 +4979,7 @@ Finished - + Beendet diff --git a/evcharger-app/i18n/qml_fi.ts b/evcharger-app/i18n/qml_fi.ts index 3a0e580..9866f2a 100644 --- a/evcharger-app/i18n/qml_fi.ts +++ b/evcharger-app/i18n/qml_fi.ts @@ -2878,10 +2878,218 @@ LedPage - + LED LED + + + + + + Ready + Valmiina + + + + Color shown while the charger is ready and no car is connected. + Väri, joka näytetään, kun laturi on valmiina eikä autoa ole yhdistetty. + + + + + + + Waiting for car + Odottaa autoa + + + + Color shown while the charger is waiting for the vehicle. + Väri, joka näytetään, kun laturi odottaa ajoneuvoa. + + + + + + + Charging + Lataus + + + + Color of the rotating tails while the vehicle is charging. + Pyörivien valovanojen väri ajoneuvon latauksen aikana. + + + + + + + Finished + Lataus valmis + + + + Color shown when charging has finished. + Väri, joka näytetään latauksen valmistuttua. + + + + Live device + Laite reaaliajassa + + + + Live LED ring + Reaaliaikainen LED-rengas + + + + No animation data + Ei animaatiotietoja + + + + Waiting for authorization + Odotetaan valtuutusta + + + + Rainbow + Sateenkaari + + + + Pulsating + Sykkivä + + + + Firmware update + Laiteohjelmistopäivitys + + + + Firmware update finished + Laiteohjelmistopäivitys valmis + + + + Firmware update failed + Laiteohjelmistopäivitys epäonnistui + + + + Configured preview + Määritetty esikatselu + + + + Adjust how the LED ring communicates charging states and preview the result before using it on the charger. + Säädä, miten LED-rengas ilmaisee lataustilat, ja esikatsele tulos ennen sen käyttämistä laturissa. + + + + LED ring preview + LED-renkaan esikatselu + + + + Preview + Esikatselu + + + + The live preview follows the animation reported by the charger. Select another state to preview its configured color; the charging preview includes the rotating tails. + Reaaliaikainen esikatselu seuraa laturin ilmoittamaa animaatiota. Valitse toinen tila nähdäksesi sen määritetyn värin; latauksen esikatselu sisältää pyörivät valovanat. + + + + Brightness + Kirkkaus + + + + LED brightness + LED-kirkkaus + + + + + %1% + %1% + + + + Lower brightness is less distracting at night. Set it to zero to turn the ring off completely. + Pienempi kirkkaus häiritsee vähemmän yöllä. Aseta arvoksi nolla sammuttaaksesi renkaan kokonaan. + + + + Status colors + Tilavärit + + + + Tap a charging state to choose the color used by the LED ring. + Valitse LED-renkaan käyttämä väri napauttamalla lataustilaa. + + + + Energy saving + Energiansäästö + + + + Energy saving on + Energiansäästö käytössä + + + + Energy saving off + Energiansäästö pois käytöstä + + + + Energy saving turns off status lighting when it is not needed. Charging and important warnings remain visible. + Energiansäästö sammuttaa tilavalot, kun niitä ei tarvita. Lataus ja tärkeät varoitukset pysyvät näkyvissä. + + + + Keep mode indicators on + Pidä tilan merkkivalot päällä + + + + Keep the small charging-mode indicators visible while the LED ring is saving energy. + Pidä pienet lataustilan merkkivalot näkyvissä, kun LED-rengas säästää energiaa. + + + + Service diagnostics + Huoltodiagnostiikka + + + + These service options can replace normal status animations. Leave them off unless you are diagnosing the LED hardware. + Nämä huoltoasetukset voivat korvata normaalit tila-animaatiot. Jätä ne pois käytöstä, ellet diagnosoi LED-laitteistoa. + + + + Factory LED test + Tehtaan LED-testi + + + + LED diagnostics + LED-diagnostiikka + + + + Choose color for %1 + Valitse kohteen %1 väri + LicensesPage diff --git a/evcharger-app/i18n/qml_nb.ts b/evcharger-app/i18n/qml_nb.ts index 819c27f..ccd0563 100644 --- a/evcharger-app/i18n/qml_nb.ts +++ b/evcharger-app/i18n/qml_nb.ts @@ -2878,10 +2878,218 @@ LedPage - + LED LED + + + + + + Ready + Klar + + + + Color shown while the charger is ready and no car is connected. + Farge som vises når laderen er klar og ingen bil er tilkoblet. + + + + + + + Waiting for car + Venter på bil + + + + Color shown while the charger is waiting for the vehicle. + Farge som vises mens laderen venter på kjøretøyet. + + + + + + + Charging + Lader + + + + Color of the rotating tails while the vehicle is charging. + Fargen på de roterende lyshalene mens kjøretøyet lades. + + + + + + + Finished + Fullført + + + + Color shown when charging has finished. + Farge som vises når ladingen er fullført. + + + + Live device + Direkte fra enheten + + + + Live LED ring + LED-ring i sanntid + + + + No animation data + Ingen animasjonsdata + + + + Waiting for authorization + Venter på godkjenning + + + + Rainbow + Regnbue + + + + Pulsating + Pulserende + + + + Firmware update + Fastvareoppdatering + + + + Firmware update finished + Fastvareoppdateringen er fullført + + + + Firmware update failed + Fastvareoppdateringen mislyktes + + + + Configured preview + Konfigurert forhåndsvisning + + + + Adjust how the LED ring communicates charging states and preview the result before using it on the charger. + Juster hvordan LED-ringen viser ladestatus, og forhåndsvis resultatet før du bruker det på laderen. + + + + LED ring preview + Forhåndsvisning av LED-ring + + + + Preview + Forhåndsvisning + + + + The live preview follows the animation reported by the charger. Select another state to preview its configured color; the charging preview includes the rotating tails. + Forhåndsvisningen i sanntid følger animasjonen som laderen rapporterer. Velg en annen tilstand for å forhåndsvise den konfigurerte fargen; ladeforhåndsvisningen inkluderer de roterende lyshalene. + + + + Brightness + Lysstyrke + + + + LED brightness + LED-lysstyrke + + + + + %1% + %1% + + + + Lower brightness is less distracting at night. Set it to zero to turn the ring off completely. + Lavere lysstyrke er mindre forstyrrende om natten. Sett den til null for å slå av ringen helt. + + + + Status colors + Statusfarger + + + + Tap a charging state to choose the color used by the LED ring. + Trykk på en ladestatus for å velge fargen som LED-ringen bruker. + + + + Energy saving + Energisparing + + + + Energy saving on + Energisparing på + + + + Energy saving off + Energisparing av + + + + Energy saving turns off status lighting when it is not needed. Charging and important warnings remain visible. + Energisparing slår av statusbelysningen når den ikke trengs. Lading og viktige advarsler forblir synlige. + + + + Keep mode indicators on + Behold modusindikatorene på + + + + Keep the small charging-mode indicators visible while the LED ring is saving energy. + Behold de små lademodusindikatorene synlige mens LED-ringen sparer energi. + + + + Service diagnostics + Servicediagnostikk + + + + These service options can replace normal status animations. Leave them off unless you are diagnosing the LED hardware. + Disse servicealternativene kan erstatte normale statusanimasjoner. La dem være av med mindre du feilsøker LED-maskinvaren. + + + + Factory LED test + LED-test fra fabrikk + + + + LED diagnostics + LED-diagnostikk + + + + Choose color for %1 + Velg farge for %1 + LicensesPage diff --git a/evcharger-app/i18n/qml_sv.ts b/evcharger-app/i18n/qml_sv.ts index 7af3d13..13be1b2 100644 --- a/evcharger-app/i18n/qml_sv.ts +++ b/evcharger-app/i18n/qml_sv.ts @@ -2878,10 +2878,218 @@ LedPage - + LED LED + + + + + + Ready + Klar + + + + Color shown while the charger is ready and no car is connected. + Färg som visas när laddaren är klar och ingen bil är ansluten. + + + + + + + Waiting for car + Väntar på bil + + + + Color shown while the charger is waiting for the vehicle. + Färg som visas medan laddaren väntar på fordonet. + + + + + + + Charging + Laddar + + + + Color of the rotating tails while the vehicle is charging. + Färgen på de roterande ljussvansarna medan fordonet laddas. + + + + + + + Finished + Slutförd + + + + Color shown when charging has finished. + Färg som visas när laddningen är slutförd. + + + + Live device + Aktuell enhet + + + + Live LED ring + LED-ring i realtid + + + + No animation data + Inga animationsdata + + + + Waiting for authorization + Väntar på auktorisering + + + + Rainbow + Regnbåge + + + + Pulsating + Pulserande + + + + Firmware update + Firmwareuppdatering + + + + Firmware update finished + Firmwareuppdateringen är slutförd + + + + Firmware update failed + Firmwareuppdateringen misslyckades + + + + Configured preview + Konfigurerad förhandsvisning + + + + Adjust how the LED ring communicates charging states and preview the result before using it on the charger. + Anpassa hur LED-ringen visar laddningsstatus och förhandsgranska resultatet innan du använder det på laddaren. + + + + LED ring preview + Förhandsvisning av LED-ring + + + + Preview + Förhandsvisning + + + + The live preview follows the animation reported by the charger. Select another state to preview its configured color; the charging preview includes the rotating tails. + Förhandsvisningen i realtid följer animationen som laddaren rapporterar. Välj ett annat tillstånd för att förhandsgranska dess konfigurerade färg; laddningsförhandsvisningen innehåller de roterande ljussvansarna. + + + + Brightness + Ljusstyrka + + + + LED brightness + LED-ljusstyrka + + + + + %1% + %1% + + + + Lower brightness is less distracting at night. Set it to zero to turn the ring off completely. + Lägre ljusstyrka stör mindre på natten. Ställ in den på noll för att stänga av ringen helt. + + + + Status colors + Statusfärger + + + + Tap a charging state to choose the color used by the LED ring. + Tryck på ett laddningstillstånd för att välja färgen som LED-ringen använder. + + + + Energy saving + Energisparläge + + + + Energy saving on + Energisparläge på + + + + Energy saving off + Energisparläge av + + + + Energy saving turns off status lighting when it is not needed. Charging and important warnings remain visible. + Energisparläget stänger av statusbelysningen när den inte behövs. Laddning och viktiga varningar förblir synliga. + + + + Keep mode indicators on + Behåll lägesindikatorerna tända + + + + Keep the small charging-mode indicators visible while the LED ring is saving energy. + Behåll de små laddningslägesindikatorerna synliga medan LED-ringen sparar energi. + + + + Service diagnostics + Servicediagnostik + + + + These service options can replace normal status animations. Leave them off unless you are diagnosing the LED hardware. + Dessa servicealternativ kan ersätta normala statusanimationer. Låt dem vara avstängda om du inte felsöker LED-maskinvaran. + + + + Factory LED test + LED-test från fabrik + + + + LED diagnostics + LED-diagnostik + + + + Choose color for %1 + Välj färg för %1 + LicensesPage diff --git a/evcharger-app/i18n/qml_tr.ts b/evcharger-app/i18n/qml_tr.ts index 4c513b4..c14c189 100644 --- a/evcharger-app/i18n/qml_tr.ts +++ b/evcharger-app/i18n/qml_tr.ts @@ -2878,10 +2878,218 @@ LedPage - + LED LED + + + + + + Ready + Hazır + + + + Color shown while the charger is ready and no car is connected. + Şarj cihazı hazırken ve hiçbir araç bağlı değilken gösterilen renk. + + + + + + + Waiting for car + Araç bekleniyor + + + + Color shown while the charger is waiting for the vehicle. + Şarj cihazı aracı beklerken gösterilen renk. + + + + + + + Charging + Şarj ediliyor + + + + Color of the rotating tails while the vehicle is charging. + Araç şarj olurken dönen ışık kuyruklarının rengi. + + + + + + + Finished + Tamamlandı + + + + Color shown when charging has finished. + Şarj tamamlandığında gösterilen renk. + + + + Live device + Canlı cihaz + + + + Live LED ring + Canlı LED halkası + + + + No animation data + Animasyon verisi yok + + + + Waiting for authorization + Yetkilendirme bekleniyor + + + + Rainbow + Gökkuşağı + + + + Pulsating + Nabız efekti + + + + Firmware update + Donanım yazılımı güncellemesi + + + + Firmware update finished + Donanım yazılımı güncellemesi tamamlandı + + + + Firmware update failed + Donanım yazılımı güncellemesi başarısız + + + + Configured preview + Yapılandırılmış önizleme + + + + Adjust how the LED ring communicates charging states and preview the result before using it on the charger. + LED halkasının şarj durumlarını nasıl gösterdiğini ayarlayın ve şarj cihazında kullanmadan önce sonucu önizleyin. + + + + LED ring preview + LED halkası önizlemesi + + + + Preview + Önizleme + + + + The live preview follows the animation reported by the charger. Select another state to preview its configured color; the charging preview includes the rotating tails. + Canlı önizleme, şarj cihazının bildirdiği animasyonu izler. Yapılandırılmış rengini önizlemek için başka bir durum seçin; şarj önizlemesi dönen ışık kuyruklarını içerir. + + + + Brightness + Parlaklık + + + + LED brightness + LED parlaklığı + + + + + %1% + %1% + + + + Lower brightness is less distracting at night. Set it to zero to turn the ring off completely. + Düşük parlaklık geceleri daha az rahatsız eder. Halkayı tamamen kapatmak için sıfıra ayarlayın. + + + + Status colors + Durum renkleri + + + + Tap a charging state to choose the color used by the LED ring. + LED halkasının kullanacağı rengi seçmek için bir şarj durumuna dokunun. + + + + Energy saving + Enerji tasarrufu + + + + Energy saving on + Enerji tasarrufu açık + + + + Energy saving off + Enerji tasarrufu kapalı + + + + Energy saving turns off status lighting when it is not needed. Charging and important warnings remain visible. + Enerji tasarrufu, gerekmediğinde durum aydınlatmasını kapatır. Şarj ve önemli uyarılar görünür kalır. + + + + Keep mode indicators on + Mod göstergelerini açık tut + + + + Keep the small charging-mode indicators visible while the LED ring is saving energy. + LED halkası enerji tasarrufu yaparken küçük şarj modu göstergelerini görünür tutun. + + + + Service diagnostics + Servis tanılama + + + + These service options can replace normal status animations. Leave them off unless you are diagnosing the LED hardware. + Bu servis seçenekleri normal durum animasyonlarının yerini alabilir. LED donanımını tanılamıyorsanız bunları kapalı bırakın. + + + + Factory LED test + Fabrika LED testi + + + + LED diagnostics + LED tanılama + + + + Choose color for %1 + %1 için renk seçin + LicensesPage diff --git a/evcharger-app/qml/LedPage.qml b/evcharger-app/qml/LedPage.qml index a3c5a69..4594835 100644 --- a/evcharger-app/qml/LedPage.qml +++ b/evcharger-app/qml/LedPage.qml @@ -1,13 +1,438 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls +import QtQuick.Dialogs import QtQuick.Layouts +import EVChargerApp NavigationPage { + id: page + + property string selectedColorKey: "cid" + property string selectedColorName + property string previewMode: "live" + title: qsTr("LED") - Text { - text: "TODO" + function colorOptions() { + const result = [] + if (idleColor.exists) { + result.push({ + key: "cid", + title: qsTr("Ready"), + description: qsTr("Color shown while the charger is ready and no car is connected."), + value: idleColor.value + }) + } + if (waitingColor.exists) { + result.push({ + key: "cwc", + title: qsTr("Waiting for car"), + description: qsTr("Color shown while the charger is waiting for the vehicle."), + value: waitingColor.value + }) + } + if (chargingColor.exists) { + result.push({ + key: "cch", + title: qsTr("Charging"), + description: qsTr("Color of the rotating tails while the vehicle is charging."), + value: chargingColor.value + }) + } + if (finishedColor.exists) { + result.push({ + key: "cfi", + title: qsTr("Finished"), + description: qsTr("Color shown when charging has finished."), + value: finishedColor.value + }) + } + return result + } - Layout.fillHeight: true + function previewOptions() { + const result = [] + if (liveAnimation.exists) + result.push({text: qsTr("Live device"), value: "live"}) + if (idleColor.exists) + result.push({text: qsTr("Ready"), value: "idle"}) + if (waitingColor.exists) + result.push({text: qsTr("Waiting for car"), value: "waiting"}) + if (chargingColor.exists) + result.push({text: qsTr("Charging"), value: "charging"}) + if (finishedColor.exists) + result.push({text: qsTr("Finished"), value: "finished"}) + return result + } + + function previewTitle() { + switch (previewMode) { + case "idle": return qsTr("Ready") + case "waiting": return qsTr("Waiting for car") + case "charging": return qsTr("Charging") + case "finished": return qsTr("Finished") + default: return qsTr("Live LED ring") + } + } + + function animationName() { + const data = liveAnimation.value + if (data === null || data === undefined || !data.name) + return qsTr("No animation data") + switch (String(data.name)) { + case "Ready": return qsTr("Ready") + case "WaitCar": return qsTr("Waiting for car") + case "WaitAuth": return qsTr("Waiting for authorization") + case "Charging": return qsTr("Charging") + case "Finished": return qsTr("Finished") + case "Rainbow": return qsTr("Rainbow") + case "Pulsate": return qsTr("Pulsating") + case "OtaProgress": return qsTr("Firmware update") + case "OtaFinished": return qsTr("Firmware update finished") + case "OtaFailed": return qsTr("Firmware update failed") + default: + return String(data.name).replace(/([a-z])([A-Z])/g, "$1 $2") + } + } + + function previewDetail() { + if (previewMode !== "live") + return qsTr("Configured preview") + return animationName() + } + + function normalizeColor(value) { + const text = String(value) + return text.length === 9 ? "#" + text.substring(3) : text + } + + function editColor(option) { + selectedColorKey = option.key + selectedColorName = option.title + colorDialog.selectedColor = option.value || "#000000" + colorDialog.open() + } + + ApiKeyValueHelper { + id: liveAnimation + deviceConnection: theDeviceConnection + apiKey: "led" + } + + ApiKeyValueHelper { + id: ledCount + deviceConnection: theDeviceConnection + apiKey: "nld" + } + + ApiKeyValueHelper { + id: brightness + deviceConnection: theDeviceConnection + apiKey: "lbr" + } + + ApiKeyValueHelper { + id: idleColor + deviceConnection: theDeviceConnection + apiKey: "cid" + } + + ApiKeyValueHelper { + id: waitingColor + deviceConnection: theDeviceConnection + apiKey: "cwc" + } + + ApiKeyValueHelper { + id: chargingColor + deviceConnection: theDeviceConnection + apiKey: "cch" + } + + ApiKeyValueHelper { + id: finishedColor + deviceConnection: theDeviceConnection + apiKey: "cfi" + } + + ApiKeyValueHelper { + id: energySaving + deviceConnection: theDeviceConnection + apiKey: "lse" + } + + ApiKeyValueHelper { + id: modeLedsWhileSaving + deviceConnection: theDeviceConnection + apiKey: "mws" + } + + ApiKeyValueHelper { + id: factoryDebug + deviceConnection: theDeviceConnection + apiKey: "fldb" + } + + ApiKeyValueHelper { + id: ledDebug + deviceConnection: theDeviceConnection + apiKey: "ldb" + } + + SetValueHelper { + id: colorSetter + deviceConnection: theDeviceConnection + apiKey: page.selectedColorKey + } + + Text { + Layout.fillWidth: true + text: qsTr("Adjust how the LED ring communicates charging states and preview the result before using it on the charger.") + wrapMode: Text.Wrap + color: "#5f6368" + } + + SettingsSectionLabel { + text: qsTr("LED ring preview") + } + + SettingsCard { + RowLayout { + Layout.fillWidth: true + + SettingsLabel { + Layout.fillWidth: true + text: qsTr("Preview") + } + + ComboBox { + id: previewSelector + model: previewOptions() + textRole: "text" + valueRole: "value" + onActivated: page.previewMode = currentValue + + function selectCurrentMode() { + const index = indexOfValue(page.previewMode) + if (index >= 0) + currentIndex = index + else if (count > 0) { + currentIndex = 0 + page.previewMode = currentValue + } + } + + Component.onCompleted: selectCurrentMode() + onModelChanged: Qt.callLater(selectCurrentMode) + } + } + + LedRingPreview { + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: Math.min(280, page.width - 50) + Layout.preferredHeight: width + animationData: liveAnimation.value + previewMode: page.previewMode + configuredLedCount: ledCount.exists ? Number(ledCount.value) : 32 + configuredBrightness: brightness.exists ? Number(brightness.value) : 255 + idleColor: idleColor.value || "#0000ff" + waitingColor: waitingColor.value || "#ffff00" + chargingColor: chargingColor.value || "#00ffff" + finishedColor: finishedColor.value || "#00ff00" + statusText: previewTitle() + detailText: previewDetail() + } + + Text { + Layout.fillWidth: true + text: qsTr("The live preview follows the animation reported by the charger. Select another state to preview its configured color; the charging preview includes the rotating tails.") + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + color: "#5f6368" + } + } + + SettingsSectionLabel { + visible: brightness.exists + text: qsTr("Brightness") + } + + EditValueItem { + apiKey: "lbr" + text: qsTr("LED brightness") + visible: exists + valueText: value === null || value === undefined + ? "—" + : qsTr("%1%").arg(Math.round(Number(value) / 255 * 100)) + fullWidth: true + + editableItem: ColumnLayout { + property alias value: brightnessSlider.value + + Label { + Layout.fillWidth: true + text: qsTr("%1%").arg(Math.round(brightnessSlider.value / 255 * 100)) + horizontalAlignment: Text.AlignHCenter + font.bold: true + } + + Slider { + id: brightnessSlider + Layout.minimumWidth: 250 + Layout.fillWidth: true + from: 0 + to: 255 + stepSize: 1 + snapMode: Slider.SnapAlways + } + + Text { + Layout.fillWidth: true + text: qsTr("Lower brightness is less distracting at night. Set it to zero to turn the ring off completely.") + wrapMode: Text.Wrap + color: "#5f6368" + } + } + } + + SettingsSectionLabel { + visible: colorOptions().length > 0 + text: qsTr("Status colors") + } + + Text { + Layout.fillWidth: true + visible: colorOptions().length > 0 + text: qsTr("Tap a charging state to choose the color used by the LED ring.") + wrapMode: Text.Wrap + color: "#5f6368" + } + + Repeater { + model: colorOptions() + + delegate: WhiteItemDelegate { + id: colorItem + required property var modelData + + Layout.fillWidth: true + implicitHeight: colorContent.implicitHeight + 20 + onClicked: editColor(modelData) + + contentItem: RowLayout { + id: colorContent + + Rectangle { + Layout.preferredWidth: 34 + Layout.preferredHeight: 34 + radius: 17 + color: colorItem.modelData.value || "#000000" + border.color: "#5f6368" + border.width: 1 + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + + SettingsLabel { + Layout.fillWidth: true + text: colorItem.modelData.title + } + + Text { + Layout.fillWidth: true + text: colorItem.modelData.description + wrapMode: Text.Wrap + color: "#5f6368" + } + } + + Text { + text: normalizeColor(colorItem.modelData.value).toUpperCase() + color: "#5f6368" + } + + Text { + text: ">" + } + } + } + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + visible: colorSetter.pending + } + + RequestStatusText { + Layout.fillWidth: true + request: colorSetter + } + + SettingsSectionLabel { + visible: energySaving.exists || modeLedsWhileSaving.exists + text: qsTr("Energy saving") + } + + GeneralOnOffSwitch { + apiKey: "lse" + visible: exists + text: checked ? qsTr("Energy saving on") : qsTr("Energy saving off") + } + + Text { + Layout.fillWidth: true + visible: energySaving.exists + text: qsTr("Energy saving turns off status lighting when it is not needed. Charging and important warnings remain visible.") + wrapMode: Text.Wrap + color: "#5f6368" + } + + GeneralOnOffSwitch { + apiKey: "mws" + visible: exists + text: qsTr("Keep mode indicators on") + } + + Text { + Layout.fillWidth: true + visible: modeLedsWhileSaving.exists + text: qsTr("Keep the small charging-mode indicators visible while the LED ring is saving energy.") + wrapMode: Text.Wrap + color: "#5f6368" + } + + SettingsSectionLabel { + visible: factoryDebug.exists || ledDebug.exists + text: qsTr("Service diagnostics") + } + + Text { + Layout.fillWidth: true + visible: factoryDebug.exists || ledDebug.exists + text: qsTr("These service options can replace normal status animations. Leave them off unless you are diagnosing the LED hardware.") + wrapMode: Text.Wrap + color: "#5f6368" + } + + GeneralOnOffSwitch { + apiKey: "fldb" + visible: exists + text: qsTr("Factory LED test") + } + + GeneralOnOffSwitch { + apiKey: "ldb" + visible: exists + text: qsTr("LED diagnostics") + } + + ColorDialog { + id: colorDialog + title: qsTr("Choose color for %1").arg(page.selectedColorName) + onAccepted: colorSetter.setValue(page.normalizeColor(selectedColor)) } } diff --git a/evcharger-app/qml/LedRingPreview.qml b/evcharger-app/qml/LedRingPreview.qml new file mode 100644 index 0000000..56ef8a0 --- /dev/null +++ b/evcharger-app/qml/LedRingPreview.qml @@ -0,0 +1,224 @@ +pragma ComponentBehavior: Bound + +import QtQuick + +Item { + id: preview + + property var animationData: null + property string previewMode: "live" + property color idleColor: "#0000ff" + property color waitingColor: "#ffff00" + property color chargingColor: "#00ffff" + property color finishedColor: "#00ff00" + property int configuredLedCount: 32 + property int configuredBrightness: 255 + property string statusText + property string detailText + property double animationTime: Date.now() + + readonly property int actualLedCount: Math.max(1, Math.min(128, Number(configuredLedCount) || 32)) + readonly property real ringDiameter: Math.min(width, height) + readonly property real ringRadius: ringDiameter * 0.39 + readonly property real ledDiameter: Math.max(5, Math.min(14, ringDiameter * 1.55 / actualLedCount)) + + implicitWidth: 260 + implicitHeight: 260 + + function normalizeIndex(index) { + let result = Math.floor(Number(index) || 0) % actualLedCount + if (result < 0) + result += actualLedCount + return result + } + + function colorComponents(value) { + let text = String(value || "#000000") + if (text.length === 9) + text = "#" + text.substring(3) + if (!/^#[0-9a-fA-F]{6}$/.test(text)) + text = "#000000" + return [ + parseInt(text.substring(1, 3), 16) / 255, + parseInt(text.substring(3, 5), 16) / 255, + parseInt(text.substring(5, 7), 16) / 255 + ] + } + + function mixColor(first, second, amount) { + const left = colorComponents(first) + const right = colorComponents(second) + const factor = Math.max(0, Math.min(1, amount)) + return Qt.rgba(left[0] + (right[0] - left[0]) * factor, + left[1] + (right[1] - left[1]) * factor, + left[2] + (right[2] - left[2]) * factor, + 1) + } + + function scaleColor(value, intensity) { + const rgb = colorComponents(value) + const brightness = Math.max(0, Math.min(1, configuredBrightness / 255)) + * Math.max(0, Math.min(1, intensity)) + return Qt.rgba(rgb[0] * brightness, rgb[1] * brightness, rgb[2] * brightness, 1) + } + + function durationMilliseconds(value) { + const text = String(value || "") + if (text.endsWith("ms")) + return Math.max(1, Number(text.substring(0, text.length - 2)) || 1) + if (text.endsWith("s")) + return Math.max(1, (Number(text.substring(0, text.length - 1)) || 1) * 1000) + return Math.max(1, Number(text) || 1000) + } + + function rangePosition(index, from, to) { + const start = normalizeIndex(from) + const end = normalizeIndex(to) + let current = start + for (let offset = 0; offset < actualLedCount; ++offset) { + if (current === index) + return offset + if (current === end) + break + current = (current + 1) % actualLedCount + } + return -1 + } + + function repeatingColor(colors, position) { + if (colors === null || colors === undefined || colors.length < 1) + return "#000000" + return colors[position % colors.length] + } + + function renderCommandColor(index, data) { + let result = "#000000" + const ranges = data && data.ranges ? data.ranges : [] + for (let rangeIndex = 0; rangeIndex < ranges.length; ++rangeIndex) { + const range = ranges[rangeIndex] + const position = rangePosition(index, range.from, range.to) + if (position < 0) + continue + + if (range.blink !== null && range.blink !== undefined) { + const duration = durationMilliseconds(range.blink) + const secondColor = Math.floor(animationTime / (duration / 2)) % 2 === 1 + result = repeatingColor(secondColor ? range.colors1 : range.colors0, position) + } else if (range.fade !== null && range.fade !== undefined) { + const duration = durationMilliseconds(range.fade) + const progress = (animationTime % duration) / duration + const amount = (1 - Math.cos(progress * Math.PI * 2)) / 2 + result = mixColor(repeatingColor(range.colors0, position), + repeatingColor(range.colors1, position), amount) + } else { + result = repeatingColor(range.colors, position) + } + } + return scaleColor(result, 1) + } + + function tailColor(index, data) { + const count = Math.max(1, Math.min(3, Number(data.count) || 1)) + const speed = Math.max(1, Number(data.speed) || 20) + const ampere = Math.max(0, Math.min(32, 35 - speed)) + const tailLength = Math.max(0.1, Math.min(0.94, (ampere + 3) / 35)) + const rotation = (animationTime / (speed * 256)) % 1 + let distance = ((index / actualLedCount) * count - rotation * count) % 1 + if (distance < 0) + distance += 1 + const intensity = distance <= tailLength ? 1 - distance / tailLength : 0 + return scaleColor(chargingColor, Math.pow(intensity, 1.45)) + } + + function effectiveAnimation() { + switch (previewMode) { + case "idle": return {subtype: "solid", color: idleColor} + case "waiting": return {subtype: "solid", color: waitingColor} + case "charging": return {subtype: "schweif", count: 3, speed: 14} + case "finished": return {subtype: "solid", color: finishedColor} + default: return animationData || {subtype: "solid", color: "#303030"} + } + } + + function ledColor(index) { + const data = effectiveAnimation() + switch (data.subtype) { + case "solid": + return scaleColor(data.color, 1) + case "rainbow": + return scaleColor(Qt.hsva(index / actualLedCount, 0.94, 1, 1), 1) + case "schweif": + return tailColor(index, data) + case "colors": + return scaleColor(repeatingColor(data.colors, index), 1) + case "renderCmds": + return renderCommandColor(index, data) + default: + return scaleColor("#303030", 1) + } + } + + Timer { + interval: 40 + repeat: true + running: preview.visible + onTriggered: preview.animationTime = Date.now() + } + + Rectangle { + anchors.centerIn: parent + width: preview.ringDiameter * 0.64 + height: width + radius: width / 2 + color: "#202124" + border.color: "#44474a" + border.width: 2 + + Column { + anchors.centerIn: parent + width: parent.width * 0.78 + spacing: 3 + + Text { + width: parent.width + text: preview.statusText + color: "white" + font.bold: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + + Text { + width: parent.width + text: preview.detailText + visible: text.length > 0 + color: "#bdc1c6" + font.pixelSize: 12 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + } + } + + Repeater { + model: preview.actualLedCount + + delegate: Rectangle { + required property int index + readonly property real angle: index / preview.actualLedCount * Math.PI * 2 + + x: preview.width / 2 + Math.sin(angle) * preview.ringRadius - width / 2 + y: preview.height / 2 - Math.cos(angle) * preview.ringRadius - height / 2 + width: preview.ledDiameter + height: width + radius: width / 2 + color: preview.ledColor(index) + border.color: "#5f6368" + border.width: 1 + + Behavior on color { + ColorAnimation { duration: 35 } + } + } + } +} diff --git a/evcharger-app/qml/qmldir b/evcharger-app/qml/qmldir index f06a160..e9f182b 100644 --- a/evcharger-app/qml/qmldir +++ b/evcharger-app/qml/qmldir @@ -56,6 +56,7 @@ EVChargerApp 1.0 IndexedEditValueItem.qml EVChargerApp 1.0 IndexedOnOffSwitch.qml EVChargerApp 1.0 KwhLimitPage.qml EVChargerApp 1.0 LedPage.qml +EVChargerApp 1.0 LedRingPreview.qml EVChargerApp 1.0 LicensesPage.qml EVChargerApp 1.0 LoadBalancingPage.qml EVChargerApp 1.0 LogicModeButton.qml