README in inglese per il repo pubblico; la versione italiana resta come README.it.md
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
|||||||
|
# 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).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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, etichette di stato, corpo dei comandi, messaggi
|
||||||
|
d'errore. **La UI Silica si verifica solo sul dispositivo.**
|
||||||
|
|
||||||
|
## Stato
|
||||||
|
|
||||||
|
- **31/31** verifiche del nucleo, compilazione senza warning
|
||||||
|
- **27/27** verifiche offline del bridge (`bridge/test_bridge_commands.py`)
|
||||||
|
- `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
|
||||||
|
- **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; 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/main.cpp context properties: appSettings, api
|
||||||
|
qml/harbour-aira.qml ApplicationWindow + copertina
|
||||||
|
qml/pages/MainPage.qml dashboard e comandi acqua/riscaldamento
|
||||||
|
qml/pages/CommandsPage.qml gli altri comandi, con lo stato attuale in cima
|
||||||
|
qml/pages/SettingsPage.qml
|
||||||
|
qml/components/InfoRow.qml
|
||||||
|
qml/components/ActionButton.qml pulsante di comando, con conferma opzionale
|
||||||
|
tests/core_test.cpp verifiche del nucleo (headless)
|
||||||
|
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/test_bridge_commands.py verifiche offline dei comandi (27 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/anteprima/ anteprime di layout
|
||||||
|
```
|
||||||
|
|
||||||
|
## Licenza
|
||||||
|
|
||||||
|
MIT.
|
||||||
@@ -1,103 +1,171 @@
|
|||||||
# harbour-aira
|
# harbour-aira
|
||||||
|
|
||||||
App nativa Sailfish OS per una pompa di calore **Aira Home**: mostra lo stato
|
Native Sailfish OS app to monitor and control an **Aira Home** heat pump: plant
|
||||||
dell'impianto e invia i comandi dell'acqua calda sanitaria e del riscaldamento
|
status, domestic hot water, and the house target temperature per zone.
|
||||||
(temperatura obiettivo della casa, per zona).
|
|
||||||
|
|
||||||

