Files

6.3 KiB
Raw Permalink Blame History

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. null significa "nessun dato": su un impianto recente il riscaldamento può non averne ancora (mentre l'acqua calda sì). label è il mese AAAA-MM.
  • cop_monthly e monthly_savings: le serie mese per mese, per il dettaglio.
  • savings: risparmio in bolletta sugli ultimi period_days giorni (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": "..."}.