Sulla copertina compare l'icona del boost (CoverAction). Una sola azione: la documentazione Silica ammette piu' di una azione ma dice che lo stile delle app di sistema ne mostra una. CoverBackground eredita da Cover, quindi l'elenco dichiarato li' si attacca; l'icona e' iconSource (non icon.source del Button: sbagliarlo non da' errore, l'icona semplicemente non compare). La riga in fondo si ancora a Cover.coverActionArea, cosi' l'icona non la copre. Nuovo controllo statico tests/cover_action_check.py: elenco figlio diretto del Cover, una sola azione, icona che esiste, id del comando presente nella tabella del bridge. Il componente e' nominato nella guida Silica dell'SDK di Carlo.
260 lines
13 KiB
Markdown
260 lines
13 KiB
Markdown
# harbour-aira — versione italiana
|
|
|
|
<img src="icons/172x172/harbour-aira.png" width="96" alt="icona">
|
|
|
|
*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**, una schermata da guardare: pallino colorato con lo stato in una riga
|
|
("Tutto regolare", "Sta scaldando l'acqua"), l'ora dell'ultimo aggiornamento, le
|
|
due letture che contano (casa e acqua calda, ognuna con il suo obiettivo)
|
|
affiancate e **toccabili**, e tre azioni rapide — boost, assenza, comandi
|
|
impianto — ognuna con la sua icona. Sta in una schermata, senza scorrere: i
|
|
comandi hanno pagine loro
|
|
- **Regolazioni**: obiettivo casa e obiettivo acqua calda, ognuno con il valore
|
|
che l'impianto sta tenendo adesso, lo slider (la proposta: il valore vero non si
|
|
muove), una fila di valori rapidi per le dita fredde e il pulsante di conferma,
|
|
che si accende solo se c'è davvero qualcosa da cambiare. Il boost sta qui
|
|
- **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, modalità manuale, sbrinamento, luci di firma, stato pompa, modi
|
|
consentiti, temperature esterna e interna, zone, ultima lettura), accessori (il
|
|
termostato a parete con temperatura, umidità, segnale, batteria), versioni del
|
|
software, numero di errori e verifica impianto. Qui sta anche la frase di stato
|
|
estesa ("Relax: la pompa di calore funziona regolarmente")
|
|
- **Comandi impianto**: quattro interruttori (assenza, riscaldamento forzato,
|
|
riscaldamento, acqua calda attiva) che mostrano lo stato e lo cambiano in un
|
|
tocco, più i pulsanti per quello che uno stato non ce l'ha: ciclo legionella e
|
|
night mode per un'ora. Gli interruttori seguono l'impianto, non lo anticipano
|
|
- **Copertina** con casa e acqua calda, ognuna con il valore che l'impianto sta
|
|
tenendo adesso e il suo obiettivo ("22,9/19,0 °C"), più il pallino di stato
|
|
come nella Home
|
|
- Indirizzo e token del bridge stanno nelle impostazioni dell'app (QSettings),
|
|
mai nel binario
|
|
|
|

