0.5.0: riordino come l'app ufficiale (home, Dati, Impianto)

- home: temperatura esterna e in casa/assente in alto, messaggio di stato,
  scheda impianto con temperatura stanza e acqua calda; comandi sotto
- pagina Dati: calore prodotto, elettricita' usata, COP acqua calda e
  riscaldamento, risparmio, CO2 evitata, Smart Tariff Control (mese per mese)
- pagina Impianto: stato, termostato a parete, versioni software, errori
- bridge 0.4.0: rotta /stats (servizio statistiche del cloud) e blocco
  dispositivo in /summary (ID breve, versioni, termostato)
- C++: parseStats, formatNumber, formatMoney, monthLabel (separatore e
  valuta secondo la lingua); 61/61 verifiche del nucleo
- anteprime aggiornate (dati, impianto) in inglese e italiano
This commit is contained in:
2026-10-10 08:59:03 +02:00
parent ec7333a185
commit 4f279a18b0
30 changed files with 2355 additions and 353 deletions
+42
View File
@@ -120,6 +120,48 @@ int main(int argc, char *argv[])
check(AiraJson::errorMessage("", 502) == "HTTP error 502", "http status fallback");
check(AiraJson::errorMessage("", 0) == "server unreachable", "status 0 = unreachable");
// ---- parseStats (pagina "Dati") --------------------------------------- //
const QByteArray statsBody = R"({"ok":true,"stats":{"currency":"EUR","period_days":30,"month":{"label":"2026-10","heat_kwh":70.8,"energy_kwh":29.3,"cop_heating":null,"cop_dhw":4.42}}})";
bool okStats = false;
const QVariantMap st = AiraJson::parseStats(statsBody, &okStats);
check(okStats, "statistiche valide riconosciute");
check(st.value("currency").toString() == "EUR", "valuta letta");
check(st.value("period_days").toInt() == 30, "periodo letto");
check(qFuzzyCompare(st.value("month").toMap().value("heat_kwh").toDouble(), 70.8),
"calore prodotto letto");
check(st.value("month").toMap().value("cop_dhw").toDouble() == 4.42,
"COP acqua calda letto");
check(st.value("month").toMap().value("cop_heating").isNull(),
"efficienza riscaldamento assente resta nulla (nessun dato)");
check(AiraJson::parseStats(R"({"ok":false,"error":"token errato"})", &okStats).isEmpty() && !okStats,
"statistiche in errore -> vuoto");
check(AiraJson::parseStats("non sono json", &okStats).isEmpty() && !okStats,
"statistiche non JSON -> vuoto");
// ---- formatNumber / formatMoney / monthLabel -------------------------- //
QLocale::setDefault(QLocale(QLocale::Italian, QLocale::Italy));
check(AiraJson::formatNumber(QVariant(4.4213), 2) == "4,42", "due decimali (italiano)");
check(AiraJson::formatNumber(QVariant(29.3), 1) == "29,3", "un decimale (italiano)");
check(AiraJson::formatNumber(QVariant(), 2) == "—", "numero assente -> trattino");
// toCurrencyString usa lo spazio unificatore del locale: si controllano
// separatore e simbolo, non la stringa esatta.
const QString importoIt = AiraJson::formatMoney(QVariant(21.78), "EUR");
check(importoIt.contains("21,78") && importoIt.contains(QString::fromUtf8("€")),
"importo in italiano: virgola e simbolo");
check(AiraJson::formatMoney(QVariant(21.78), "eur") == importoIt,
"codice valuta minuscolo accettato");
check(AiraJson::formatMoney(QVariant(), "EUR") == "—", "importo assente -> trattino");
check(AiraJson::monthLabel("2026-10") == "ottobre 2026", "mese in italiano");
check(AiraJson::monthLabel("non-mese").isEmpty(), "chiave mese illeggibile -> vuoto");
check(AiraJson::monthLabel("2026-13").isEmpty(), "mese 13 -> vuoto");
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedKingdom));
check(AiraJson::formatNumber(QVariant(4.4213), 2) == "4.42", "due decimali (inglese)");
const QString importoEn = AiraJson::formatMoney(QVariant(21.78), "EUR");
check(importoEn.contains("21.78") && importoEn.contains(QString::fromUtf8("€")),
"importo in inglese: punto e simbolo");
check(AiraJson::monthLabel("2026-10") == "October 2026", "mese in inglese");
QLocale::setDefault(QLocale::c());
// ---- catalogo italiano: il .qm caricato davvero ------------------------ //
// Non basta che il .ts esista: qui si installa il .qm come fa main.cpp e si
// controlla che le stringhe sorgente inglesi diventino italiane. Il percorso