L'inglese è la lingua sorgente delle stringhe; l'italiano sta nel catalogo translations/harbour-aira-it.ts (99 stringhe), che lupdate aggiorna e lrelease compila in build. Di serie la lingua del sistema, con ripiego sull'inglese. - nuova classe Translator: risolve la lingua prima di createView e allinea QLocale::setDefault, così anche i numeri seguono la lingua (23,0 °C in italiano, 23.0 °C in inglese) — le temperature passano da formatValue, non più dal toFixed() di JavaScript che metteva sempre il punto - Qt 5.6 non ha QQmlEngine::retranslate(): al cambio di lingua la radice QML viene ricaricata (setSource ricrea il componente anche a url invariata) - le etichette di stato in airajson.cpp usano QCoreApplication::translate per esteso: lupdate non riconosce una funzione wrapper e le avrebbe perse - selettore della lingua in Impostazioni (Sistema / English / Italiano) - test: 41/41, carica il .qm vero come fa main.cpp e verifica entrambe le lingue, il ripiego e i formati numerici - anteprime bilingui (docs/anteprima/ inglese, it/ italiano), README e PIANO aggiornati, spec RPM con i cataloghi in %files
9.8 KiB
harbour-aira — app Sailfish per la pompa di calore Aira
Piano scritto il 9 ottobre 2026, prima di scrivere la UI (convenzione di lavoro).
1. Stato verificato sul campo
| Cosa | Esito |
|---|---|
| Accesso cloud Aira | funziona — pyairahome 2.3.0, pool Cognito eu-north-1_cnqyjWtbz (indice 0), token salvati in ~/.hermes/profiles/carlo/aira_tokens.json (600) |
| Lettura impianto | funziona — esterna, interna, ACS attuale/obiettivo, modalità, curve zone, firmware, errori |
| Bridge HTTP sul server | scritto e provato — ~/workspace/Carlo/Aira/aira_bridge.py, v0.3.0 (servizio systemd utente attivo) |
| Bluetooth sul server | assente — nessun adattatore HCI (esclude la via locale BLE su questa macchina) |
SDK Sailfish (sfdk) |
assente qui — build e deploy li fa Carlo dal suo PC |
| Jolla Phone 2026 | non raggiungibile dalla rete del server: deploy solo dal PC |
2. Perché serve un bridge e non una chiamata diretta
Il cloud Aira espone gRPC su HTTP/2. Qt 5.6 — il toolkit su cui gira Sailfish — non ha HTTP/2, quindi un'app nativa non può parlarci direttamente. Vie alternative valutate e scartate:
- PyOtherSide + pyairahome dentro l'app: servirebbe impacchettare
grpcioebleakcompilati per aarch64/SFOS, con l'RPM che diventa fragile a ogni aggiornamento. - Cognito + gRPC riscritti in C++: HTTP/2 e protobuf da zero. Molto lavoro, molto rischio.
Scelta: la logica resta sul server (dove Python e i token già funzionano), l'app è un client sottile. Vantaggio collaterale: credenziali e token non stanno mai sul telefono.
Jolla (QML/C++ Qt 5.6) --HTTP/1.1 + JSON--> bridge sul server --gRPC--> cloud Aira
(pyairahome + token)
3. Protocollo del bridge (v1 — già implementato)
Autenticazione: Authorization: Bearer <token> su tutto tranne /health.
Token generato al primo avvio in ~/.hermes/profiles/carlo/aira_bridge.json (600).
| Metodo | Rotta | Risposta |
|---|---|---|
| GET | /health |
{ok, version} — nessuna auth, per la diagnostica |
| GET | /summary |
valori leggibili: temperature, ACS, modalità, flag, errori |
| GET | /state |
stato completo grezzo (42 campi) |
| GET | /commands |
elenco comandi ammessi + parametri |
| POST | /command |
{"id": "...", "value": ...} |
Comandi ammessi in v1: ping, set_hot_water_target (45–60 °C), hot_water_boost_on/off,
hot_water_heating_on/off, away_mode_on/off, night_mode_1h, force_heating_on/off,
heating_on/off, legionella_cycle.
Whitelist voluta: la libreria espone ~90 comandi, inclusi FactoryReset,
InstallFirmware, RebootDevice, RotateCertificate. Quelli restano fuori di proposito:
un client mobile non deve poter riformattare l'impianto per un tap sbagliato.
4. Architettura dell'app
harbour-aira.pro/rpm//icons/dai template consolidati della skill.- C++ minimo:
Settings(QSettings suAppConfigLocation, obbligatorio per la sandbox SailJail) eApiClient(QNetworkAccessManager + watchdog di timeout 15 s, JSON parsing). Esposti come context properties:appSettings,api. - QML:
MainPage(dashboard),SettingsPage(indirizzo bridge + token),CoverPage. - Nessun dato personale nel binario: indirizzo e token si configurano nell'app (le repo sono pubbliche su Gitea).
5. Fasi
- F0 — ponte (fatto): bridge scritto, provato su
/health,/summary,/commands, errori 400/401 verificati. Nessun comando inviato all'impianto. - F1 — v0.1 in sola lettura (fatto): dashboard con esterna/interna/ACS, modalità, stato ACS, pull-to-refresh, copertina con le temperature.
- F2 — comandi (fatto in v0.1): obiettivo ACS (slider 45–60), boost on/off, con conferma
RemorseItem. Gli altri comandi della whitelist restano raggiungibili dal bridge ma non hanno ancora un pulsante nell'app. - F3 — servizio e rete: unità systemd per il bridge (avvio automatico), accesso dal telefono. Decisione dell'utente: per ora solo rete locale — quindi l'indirizzo del bridge è l'IP del server in LAN, e la copertura fuori casa si affronta più avanti (VPN WireGuard già attiva, oppure sottodominio HTTPS).
5-bis. Consegna del 9 ottobre 2026 (sera)
Stato: progetto completo in ~/workspace/Carlo/AppSailfish/harbour-aira
(commit iniziale, 27 file) e tarball AppSailfish/pacchetti/harbour-aira-0.1.0.tar.gz.
Verificato qui, senza SDK:
- nucleo puro (
src/airajson.cpp): 26/26 verifiche superate con l'harness Qt 5.15; - compilazione del nucleo senza warning (
-Wall -Wextra); qmllintsui 6 file QML: nessun errore oltre ai moduli Silica assenti (atteso senza SDK).
Non verificabile qui (serve il suo PC + il telefono): build RPM, istallazione, UI reale.
5-ter. Aggiornamento del 10 ottobre 2026 — v0.2.0 (riscaldamento)
-
Bridge 0.2.0: comandi
set_heating_setpointeset_cooling_setpoint(5–30 °C, zona opzionale 1 o 2) e campizone1_*/zone2_*nel riassunto. Il messaggioZoneTemperaturesva inviato completo: la zona non toccata viaggia col suo valore attuale (letto dall'ultimo stato, tenuto in cache nella sessione), altrimenti a zero verrebbe spenta. Verifica:test_bridge_commands.py, 19/19, nessun comando inviato all'impianto. -
App 0.2.0: sezione "Riscaldamento" nella dashboard (temperatura in casa, obiettivo, stato zona, slider 5–30 °C a passi di 0,5 con conferma) e copertina con la temperatura di casa. Aggiunte le etichette per gli stati
PUMP_MODE_STATE_*. Nucleo: 29/29. -
Bridge 0.3.0 + App 0.3.0: pagina "Altri comandi" nell'app (dalla tendina in alto o dal pulsante in fondo alla home) con assenza, night mode un'ora, riscaldamento forzato on/off, funzione riscaldamento on/off, ciclo antilegionella, riscaldamento ACS on/off e verifica impianto; i comandi che durano (forzato, legionella, disabilitazioni) chiedono conferma. In cima alla pagina lo stato attuale, così non si preme alla cieca. Bug corretto nello stesso giro:
force_heatingnello stato è un oggetto{enabled, remaining_time}, e il bridge lo leggeva conbool()— sempre vero. Ora si legge il campoenabled; il test lo copre (27/27). Nucleo app: 31/31. -
App 0.2.1 (richiesta dell'utente): la temperatura della casa diventa il dato principale in cima alla home (valore grande, barra, obiettivo, slider e pulsante), l'acqua calda sanitaria scende in una propria sezione sotto, lo stato impianto va in fondo. Stessa gerarchia nella copertina: casa in grande, ACS in una riga secondaria.
-
F3 fatto:
harbour-aira-bridge.service(systemd utente,enable --now, parte al boot grazie aLinger=yes,Restart=on-failureverificato con SIGKILL → riparte in ~10 s) e regola52-aira-bridge-lan.confin/etc/nftables.d/. Il firewall YunoHost hapolicy drope apriva solo 22/25/53/80/443/587/993/5349/5350: la 8790 va aperta a mano, solo per LAN e VPN. Lezione: un test da127.0.0.1non attraversa quelle regole e non rivela il problema — va provato dalla LAN. -
App 0.4.0 — localizzazione ita/eng (richiesta dell'utente): l'interfaccia si può mettere in italiano o inglese dalle impostazioni; di serie segue la lingua del sistema, con ripiego sull'inglese. L'inglese è la lingua sorgente delle stringhe (restano nel codice), l'italiano sta in
translations/harbour-aira-it.ts(99 stringhe, aggiornate conlupdatee compilate dalreleasein build). Nuova classeTranslator(src/translator.{h,cpp}): risolve la lingua prima dicreateView, così leqsTr()del QML nascono già tradotte, e allineaQLocale::setDefaultalla lingua scelta — così i numeri seguono la lingua (23,0 °Cin italiano,23.0 °Cin inglese): le temperature si formattano inApiClient::formatValue, non più con iltoFixed()di JavaScript, che avrebbe sempre messo il punto. Vincolo di piattaforma: Qt 5.6 non haQQmlEngine::retranslate()(arriva con Qt 5.10), quindi installare il traduttore non ritraduce ciò che è già a schermo: al cambio lingua l'app ricarica la radice QML. Trappola dilupdate: riconosce solo le funzioni di traduzione note (tr(),QCoreApplication::translate()), non una funzione wrapper scritta da noi — le etichette diairajson.cppsono quindi chiamateQCoreApplication::translate("AiraJson", …)per esteso, altrimenti sparirebbero dal catalogo. Nucleo app: 39/39 (le attese sono cambiate in inglese e il test carica il.qmvero come famain.cpp, verificando entrambe le lingue, il ripiego e i formati numerici). Anteprime rigenerate in due lingue (docs/anteprima/inglese,docs/anteprima/it/italiano), impostazioni comprese la nuova riga della lingua.
6. Rischi noti
- Dipendenza dal server: se il bridge è spento, l'app non vede nulla. Mitigazione: unità systemd con riavvio automatico (F3).
- Scadenza credenziali: i token Cognito si rinnovano da soli per settimane; quando il
refresh token scade serve rilanciare
setup_creds.pysul server. L'app deve mostrare un errore chiaro, non un retry infinito. - Assenza di test sulla UI: senza SDK né device raggiungibile da qui, la UI Silica si verifica solo sul telefono di Carlo (ciclo consolidato: io consegno il tarball, lui compila e incolla i log).
- Token del bridge: è l'unica credenziale sul telefono; va trattato come una password e
revocabile rigenerando
aira_bridge.json.
7. Decisioni aperte
- Come raggiungere il bridge dal telefono: VPN WireGuard (già attiva, zero configurazione nuova) oppure sottodominio HTTPS pubblico con reverse proxy.
- Ambito della v0.1: sola lettura (consigliata) oppure lettura + comandi ACS subito.
- Nome visibile dell'app: default proposto "Aira" (pacchetto
harbour-aira).