La copia del bridge dentro l'app va tenuta allineata: contiene la correzione del boost, dell'assenza, di ACS off e del riscaldamento forzato, che la libreria accetta solo con un argomento. Il pacchetto Sailfish non cambia (il bridge non entra nell'RPM), quindi non serve ricostruire l'RPM.
6.4 KiB
API del bridge
Servizio: ~/workspace/Carlo/Aira/aira_bridge.py (Python, solo pyairahome).
Traduce HTTP/1.1 + JSON in gRPC verso il cloud Aira. Tutte le rotte tranne
/health richiedono:
Authorization: Bearer <token>
Token: generato al primo avvio in ~/.hermes/profiles/carlo/aira_bridge.json.
GET /health
Nessuna autenticazione: serve a distinguere "server irraggiungibile" da "token sbagliato".
{"ok": true, "version": "0.4.0"}
GET /summary
Valori pronti per la dashboard.
{
"ok": true,
"summary": {
"outdoor_temperature": 18.1,
"indoor_temperature": 22.5,
"dhw_current": 51.0,
"dhw_target": 55.0,
"operating_status": "OPERATING_STATUS_AUTO",
"away_mode": false,
"night_mode": false,
"manual_mode": false,
"force_heating": false,
"force_heating_remaining": "0s",
"inline_heater": false,
"hot_water_heating": true,
"configured_modes": "PUMP_MODE_STATE_HEATING",
"pump_active_state": "PUMP_ACTIVE_STATE_IDLE",
"outdoor_defrost": false,
"signature_lights": true,
"led_pattern": "LED_PATTERN_NORMAL",
"zones": 1,
"zone1_setpoint": 19.0,
"zone1_temperature": 23.0,
"zone1_mode": "PUMP_MODE_STATE_HEATING",
"zone1_cooling_setpoint": 22.0,
"zone2_setpoint": 0.0,
"zone2_temperature": 0.0,
"zone2_mode": "PUMP_MODE_STATE_IDLE",
"zone2_cooling_setpoint": 0.0,
"errors_count": 0,
"pump_updated": "2026-10-09 18:52:17.199340",
"device_id": "1f4b26",
"device_uuid": "1f4b26c4-1800-4b21-8ea2-236925a92aeb",
"version_platform": "6.10.0",
"version_connectivity": "3.30.1",
"version_climate_control": "3.9.12",
"version_outdoor_unit": "1.30.0",
"version_outdoor_eeprom": "0.114.0",
"connected_via": "Cloud",
"timezone": "Europe/Rome",
"thermostats_count": 1,
"thermostat_zone": 1,
"thermostat_article": "201884",
"thermostat_temperature": 22.8,
"thermostat_humidity": 73.3,
"thermostat_rssi": -79,
"thermostat_battery_low": false
}
}
Il blocco del dispositivo serve alle pagine "Impianto" e "Accessori" dell'app:
device_id sono le prime 6 cifre dell'UUID (come le mostra l'app ufficiale),
connected_via è la via di connessione, le version_* sono le versioni del
software e i campi thermostat_* descrivono il termostato a parete (assenti se
l'impianto non ne ha: in quel caso thermostats_count è 0 e gli altri sono
null). Il termostato manda i decimali come interi (228 = 22,8 °C): la
conversione la fa il bridge, non l'app.
GET /stats
I numeri che Aira calcola nel cloud: sono gli stessi della scheda "Dati"
dell'app ufficiale (verificati sullo stesso periodo). Il bridge li tiene in
cache per 10 minuti (ttl), perché sono quattro chiamate e cambiano
lentamente.
{
"ok": true,
"stats": {
"period_days": 30,
"currency": "EUR",
"month": {
"label": "2026-10",
"heat_kwh": 70.8,
"energy_kwh": 29.3,
"cop_heating": null,
"cop_dhw": 4.42
},
"cop_monthly": [
{"month": "2026-09", "heating_cop": null, "dhw_cop": 4.42, "avg_outdoor": 21.8}
],
"insights": [
{"date": "2026-10-01", "energy_kwh": 29.3, "heat_kwh": 70.8}
],
"savings": {"amount": 21.78, "co2_kg": 38.1, "show": true, "points": 31},
"smart_tariff": {"amount": 2.71, "hours": 180},
"monthly_savings": [{"month": "2026-09", "amount": 3.52}]
}
}
month: l'ultimo periodo con dati — calore prodotto (heat_kwh), elettricità usata (energy_kwh) ed efficienza (COP) di acqua calda e riscaldamento.nullsignifica "nessun dato": su un impianto recente il riscaldamento può non averne ancora (mentre l'acqua calda sì).labelè il meseAAAA-MM.cop_monthlyemonthly_savings: le serie mese per mese, per il dettaglio.savings: risparmio in bolletta sugli ultimiperiod_daysgiorni (30), CO₂ evitata e numero di punti usati dal calcolo;showè il permesso di Aira a mostrare il dato.smart_tariff: il risparmio del mese mobile dovuto allo Smart Tariff Control.- Questa rotta è di sola lettura: non invia nessun comando all'impianto.
GET /state
Stato completo e grezzo dal cloud (42 campi: curve climatiche per zona, versioni firmware, schedulazioni, errori). Per diagnostica.
GET /commands
Elenco dei comandi ammessi con i loro parametri.
POST /command
{"id": "set_hot_water_target", "value": 55}
Risposta: {"ok": true, "id": "...", "result": {...}}.
Comandi ammessi
| id | value | effetto |
|---|---|---|
ping |
— | verifica che l'impianto risponda |
set_hot_water_target |
45–60 | temperatura ACS obiettivo |
set_heating_setpoint |
5–30 | temperatura obiettivo della casa (zona, default 1) |
set_cooling_setpoint |
5–30 | temperatura obiettivo del raffrescamento (zona, default 1) |
hot_water_boost_on / hot_water_boost_off |
minuti, 15–240 (default 60) | boost ACS una tantum |
hot_water_heating_on / hot_water_heating_off |
minuti, 15–1440 (default 60) | riscaldamento ACS |
away_mode_on / away_mode_off |
giorni, 1–60 (default 7) | modalità assenza |
night_mode_1h |
— | night mode per un'ora |
force_heating_on / force_heating_off |
minuti, 15–240 (default 60) | riscaldamento forzato |
heating_on / heating_off |
— | funzione riscaldamento |
legionella_cycle |
— | ciclo antilegionella |
Riscaldamento: come si invia il comando per zona
Il messaggio ZoneTemperatures contiene sempre entrambe le zone:
{"id": "set_heating_setpoint", "value": 21.5, "zone": 1}
zone è opzionale (default 1) e accetta 1 o 2. La zona che non si sta
modificando viene rimandata con il suo valore attuale, letto dall'ultimo
stato disponibile: inviarla a zero la spegnerebbe. Per questo il bridge tiene
in memoria l'ultima lettura, e il comando va costruito con quel contesto.
Il campo kind distingue riscaldamento (1) da raffrescamento (2).
Perché una whitelist
La libreria espone ~90 comandi, fra cui FactoryReset, InstallFirmware,
RebootDevice, RotateCertificate, SetWifiCredentials. Restano fuori di
proposito: un client mobile non deve poter riformattare l'impianto per un tap
sbagliato.
Errori
| stato | significato |
|---|---|
| 400 | comando non ammesso, oppure valore fuori intervallo |
| 401 | token mancante o errato |
| 404 | rotta sconosciuta |
| 502 | il bridge non ha potuto parlare con il cloud (dettaglio in error) |
Formato: {"ok": false, "error": "..."}.