From e0dcf1f830fdb1c0bcbba9a387236e72cded56a6 Mon Sep 17 00:00:00 2001 From: Carlo Date: Sat, 10 Oct 2026 08:05:23 +0200 Subject: [PATCH] README in inglese per il repo pubblico; la versione italiana resta come README.it.md --- README.it.md | 126 ++++++++++++++++++++++++++++++ README.md | 174 +++++++++++++++++++++++++++++------------- docs/architettura.svg | 2 +- 3 files changed, 248 insertions(+), 54 deletions(-) create mode 100644 README.it.md diff --git a/README.it.md b/README.it.md new file mode 100644 index 0000000..9aa8d55 --- /dev/null +++ b/README.it.md @@ -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). + +![architettura](docs/architettura.svg) + +## 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, 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. diff --git a/README.md b/README.md index ee0acb1..05a7747 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,171 @@ # 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). +Native Sailfish OS app to monitor and control an **Aira Home** heat pump: plant +status, domestic hot water, and the house target temperature per zone. -![architettura](docs/architettura.svg) +*Versione italiana: [README.it.md](README.it.md)* -## Perché c'è un bridge (e non una chiamata diretta al cloud) +![architecture](docs/architettura.svg) -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. +## What it does -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. +- **Home**: house temperature and room temperature at the top, target with a + 5–30 °C slider (0.5 °C steps) and a confirmation before the command is sent; + 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 | +|---|---|---| +| ![home](docs/anteprima/dashboard.png) | ![commands](docs/anteprima/comandi.png) | ![cover](docs/anteprima/copertina.png) | + +## 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 ``` -## Il bridge +## Requirements -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). +- a Sailfish OS phone +- an Aira Home heat pump, with an account in the Aira app +- 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 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 setup_creds.py # Aira email + password, saved in a 600 file 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). +The first start writes a random token to `$AIRA_CONFIG_DIR/aira_bridge.json` +(mode 600): that value goes in the app settings, together with the LAN address of +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 -`~/.config/harbour/aira/aira.conf`): niente dati personali nel binario. +Address and token are configured in the **Settings** page (QSettings in +`~/.config/harbour/aira/aira.conf`): no personal data in the binary. ## 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 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 +sfdk deploy # to the connected device ``` -Sorgenti dal tarball: `tar xzf harbour-aira-0.3.0.tar.gz` e compilare in una -cartella nuova (mai sopra una estrazione precedente: i Makefile restano stale). -Il log dell'app inizia con `harbour-aira v build `: serve a -distinguere una build nuova da una stale senza indovinare. +From a source tarball (`harbour-aira-.tar.gz`, if you were handed one): +extract it into a **new** directory (never on top of a previous extraction: the +Makefiles stay stale), or simply clone this repository. +The app log starts with `harbour-aira v build `, which tells a +fresh build from a stale one without guessing. -### Verifica locale, senza SDK +### Local checks, no SDK needed ```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.** +Covers the pure logic (`src/airajson.cpp`): address normalisation, summary +parsing, mode labels, command payloads, error messages. +**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) -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) +harbour-aira.pro qmake project (icons, qml, docs) +src/airajson.{h,cpp} pure logic, testable without the SDK +src/apiclient.{h,cpp} QNetworkAccessManager + timeout watchdog +src/settings.{h,cpp} QSettings in AppConfigLocation (SailJail sandbox) 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/harbour-aira.qml ApplicationWindow + cover +qml/pages/MainPage.qml dashboard and water/heating commands +qml/pages/CommandsPage.qml the other commands, current state on top 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 +qml/components/ActionButton.qml command button, optional confirmation +tests/core_test.cpp core checks (headless) +bridge/ local HTTP bridge -> see bridge/README.md +docs/PIANO.md project plan and decisions (Italian) +docs/PROTOCOL.md bridge API +docs/anteprima/ layout previews ``` -## Licenza +## License MIT. diff --git a/docs/architettura.svg b/docs/architettura.svg index d59590e..159a459 100644 --- a/docs/architettura.svg +++ b/docs/architettura.svg @@ -15,7 +15,7 @@ Jolla · Sailfish harbour-aira (QML/C++ - Qt 5.6, solo HTTP/1.1) + Qt 5.6 · HTTP/1.1 only) Bridge (server)