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
+74 -7
View File
@@ -5,6 +5,7 @@
#include <QJsonObject>
#include <QJsonValue>
#include <QLocale>
#include <QStringList>
#include <QUrl>
namespace AiraJson {
@@ -47,6 +48,29 @@ QVariantMap parseSummary(const QByteArray &body, bool *ok)
return summary.toVariantMap();
}
QVariantMap parseStats(const QByteArray &body, bool *ok)
{
if (ok)
*ok = false;
QJsonParseError err;
const QJsonDocument doc = QJsonDocument::fromJson(body, &err);
if (err.error != QJsonParseError::NoError || !doc.isObject())
return QVariantMap();
const QJsonObject root = doc.object();
if (root.value(QStringLiteral("ok")).toBool() == false)
return QVariantMap();
const QJsonObject stats = root.value(QStringLiteral("stats")).toObject();
if (stats.isEmpty())
return QVariantMap();
if (ok)
*ok = true;
return stats.toVariantMap();
}
QString modeLabel(const QString &operatingStatus)
{
const QString s = operatingStatus;
@@ -92,20 +116,63 @@ QString modeLabel(const QString &operatingStatus)
}
QString formatValue(const QVariant &value)
{
// Un decimale: e' il caso delle temperature ("51,0").
return formatNumber(value, 1);
}
QString formatNumber(const QVariant &value, int decimals)
{
if (!value.isValid() || value.isNull())
return QStringLiteral("—");
bool isNumber = false;
const double d = value.toDouble(&isNumber);
if (isNumber)
// QLocale() = locale predefinito, che translator.cpp allinea alla lingua
// dell'app: separatore decimale col punto in inglese, con la virgola in
// italiano (QString::number darebbe sempre il punto).
return QLocale().toString(d, 'f', 1);
if (!isNumber)
return QStringLiteral("—");
const QString s = value.toString();
return s.isEmpty() ? QStringLiteral("—") : s;
// QLocale() = locale predefinito, che translator.cpp allinea alla lingua
// dell'app: separatore decimale col punto in inglese, con la virgola in
// italiano (QString::number darebbe sempre il punto).
return QLocale().toString(d, 'f', decimals < 0 ? 0 : decimals);
}
QString formatMoney(const QVariant &value, const QString &currencyCode)
{
if (!value.isValid() || value.isNull())
return QStringLiteral("—");
bool isNumber = false;
const double d = value.toDouble(&isNumber);
if (!isNumber)
return QStringLiteral("—");
// Posizione del simbolo e separatori seguono la lingua dell'app, non la
// valuta: "21,78 €" in italiano, "€21.78" in inglese.
const QString code = currencyCode.trimmed().toUpper();
QString symbol;
if (code == QLatin1String("EUR"))
symbol = QString::fromUtf8("€");
else if (!code.isEmpty())
symbol = code; // valuta senza simbolo noto: codice ISO
return QLocale().toCurrencyString(d, symbol);
}
QString monthLabel(const QString &yearMonth)
{
// "2026-10" -> "ottobre 2026": il nome del mese segue la lingua dell'app.
const QStringList parti = yearMonth.split(QLatin1Char('-'));
if (parti.size() != 2)
return QString();
bool annoOk = false;
bool meseOk = false;
const int anno = parti.at(0).toInt(&annoOk);
const int mese = parti.at(1).toInt(&meseOk);
if (!annoOk || !meseOk || mese < 1 || mese > 12)
return QString();
return QStringLiteral("%1 %2").arg(QLocale().monthName(mese, QLocale::LongFormat)).arg(anno);
}
QByteArray commandPayload(const QString &id, const QVariant &value)