6.3 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 |
— | boost ACS una tantum |
hot_water_heating_on / hot_water_heating_off |
— | riscaldamento ACS |
away_mode_on / away_mode_off |
— | modalità assenza |
night_mode_1h |
— | night mode per un'ora |
force_heating_on / force_heating_off |
— | 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": "..."}.