|
*Versione italiana: [README.it.md](README.it.md)*
|
||||||
|
|
||||||
## 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
|
## What it does
|
||||||
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
|
- **Home**: house temperature and room temperature at the top, target with a
|
||||||
l'app è un client sottile che parla HTTP/1.1 + JSON. Effetto collaterale
|
5–30 °C slider (0.5 °C steps) and a confirmation before the command is sent;
|
||||||
gradito: credenziali e token non stanno mai sul telefono.
|
domestic hot water (current, target, boost) in its own section below; plant
|
||||||
|
status at the bottom (outdoor and indoor temperature, operating mode,
|
||||||
|
away/night/manual mode, inline heater, number of zones, errors, time of the
|
||||||
|
last reading)
|
||||||
|
- **Other commands** page: away mode, night mode for one hour, force heating,
|
||||||
|
heating function on/off, legionella cycle, DHW heating on/off and a plant
|
||||||
|
check — with the current state at the top, so nothing is pressed blindly
|
||||||
|
- **Cover page** showing the house temperature
|
||||||
|
- Address and bridge token live in the app settings (QSettings), never in the
|
||||||
|
binary
|
||||||
|
|
||||||
|
Layout previews (rendered with the real values the plant returned, **not**
|
||||||
|
screenshots — see *Status*):
|
||||||
|
|
||||||
|
| Home | Other commands | Cover |
|
||||||
|
|---|---|---|
|
||||||
|
|  |  |  |
|
||||||
|
|
||||||
|
## Why there is a bridge
|
||||||
|
|
||||||
|
Aira's cloud API is **gRPC over HTTP/2**. Qt 5.6 — the toolkit every Sailfish
|
||||||
|
app runs on — **has no HTTP/2**, so a native client cannot talk to it directly.
|
||||||
|
The alternatives (packaging `grpcio` for aarch64 with PyOtherSide, or
|
||||||
|
rewriting Cognito + gRPC in C++) are fragile and expensive.
|
||||||
|
|
||||||
|
So the logic stays **on a server**, where Python and the tokens already work, and
|
||||||
|
the app is a thin client speaking HTTP/1.1 + JSON. A side effect worth having:
|
||||||
|
credentials and session tokens never leave that machine.
|
||||||
|
|
||||||
```
|
```
|
||||||
Jolla (QML/C++ Qt 5.6) --HTTP/1.1 + JSON--> bridge (Linux in LAN) --gRPC--> cloud Aira
|
Jolla (QML/C++ Qt 5.6) --HTTP/1.1 + JSON--> bridge (Linux, LAN) --gRPC--> cloud Aira
|
||||||
bridge/aira_bridge.py
|
bridge/aira_bridge.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Il bridge
|
## Requirements
|
||||||
|
|
||||||
Il codice sta in [`bridge/`](bridge/): istruzioni complete in
|
- a Sailfish OS phone
|
||||||
[bridge/README.md](bridge/README.md) (in inglese, perché è la parte che serve a
|
- an Aira Home heat pump, with an account in the Aira app
|
||||||
chi prova l'app su un altro impianto).
|
- the bridge running on a machine that stays on, in the same LAN
|
||||||
|
(a Raspberry Pi is plenty)
|
||||||
|
|
||||||
|
## The bridge
|
||||||
|
|
||||||
|
Sources, systemd user unit and full instructions:
|
||||||
|
[`bridge/README.md`](bridge/README.md). Short version:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd bridge
|
cd bridge
|
||||||
python3 -m venv .venv && . .venv/bin/activate
|
python3 -m venv .venv && . .venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
export AIRA_CONFIG_DIR="$HOME/.config/aira"
|
export AIRA_CONFIG_DIR="$HOME/.config/aira"
|
||||||
python setup_creds.py # email e password Aira: salvate in un file 600
|
python setup_creds.py # Aira email + password, saved in a 600 file
|
||||||
python aira_bridge.py --port 8790
|
python aira_bridge.py --port 8790
|
||||||
```
|
```
|
||||||
|
|
||||||
Al primo avvio il bridge genera un token in `$AIRA_CONFIG_DIR/aira_bridge.json`
|
The first start writes a random token to `$AIRA_CONFIG_DIR/aira_bridge.json`
|
||||||
(permessi 600): è il valore da incollare nelle impostazioni dell'app.
|
(mode 600): that value goes in the app settings, together with the LAN address of
|
||||||
API completa in [docs/PROTOCOL.md](docs/PROTOCOL.md).
|
the machine. Full API: [docs/PROTOCOL.md](docs/PROTOCOL.md).
|
||||||
|
|
||||||
## Impostazioni nell'app
|
## App settings
|
||||||
|
|
||||||
Indirizzo e token si configurano nella pagina **Impostazioni** (QSettings in
|
Address and token are configured in the **Settings** page (QSettings in
|
||||||
`~/.config/harbour/aira/aira.conf`): niente dati personali nel binario.
|
`~/.config/harbour/aira/aira.conf`): no personal data in the binary.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
Servono l'SDK Sailfish (`sfdk`) e un target aarch64 (Jolla Phone 2026):
|
Requires the Sailfish SDK (`sfdk`) and an aarch64 target (Jolla Phone 2026):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sfdk target install SailfishOS-5.0.0.62-aarch64
|
sfdk target install SailfishOS-5.0.0.62-aarch64
|
||||||
sfdk target use SailfishOS-5.0.0.62-aarch64
|
sfdk target use SailfishOS-5.0.0.62-aarch64
|
||||||
sfdk build # RPM in RPMS/
|
sfdk build # RPM in RPMS/
|
||||||
sfdk deploy # sul dispositivo collegato
|
sfdk deploy # to the connected device
|
||||||
```
|
```
|
||||||
|
|
||||||
Sorgenti dal tarball: `tar xzf harbour-aira-0.3.0.tar.gz` e compilare in una
|
From a source tarball (`harbour-aira-<version>.tar.gz`, if you were handed one):
|
||||||
cartella nuova (mai sopra una estrazione precedente: i Makefile restano stale).
|
extract it into a **new** directory (never on top of a previous extraction: the
|
||||||
Il log dell'app inizia con `harbour-aira v<versione> build <data>`: serve a
|
Makefiles stay stale), or simply clone this repository.
|
||||||
distinguere una build nuova da una stale senza indovinare.
|
The app log starts with `harbour-aira v<version> build <date>`, which tells a
|
||||||
|
fresh build from a stale one without guessing.
|
||||||
|
|
||||||
### Verifica locale, senza SDK
|
### Local checks, no SDK needed
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd tests && mkdir -p build && cd build
|
cd tests && mkdir -p build && cd build
|
||||||
~/Qt/5.15.2/gcc_64/bin/qmake ../core_test.pro && make && ./core_test
|
~/Qt/5.15.2/gcc_64/bin/qmake ../core_test.pro && make && ./core_test
|
||||||
```
|
```
|
||||||
|
|
||||||
Copre la logica pura (`src/airajson.cpp`): normalizzazione dell'indirizzo,
|
Covers the pure logic (`src/airajson.cpp`): address normalisation, summary
|
||||||
lettura del riassunto, etichette di stato, corpo dei comandi, messaggi
|
parsing, mode labels, command payloads, error messages.
|
||||||
d'errore. **La UI Silica si verifica solo sul dispositivo.**
|
**The Silica UI can only be verified on a device.**
|
||||||
|
|
||||||
## Struttura
|
## Status
|
||||||
|
|
||||||
|
Verified so far:
|
||||||
|
|
||||||
|
- **31/31** core checks (`tests/core_test.cpp`), compiled with no warnings
|
||||||
|
- **27/27** bridge checks, offline (`bridge/test_bridge_commands.py`) —
|
||||||
|
including that a zone command always carries every zone, and that absurd
|
||||||
|
values are refused (40 °C, zone 3, text)
|
||||||
|
- `qmllint` clean on every QML file. Note it validates syntax only: without the
|
||||||
|
Silica module an invalid Silica property passes silently
|
||||||
|
- read-only queries against one real Aira Home unit, through the bridge
|
||||||
|
- the bridge runs as a systemd user unit and comes back by itself after a kill
|
||||||
|
|
||||||
|
Not verified: **the app has not been built or run on a phone yet** — the first
|
||||||
|
build is up to whoever has the Sailfish SDK. The images in `docs/anteprima/` are
|
||||||
|
layout previews built from the real plant values, not screenshots.
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
|
||||||
|
- **Unofficial client**, not in the Jolla Store: it talks to Aira's API with
|
||||||
|
your own account. Terms of service and trademarks remain Aira's business — use
|
||||||
|
it at your own risk, on your own pump
|
||||||
|
- tested against **one** unit so far (firmware: outdoor unit 1.30.0, system
|
||||||
|
6.10.0, climate control 3.9.12); other units and other countries may expose
|
||||||
|
different fields
|
||||||
|
- cooling is reachable from the bridge but has no UI yet; schedules/curves and
|
||||||
|
firmware info are not implemented
|
||||||
|
- only a whitelist of commands is exposed: factory reset, firmware updates,
|
||||||
|
reboots and Wi-Fi provisioning are deliberately unreachable
|
||||||
|
- no command is ever sent to the plant by the tests
|
||||||
|
|
||||||
|
## Looking for testers
|
||||||
|
|
||||||
|
If you own an Aira heat pump and a Sailfish phone, and you are willing to run the
|
||||||
|
bridge on a machine at home, I would like to hear how it behaves on your unit:
|
||||||
|
|
||||||
|
- pump model and firmware versions (visible in the Aira app)
|
||||||
|
- how many heating zones you have (1 or 2), and whether cooling is configured
|
||||||
|
- the output of `python aira_probe.py --json` (it contains no credentials)
|
||||||
|
- anything that looks wrong next to what the Aira app shows at the same moment
|
||||||
|
- your country, since Aira may not expose the same fields in every market
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
harbour-aira.pro progetto qmake (icona, qml, docs)
|
harbour-aira.pro qmake project (icons, qml, docs)
|
||||||
src/airajson.{h,cpp} logica pura, testabile senza SDK
|
src/airajson.{h,cpp} pure logic, testable without the SDK
|
||||||
src/apiclient.{h,cpp} QNetworkAccessManager + watchdog di timeout
|
src/apiclient.{h,cpp} QNetworkAccessManager + timeout watchdog
|
||||||
src/settings.{h,cpp} QSettings su AppConfigLocation (sandbox SailJail)
|
src/settings.{h,cpp} QSettings in AppConfigLocation (SailJail sandbox)
|
||||||
src/main.cpp context properties: appSettings, api
|
src/main.cpp context properties: appSettings, api
|
||||||
qml/harbour-aira.qml ApplicationWindow + copertina
|
qml/harbour-aira.qml ApplicationWindow + cover
|
||||||
qml/pages/MainPage.qml dashboard e comandi acqua/riscaldamento
|
qml/pages/MainPage.qml dashboard and water/heating commands
|
||||||
qml/pages/CommandsPage.qml gli altri comandi, con lo stato attuale in cima
|
qml/pages/CommandsPage.qml the other commands, current state on top
|
||||||
qml/pages/SettingsPage.qml
|
qml/pages/SettingsPage.qml
|
||||||
qml/components/InfoRow.qml
|
qml/components/InfoRow.qml
|
||||||
qml/components/ActionButton.qml pulsante di comando, con conferma opzionale
|
qml/components/ActionButton.qml command button, optional confirmation
|
||||||
tests/core_test.cpp verifiche del nucleo (headless)
|
tests/core_test.cpp core checks (headless)
|
||||||
bridge/aira_bridge.py servizio HTTP/1.1 + JSON verso il cloud (gRPC)
|
bridge/ local HTTP bridge -> see bridge/README.md
|
||||||
bridge/setup_creds.py salva le credenziali Aira (file 600)
|
docs/PIANO.md project plan and decisions (Italian)
|
||||||
bridge/aira_probe.py sonda di sola lettura: login + stato impianto
|
docs/PROTOCOL.md bridge API
|
||||||
bridge/test_bridge_commands.py verifiche offline dei comandi (27 controlli)
|
docs/anteprima/ layout previews
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Licenza
|
## License
|
||||||
|
|
||||||
MIT.
|
MIT.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<rect class="box" x="20" y="60" width="180" height="80"/>
|
<rect class="box" x="20" y="60" width="180" height="80"/>
|
||||||
<text class="title" x="110" y="92" text-anchor="middle">Jolla · Sailfish</text>
|
<text class="title" x="110" y="92" text-anchor="middle">Jolla · Sailfish</text>
|
||||||
<text class="sub" x="110" y="112" text-anchor="middle">harbour-aira (QML/C++</text>
|
<text class="sub" x="110" y="112" text-anchor="middle">harbour-aira (QML/C++</text>
|
||||||
<text class="sub" x="110" y="128" text-anchor="middle">Qt 5.6, solo HTTP/1.1)</text>
|
<text class="sub" x="110" y="128" text-anchor="middle">Qt 5.6 · HTTP/1.1 only)</text>
|
||||||
|
|
||||||
<rect class="box" x="270" y="60" width="180" height="80"/>
|
<rect class="box" x="270" y="60" width="180" height="80"/>
|
||||||
<text class="title" x="360" y="92" text-anchor="middle">Bridge (server)</text>
|
<text class="title" x="360" y="92" text-anchor="middle">Bridge (server)</text>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Reference in New Issue
Block a user