# harbour-aira — versione italiana *English version: [README.md](README.md)* --- # harbour-aira App nativa Sailfish OS per una pompa di calore **Aira Home**: mostra lo stato dell'impianto e invia i comandi dell'acqua calda sanitaria e del riscaldamento (temperatura obiettivo della casa, per zona). L'interfaccia è in **inglese e italiano**: di serie segue la lingua del telefono e ripiega sull'inglese; la si può scegliere anche nelle impostazioni dell'app. ## Cosa mostra - **Home**, ordinata come l'app ufficiale: temperatura esterna e *in casa / assente* in alto, una frase di stato in chiaro ("Relax: la pompa di calore funziona regolarmente"), poi la **scheda dell'impianto** con le due letture che contano (temperatura stanza e acqua calda, ognuna con il suo obiettivo). Toccando la scheda si apre la pagina dell'impianto. Sotto, i comandi: obiettivo della casa con slider 5–30 °C (passi da 0,5 °C) e conferma prima dell'invio, obiettivo ACS (45–60 °C) e i pulsanti del boost - **Dati**: calore prodotto, elettricità usata, efficienza (COP) di acqua calda e riscaldamento, risparmio in bolletta, CO₂ evitata e risparmio Smart Tariff Control, mese per mese. Sono i numeri che Aira calcola nel cloud, gli stessi della scheda *Dati* dell'app ufficiale - **Impianto**: stato (connessione, modalità, assenza/notte/riscaldamento forzato, temperature esterna e interna, zone, ultima lettura), accessori (il termostato a parete con temperatura, umidità, segnale, batteria), versioni del software e numero di errori - **Altri comandi**: assenza, night mode per un'ora, riscaldamento forzato, funzione riscaldamento on/off, ciclo legionella, ACS on/off e verifica impianto, con lo stato attuale in cima - **Copertina** con la temperatura della casa - Indirizzo e token del bridge stanno nelle impostazioni dell'app (QSettings), mai nel binario ![architettura](docs/architettura.svg) Anteprime del layout (valori veri dell'impianto, **non** screenshot — vedi *Stato*). Queste sono in italiano; quelle in inglese stanno in `docs/anteprima/`: | Home | Dati | Impianto | |---|---|---| | ![home](docs/anteprima/it/dashboard.png) | ![dati](docs/anteprima/it/dati.png) | ![impianto](docs/anteprima/it/impianto.png) | | Altri comandi | Impostazioni | Copertina | |---|---|---| | ![comandi](docs/anteprima/it/comandi.png) | ![impostazioni](docs/anteprima/it/impostazioni.png) | ![copertina](docs/anteprima/it/copertina.png) | ## La lingua dell'interfaccia L'inglese è la **lingua sorgente** delle stringhe (sta direttamente nel codice); l'italiano sta nel catalogo `translations/harbour-aira-it.ts`, che `lupdate` aggiorna e `lrelease` compila in build. La scelta fatta nelle impostazioni è salvata in `QSettings` con il codice della lingua; vuoto (o lingua assente) significa **lingua di sistema**, con ripiego sull'inglese quando non esiste il catalogo corrispondente. Qt 5.6 non ha `QQmlEngine::retranslate()` (arriva con Qt 5.10): installare un traduttore non basta a ritradurre quello che è già a schermo. L'app quindi, al cambio di lingua, **ricarica la radice QML** — la scelta ha effetto subito, al prezzo di tornare alla pagina iniziale. Anche i numeri seguono la lingua: le temperature passano da `QLocale`, quindi in italiano si scrive `23,0 °C` e in inglese `23.0 °C`. ## Perché c'è un bridge (e non una chiamata diretta al cloud) Il cloud Aira espone **gRPC su HTTP/2**. Qt 5.6 — il toolkit su cui gira ogni app Sailfish — **non ha HTTP/2**, quindi un client nativo non può parlarci direttamente. Le alternative (impacchettare `grpcio` per aarch64 con PyOtherSide, oppure riscrivere Cognito + gRPC in C++) sono fragili e costose. Quindi la logica resta **sul server**, dove Python e i token già funzionano, e l'app è un client sottile che parla HTTP/1.1 + JSON. Effetto collaterale gradito: credenziali e token non stanno mai sul telefono. ``` Jolla (QML/C++ Qt 5.6) --HTTP/1.1 + JSON--> bridge (Linux in LAN) --gRPC--> cloud Aira bridge/aira_bridge.py ``` ## Il bridge Il codice sta in [`bridge/`](bridge/): istruzioni complete in [bridge/README.md](bridge/README.md) (in inglese, perché è la parte che serve a chi prova l'app su un altro impianto). ```sh cd bridge python3 -m venv .venv && . .venv/bin/activate pip install -r requirements.txt export AIRA_CONFIG_DIR="$HOME/.config/aira" python setup_creds.py # email e password Aira: salvate in un file 600 python aira_bridge.py --port 8790 ``` Al primo avvio il bridge genera un token in `$AIRA_CONFIG_DIR/aira_bridge.json` (permessi 600): è il valore da incollare nelle impostazioni dell'app. API completa in [docs/PROTOCOL.md](docs/PROTOCOL.md). ## Impostazioni nell'app Indirizzo e token si configurano nella pagina **Impostazioni** (QSettings in `~/.config/harbour/aira/aira.conf`): niente dati personali nel binario. ## Build Servono l'SDK Sailfish (`sfdk`) e un target aarch64 (Jolla Phone 2026): ```sh sfdk target install SailfishOS-5.0.0.62-aarch64 sfdk target use SailfishOS-5.0.0.62-aarch64 sfdk build # RPM in RPMS/ sfdk deploy # sul dispositivo collegato ``` Sorgenti dal tarball (`harbour-aira-.tar.gz`, se ne hai ricevuto uno): estrarre in una cartella **nuova** (mai sopra una estrazione precedente: i Makefile restano stale), oppure clonare direttamente questo repository. Il log dell'app inizia con `harbour-aira v build `: serve a distinguere una build nuova da una stale senza indovinare. ### Verifica locale, senza SDK ```sh cd tests && mkdir -p build && cd build ~/Qt/5.15.2/gcc_64/bin/qmake ../core_test.pro && make && ./core_test ``` Copre la logica pura (`src/airajson.cpp`): normalizzazione dell'indirizzo, lettura del riassunto e delle statistiche, etichette di stato, corpo dei comandi, messaggi d'errore e la formattazione di numeri, importi e mesi della pagina Dati. **La UI Silica si verifica solo sul dispositivo.** C'è poi un controllo di **contratto fra bridge e interfaccia**: confronta le chiavi che i QML leggono con quelle che il bridge restituisce davvero (26 da `/summary`, 15 da `/stats`). Serve il bridge in esecuzione, e nasce dal fatto che la UI qui non si può eseguire: un refuso in una chiave si vedrebbe sul telefono come un "—" perenne, senza niente nel log. ```sh python3 tests/ui_contract_check.py # bridge su 127.0.0.1:8790 python3 tests/ui_contract_check.py http://192.168.3.21:8790 ``` ## Stato - **61/61** verifiche del nucleo, compilazione senza warning - **48/48** verifiche offline del bridge (`bridge/test_bridge_commands.py`), comprese le conversioni Wh→kWh e degli importi delle statistiche - **170/170** stringhe tradotte nel catalogo italiano: il test carica il `.qm` compilato come fa `main.cpp` e verifica che in inglese l'etichetta di stato sia la sorgente e in italiano quella tradotta, e che togliendo il catalogo si torni all'inglese - numeri, importi e nomi dei mesi formattati con `QLocale`: separatore decimale e posizione del simbolo di valuta seguono la lingua (verificato per entrambe) - `qmllint` pulito su tutti i QML (valida solo la sintassi: senza il modulo Silica una proprietà inesistente passa in silenzio) - provata dal vivo in **sola lettura** su un impianto Aira Home, statistiche comprese: coincidono con quelle mostrate dall'app ufficiale per lo stesso periodo (calore prodotto 70,8 kWh, elettricità 29,3 kWh, COP acqua calda 4,42, risparmio Smart Tariff Control 2,71 €) - **l'app non è ancora stata compilata né eseguita su un telefono**: le immagini in `docs/anteprima/` sono anteprime di layout costruite con i valori veri dell'impianto, non screenshot Non c'è ancora la UI per il raffrescamento (il bridge sa farlo), né orari e curve; le versioni del software si vedono in sola lettura. La pagina Dati dipende dal servizio statistiche di Aira: su un impianto appena installato qualche mese non ha ancora dati e l'app scrive *nessun dato* invece del numero. Non è nella Jolla Store: è un client non ufficiale che parla con le API Aira usando il proprio account. ## Struttura ``` harbour-aira.pro progetto qmake (icona, qml, docs) src/airajson.{h,cpp} logica pura, testabile senza SDK src/apiclient.{h,cpp} QNetworkAccessManager + watchdog di timeout src/settings.{h,cpp} QSettings su AppConfigLocation (sandbox SailJail) src/translator.{h,cpp} lingua dell'interfaccia: locale di sistema, ripiego sull'inglese, ricarica della UI al cambio src/main.cpp context properties: appSettings, api, translator qml/harbour-aira.qml ApplicationWindow + copertina qml/pages/MainPage.qml home: stato, scheda impianto, riscaldamento e acqua qml/pages/DataPage.qml energia, efficienza e risparmi qml/pages/PlantPage.qml stato, accessori, versioni, errori qml/pages/CommandsPage.qml gli altri comandi, con lo stato attuale in cima qml/pages/SettingsPage.qml qml/components/InfoRow.qml etichetta a sinistra, valore a destra qml/components/ValueRow.qml titolo e sottotitolo a sinistra, valore grande a destra qml/components/ActionButton.qml pulsante di comando, con conferma opzionale tests/core_test.cpp verifiche del nucleo (headless) tests/ui_contract_check.py chiavi dei QML contro quelle del bridge (bridge attivo) bridge/aira_bridge.py servizio HTTP/1.1 + JSON verso il cloud (gRPC) bridge/setup_creds.py salva le credenziali Aira (file 600) bridge/aira_probe.py sonda di sola lettura: login + stato impianto bridge/aira_stats_probe.py sonda di sola lettura delle statistiche del cloud bridge/test_bridge_commands.py verifiche offline dei comandi e delle statistiche (48 controlli) bridge/harbour-aira-bridge.service unit systemd utente bridge/README.md istruzioni di installazione del bridge (inglese) docs/PIANO.md piano di progetto e scelte docs/PROTOCOL.md API del bridge docs/UX-STUDIO.md studio UX e proposta per la 0.6.0 docs/ux/ schemi dello studio UX translations/ harbour-aira-it.ts (l'inglese è la lingua sorgente) docs/anteprima/ anteprime di layout in inglese (in italiano: it/) ``` ## Licenza MIT.