|
|
|
|
Anteprime del layout (valori veri dell'impianto, **non** screenshot — vedi
|
|
*Stato*). Queste sono in italiano; quelle in inglese stanno in
|
|
`docs/anteprima/`:
|
|
|
|
| Home | Regolazioni | Dati |
|
|
|---|---|---|
|
|
|  |  |  |
|
|
|
|
| Impianto | Comandi impianto | Impostazioni | Copertina |
|
|
|---|---|---|---|
|
|
|  |  |  |  |
|
|
|
|
## 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-<versione>.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<versione> build <data>`: 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
|
|
```
|
|
|
|
Altri due controlli guardano i QML stessi, visto che qui non se ne può eseguire
|
|
nessuno. Impiegano millisecondi e dicono il file di cui non sono contenti:
|
|
|
|
```sh
|
|
python3 tests/qml_check_bindings.py qml # nessuna proprietà assegnata due volte
|
|
python3 tests/qml_focus_check.py # un tocco apre solo quello che ha toccato
|
|
python3 tests/label_fit_check.py # le etichette dei tasti ci stanno
|
|
python3 tests/cover_action_check.py # l'azione di copertina c'e' ed e' collegata
|
|
```
|
|
|
|
## Stato
|
|
|
|
- **85/85** verifiche del nucleo, compilazione senza warning (comprese le frasi
|
|
di stato, breve ed estesa, e la severita' degli allarmi: quello che la Home
|
|
dice dell'impianto è verificato senza telefono)
|
|
- **48/48** verifiche offline del bridge (`bridge/test_bridge_commands.py`),
|
|
comprese le conversioni Wh→kWh e degli importi delle statistiche
|
|
- **191/191** 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)
|
|
- controlli statici dove la UI non è eseguibile: `qmllint` pulito su tutti i QML
|
|
(valida solo la sintassi: senza il modulo Silica una proprietà inesistente
|
|
passa in silenzio), le chiavi che i QML leggono sono quelle che il bridge manda
|
|
(`tests/ui_contract_check.py`), e toccare una lettura in Home apre solo le
|
|
regolazioni di quell'oggetto (`tests/qml_focus_check.py`)
|
|
- **nessuna assegnazione duplicata** nei QML (`tests/qml_check_bindings.py`,
|
|
13 file): assegnare due volte la stessa proprietà nello stesso oggetto — due
|
|
`Component.onCompleted` in una pagina, tipicamente — fa fallire la creazione
|
|
dell'oggetto in Qt 5.6, e sul telefono si vede solo `Type MainPage unavailable`
|
|
senza il nome del file. Il controllo impiega millisecondi e dice la riga
|
|
- 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 €)
|
|
- **compilata ed eseguita su un telefono** (Jolla, Sailfish OS 5.2): è proprio la
|
|
prima build ad aver rivelato il difetto della Home, corretto nella 0.6.1. Resta
|
|
**non verificato l'invio di un comando** a un impianto vero: finora solo
|
|
letture. 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 in una riga, due letture, azioni rapide
|
|
qml/pages/SetpointsPage.qml obiettivi casa e acqua, valori rapidi, boost
|
|
qml/pages/DataPage.qml energia, efficienza e risparmi
|
|
qml/pages/PlantPage.qml stato, accessori, versioni, errori, diagnostica
|
|
qml/pages/CommandsPage.qml i comandi impianto, stato negli interruttori
|
|
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/ReadingCell.qml lettura toccabile: titolo, valore grande, barra
|
|
qml/components/NoticeBar.qml messaggi dei comandi, ancorati in fondo
|
|
qml/components/ActionButton.qml pulsante di comando, con conferma opzionale
|
|
qml/icons/ i tre glifi delle azioni rapide (disegnati, non dal tema)
|
|
tests/core_test.cpp verifiche del nucleo (headless)
|
|
tests/ui_contract_check.py chiavi dei QML contro quelle del bridge (bridge attivo)
|
|
tests/qml_check_bindings.py assegnazioni duplicate di proprieta' nei QML
|
|
tests/qml_focus_check.py un tocco apre solo le regolazioni di quell'oggetto
|
|
tests/label_fit_check.py le etichette dei tasti ci stanno accanto all'icona
|
|
tests/cover_action_check.py azione di copertina: figlia diretta del Cover, icona e comando esistono
|
|
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 cosa è cambiato nella 0.6.0
|
|
docs/ux/ schemi dello studio UX
|
|
docs/icona/ proposte di icona e confronto prima/dopo
|
|
translations/ harbour-aira-it.ts (l'inglese è la lingua sorgente)
|
|
docs/anteprima/ anteprime di layout in inglese (in italiano: it/)
|
|
```
|
|
|
|
## Licenza
|
|
|
|
MIT.
|