diff --git a/qml/pages/CommandsPage.qml b/qml/pages/CommandsPage.qml index b98e420..e6486d9 100644 --- a/qml/pages/CommandsPage.qml +++ b/qml/pages/CommandsPage.qml @@ -2,90 +2,142 @@ import QtQuick 2.2 import Sailfish.Silica 1.0 import "../components" -// Comandi secondari: quelli che non stanno nella home. In cima lo stato -// attuale, cosi' non si preme alla cieca. +// Comandi impianto: i comandi che non stanno nella Home. +// +// Prima erano 12 pulsanti in quattro sezioni, otto dei quali in coppie +// accendi/spegni, con lo stato da leggere in sei righe piu' in alto. Ora ogni +// cosa che ha uno stato e' un interruttore: lo stato si vede e si cambia in un +// tocco solo (docs/UX-STUDIO.md). +// +// Gli interruttori NON anticipano l'impianto: `checked` segue l'ultimo dato +// vero (automaticCheck: false) e al tocco parte il comando. Se il comando non +// riesce, l'interruttore non si muove; se riesce, lo stato arriva da solo +// (ApiClient rilegge il riassunto due secondi dopo). Page { id: page allowedOrientations: Orientation.All - property string notice: "" - property bool noticeIsError: false - function raw(key) { return api.summary[key] } - function flag(key) { - return page.raw(key) ? qsTr("yes") : qsTr("no") + // La funzione riscaldamento e' abilitata quando i modi configurati della + // pompa la comprendono ("PUMP_MODE_STATE_HEATING"). + function heatingEnabled() { + return String(page.raw("configured_modes") || "").indexOf("HEATING") >= 0 } - function stato(key) { - return api.modeLabel(page.raw(key)) + Component.onCompleted: { + if (api.configured) + api.refresh() } - function showNotice(msg, isError) { - page.notice = msg - page.noticeIsError = isError === true - noticeTimer.restart() - } - - Timer { - id: noticeTimer - interval: 4000 - onTriggered: page.notice = "" - } - - Connections { - target: api - onErrorOccurred: page.showNotice(message, true) - onCommandFinished: page.showNotice(message, !ok) - } + // Fuori dai pulsanti/interruttori: dentro finirebbero nel loro contentItem. + RemorseItem { id: remorse } SilicaFlickable { anchors.fill: parent contentHeight: col.height + Theme.paddingLarge + PullDownMenu { + MenuItem { + text: qsTr("Refresh") + onClicked: api.refresh() + } + MenuItem { + text: qsTr("Data") + onClicked: pageStack.push(Qt.resolvedUrl("DataPage.qml")) + } + MenuItem { + text: qsTr("Plant") + onClicked: pageStack.push(Qt.resolvedUrl("PlantPage.qml")) + } + MenuItem { + text: qsTr("Settings") + onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) + } + } + Column { id: col width: parent.width spacing: Theme.paddingSmall - PageHeader { title: qsTr("Other commands") } + PageHeader { title: qsTr("Plant commands") } - Label { - visible: page.notice.length > 0 - x: Theme.horizontalPageMargin - width: parent.width - 2 * Theme.horizontalPageMargin - text: page.notice - color: page.noticeIsError ? Theme.errorColor : Theme.highlightColor - font.pixelSize: Theme.fontSizeSmall - wrapMode: Text.Wrap + // ---- modalita' -------------------------------------------------- // + SectionHeader { text: qsTr("Modes") } + + TextSwitch { + id: awaySwitch + text: qsTr("Away mode") + description: qsTr("Lowers the house and stops the hot water") + automaticCheck: false + checked: page.raw("away_mode") === true + onClicked: { + if (checked) + api.sendCommand("away_mode_off") + else + api.sendCommand("away_mode_on") + } } - // ---- stato attuale -------------------------------------------- // - SectionHeader { text: qsTr("Current state") } + TextSwitch { + text: qsTr("Forced heating") + description: qsTr("Heats at full power until you stop it") + automaticCheck: false + checked: page.raw("force_heating") === true + onClicked: { + if (checked) + api.sendCommand("force_heating_off") + else + api.sendCommand("force_heating_on") + } + } - InfoRow { label: qsTr("Away mode"); value: page.flag("away_mode") } - InfoRow { label: qsTr("Night mode"); value: page.flag("night_mode") } - InfoRow { label: qsTr("Forced heating"); value: page.flag("force_heating") } - InfoRow { label: qsTr("Hot water active"); value: page.flag("hot_water_heating") } - InfoRow { label: qsTr("Pump"); value: page.stato("pump_active_state") } - InfoRow { label: qsTr("Allowed modes"); value: page.stato("configured_modes") } + TextSwitch { + id: heatingSwitch + text: qsTr("Heating") + description: qsTr("The heating can turn on") + automaticCheck: false + checked: page.heatingEnabled() + onClicked: { + // Spegnere il riscaldamento ha conseguenze: si conferma. + if (checked) + remorse.execute(heatingSwitch, qsTr("Heating will be disabled"), + function() { api.sendCommand("heating_off") }) + else + api.sendCommand("heating_on") + } + } - // ---- assenza --------------------------------------------------- // - SectionHeader { text: qsTr("Away mode") } + // ---- acqua calda ------------------------------------------------ // + SectionHeader { text: qsTr("Domestic hot water") } + + TextSwitch { + id: waterSwitch + text: qsTr("Hot water active") + description: qsTr("The heat pump keeps the water in temperature") + automaticCheck: false + checked: page.raw("hot_water_heating") === true + onClicked: { + if (checked) + remorse.execute(waterSwitch, + qsTr("Domestic hot water will stop being heated"), + function() { api.sendCommand("hot_water_heating_off") }) + else + api.sendCommand("hot_water_heating_on") + } + } ActionButton { - text: qsTr("Turn away mode on") - onTriggered: api.sendCommand("away_mode_on") - } - ActionButton { - text: qsTr("Turn away mode off") - onTriggered: api.sendCommand("away_mode_off") + text: qsTr("Legionella cycle") + confirmText: qsTr("Starting the legionella cycle: it takes a while") + onTriggered: api.sendCommand("legionella_cycle") } - // ---- rumore ---------------------------------------------------- // + // ---- rumore ----------------------------------------------------- // SectionHeader { text: qsTr("Noise") } ActionButton { @@ -93,59 +145,13 @@ Page { onTriggered: api.sendCommand("night_mode_1h") } - // ---- riscaldamento --------------------------------------------- // - SectionHeader { text: qsTr("Heating") } - - ActionButton { - text: qsTr("Force heating") - confirmText: qsTr("Starting forced heating") - onTriggered: api.sendCommand("force_heating_on") - } - ActionButton { - text: qsTr("Stop forced heating") - onTriggered: api.sendCommand("force_heating_off") - } - ActionButton { - text: qsTr("Enable heating function") - onTriggered: api.sendCommand("heating_on") - } - ActionButton { - text: qsTr("Disable heating function") - confirmText: qsTr("Heating will be disabled") - onTriggered: api.sendCommand("heating_off") - } - - // ---- acqua calda ----------------------------------------------- // - SectionHeader { text: qsTr("Domestic hot water") } - - ActionButton { - text: qsTr("Legionella cycle") - confirmText: qsTr("Starting the legionella cycle: it takes a while") - onTriggered: api.sendCommand("legionella_cycle") - } - ActionButton { - text: qsTr("Enable DHW heating") - onTriggered: api.sendCommand("hot_water_heating_on") - } - ActionButton { - text: qsTr("Disable DHW heating") - confirmText: qsTr("Domestic hot water will stop being heated") - onTriggered: api.sendCommand("hot_water_heating_off") - } - - // ---- diagnostica ----------------------------------------------- // - SectionHeader { text: qsTr("Diagnostics") } - - ActionButton { - text: qsTr("Plant check") - onTriggered: api.sendCommand("ping") - } - - BusyIndicator { - visible: api.busy - anchors.horizontalCenter: parent.horizontalCenter - size: BusyIndicatorSize.Small - running: true + Label { + x: Theme.horizontalPageMargin + width: parent.width - 2 * Theme.horizontalPageMargin + text: qsTr("The switches show the current state of the plant: the Status section is not needed any more.") + color: Theme.secondaryColor + font.pixelSize: Theme.fontSizeExtraSmall + wrapMode: Text.Wrap } Label { @@ -156,11 +162,10 @@ Page { font.pixelSize: Theme.fontSizeExtraSmall wrapMode: Text.Wrap } + + Item { width: 1; height: Theme.paddingMedium } } } - Component.onCompleted: { - if (api.configured) - api.refresh() - } -} \ No newline at end of file + NoticeBar { } +} diff --git a/qml/pages/DataPage.qml b/qml/pages/DataPage.qml index 1e2805d..8cef821 100644 --- a/qml/pages/DataPage.qml +++ b/qml/pages/DataPage.qml @@ -109,15 +109,8 @@ Page { wrapMode: Text.Wrap } - Label { - visible: page.error.length > 0 - x: Theme.horizontalPageMargin - width: parent.width - 2 * Theme.horizontalPageMargin - text: page.error - color: Theme.errorColor - font.pixelSize: Theme.fontSizeSmall - wrapMode: Text.Wrap - } + // Gli errori non si mostrano piu' qui dentro: il messaggio arriva + // ancorato in fondo alla pagina (NoticeBar), dove si vede sempre. Label { visible: Object.keys(page.stats()).length === 0 && page.error.length === 0 @@ -242,6 +235,8 @@ Page { } } + NoticeBar { } + Component.onCompleted: { if (api.configured) { page.loading = true diff --git a/qml/pages/PlantPage.qml b/qml/pages/PlantPage.qml index 37f7bd3..6599745 100644 --- a/qml/pages/PlantPage.qml +++ b/qml/pages/PlantPage.qml @@ -71,6 +71,19 @@ Page { PageHeader { title: qsTr("Plant") } + // La frase estesa dell'app ufficiale ("Relax: la pompa di calore + // funziona regolarmente"): sta qui e non in Home, dove serve + // spazio per le due letture. La calcola il C++ (AiraJson::stateLong). + Label { + x: Theme.horizontalPageMargin + width: parent.width - 2 * Theme.horizontalPageMargin + text: api.stateLong() + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeMedium + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + // ---- stato ------------------------------------------------------ // SectionHeader { text: qsTr("Status") } @@ -86,6 +99,11 @@ Page { InfoRow { label: qsTr("Night mode"); value: page.flag("night_mode") } InfoRow { label: qsTr("Forced heating"); value: page.flag("force_heating") } InfoRow { label: qsTr("Inline heater"); value: page.flag("inline_heater") } + InfoRow { label: qsTr("Manual mode"); value: page.flag("manual_mode") } + InfoRow { label: qsTr("Defrost"); value: page.flag("outdoor_defrost") } + InfoRow { label: qsTr("Signature lights"); value: page.flag("signature_lights") } + InfoRow { label: qsTr("Pump"); value: api.modeLabel(page.raw("pump_active_state")) } + InfoRow { label: qsTr("Allowed modes"); value: api.modeLabel(page.raw("configured_modes")) } InfoRow { label: qsTr("Outdoor temperature"); value: page.temp("outdoor_temperature") } InfoRow { label: qsTr("Indoor temperature (pump)"); value: page.temp("indoor_temperature") } InfoRow { label: qsTr("Zones"); value: page.count("zones") } @@ -176,10 +194,20 @@ Page { value: page.count("errors_count") } + // ---- diagnostica ------------------------------------------------ // + SectionHeader { text: qsTr("Diagnostics") } + + ActionButton { + text: qsTr("Plant check") + onTriggered: api.sendCommand("ping") + } + Item { width: 1; height: Theme.paddingMedium } } } + NoticeBar { } + Component.onCompleted: { if (api.configured) api.refresh() diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml index 423e321..4ea99a6 100644 --- a/qml/pages/SettingsPage.qml +++ b/qml/pages/SettingsPage.qml @@ -128,19 +128,11 @@ Page { wrapMode: Text.Wrap } + // Un pulsante solo: salvare e provare erano due gesti per la stessa + // cosa (il secondo salvava comunque). Button { anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Save") - onClicked: { - page.save() - page.testResult = qsTr("settings saved") - page.testOk = true - } - } - - Button { - anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Test connection") + text: qsTr("Save and test") onClicked: page.testAndReport() } diff --git a/src/apiclient.cpp b/src/apiclient.cpp index 4eeeb21..4145463 100644 --- a/src/apiclient.cpp +++ b/src/apiclient.cpp @@ -17,6 +17,7 @@ ApiClient::ApiClient(Settings *settings, QObject *parent) , m_stale(false) , m_noticeIsError(false) , m_noticeTimer(0) + , m_refreshTimer(0) { // Il messaggio dell'ultimo comando (o dell'ultimo errore) vale per ogni // pagina: lo stato sta qui, si spegne da solo e la UI lo mostra ancorato @@ -40,6 +41,18 @@ ApiClient::ApiClient(Settings *settings, QObject *parent) setNotice(message, !ok); }); + // Dopo un comando riuscito lo stato si rilegge da solo: l'impianto applica + // in qualche istante, e senza questa rilettura gli interruttori della + // pagina dei comandi resterebbero indietro rispetto alla pompa. + m_refreshTimer = new QTimer(this); + m_refreshTimer->setSingleShot(true); + m_refreshTimer->setInterval(2000); + connect(m_refreshTimer, &QTimer::timeout, this, &ApiClient::refresh); + connect(this, &ApiClient::commandFinished, this, [this](bool ok, const QString &) { + if (ok) + m_refreshTimer->start(); + }); + if (m_settings) { // La UI deve riflettere subito il passaggio da "non configurato" a // "configurato" quando si salvano indirizzo o token. diff --git a/src/apiclient.h b/src/apiclient.h index 6468d14..d05c16c 100644 --- a/src/apiclient.h +++ b/src/apiclient.h @@ -105,6 +105,7 @@ private: QString m_notice; bool m_noticeIsError; QTimer *m_noticeTimer; + QTimer *m_refreshTimer; QVariantMap m_summary; QVariantMap m_stats; }; diff --git a/translations/harbour-aira-it.ts b/translations/harbour-aira-it.ts index 0a59ade..3546102 100644 --- a/translations/harbour-aira-it.ts +++ b/translations/harbour-aira-it.ts @@ -96,7 +96,7 @@ Heating the hot water - Sta scaldando l'acqua + Sta scaldando l'acqua @@ -111,12 +111,12 @@ Attention: one error on the plant - Attenzione: un errore sull'impianto + Attenzione: un errore sull'impianto Attention: %1 errors on the plant - Attenzione: %1 errori sull'impianto + Attenzione: %1 errori sull'impianto @@ -136,7 +136,7 @@ The heat pump is heating the hot water. - La pompa di calore sta scaldando l'acqua calda. + La pompa di calore sta scaldando l'acqua calda. @@ -152,66 +152,66 @@ ApiClient - + Bridge address not configured Indirizzo del bridge non configurato - - - + + + Set the bridge address and token Configura indirizzo e token del bridge - + Server unreachable (timeout) Server non raggiungibile (timeout) - - + + Unreadable response from the bridge Risposta del bridge non interpretabile - + Enter the bridge address Inserisci l'indirizzo del bridge - + Timeout: no response Timeout: nessuna risposta - + Unreachable (HTTP %1) Irraggiungibile (HTTP %1) - + Bridge reachable Bridge raggiungibile - - + + Bridge not configured Bridge non configurato - + Timeout: no statistics Timeout: statistiche non disponibili - + Timeout: command not sent Timeout: comando non inviato - + Command sent Comando inviato @@ -219,153 +219,117 @@ CommandsPage - - yes - sì - - - - no - no - - - - Other commands - Altri comandi - - - - Current state - Stato attuale - - - - + Away mode Assenza - - Night mode - Night mode - - - + Forced heating Riscaldamento forzato - + Hot water active Acqua calda attiva - - Pump - Pompa - - - - Allowed modes - Modalità consentite - - - - Turn away mode on - Attiva assenza - - - - Turn away mode off - Disattiva assenza - - - + Noise Rumore - + Night mode for one hour Night mode per un'ora - + Heating Riscaldamento - - Force heating - Forza riscaldamento + + Refresh + Aggiorna - - Starting forced heating - Avvio del riscaldamento forzato + + Data + Dati - - Stop forced heating - Ferma riscaldamento forzato + + Plant + Impianto - - Enable heating function - Abilita funzione riscaldamento + + Settings + Impostazioni - - Disable heating function - Disabilita funzione riscaldamento + + Plant commands + Comandi impianto - + + Modes + Modalità + + + + Lowers the house and stops the hot water + Abbassa la casa e spegne l'acqua calda + + + + Heats at full power until you stop it + Scalda a pieno regime finché non lo fermi + + + + The heating can turn on + Il riscaldamento può accendersi + + + Heating will be disabled Il riscaldamento verrà disabilitato - + Domestic hot water Acqua calda sanitaria - + + The heat pump keeps the water in temperature + Mantiene l'acqua in temperatura + + + Legionella cycle Ciclo antilegionella - + Starting the legionella cycle: it takes a while Avvio del ciclo antilegionella: richiede tempo + + + The switches show the current state of the plant: the Status section is not needed any more. + Gli interruttori mostrano lo stato attuale: la sezione Stato non serve più. + - Enable DHW heating - Abilita riscaldamento ACS - - - - Disable DHW heating - Disabilita riscaldamento ACS - - - Domestic hot water will stop being heated L'acqua calda smetterà di essere riscaldata - - Diagnostics - Diagnostica - - - - Plant check - Verifica impianto - - - + Commands go through the bridge and may take a few seconds. The status refreshes by itself afterwards. I comandi passano dal bridge e possono richiedere qualche secondo. Dopo l'invio lo stato si aggiorna da solo. @@ -421,97 +385,97 @@ Numeri calcolati da Aira nel cloud: l'app li legge soltanto dal tuo bridge. - + Reading the data... Lettura dei dati in corso... - + Month of %1 Mese di %1 - + This month Questo mese - + Heat produced Calore prodotto - + What the heat pump gave the house Quanto la pompa ha dato alla casa - + Electricity used Elettricità usata - + What the heat pump took from the grid Quanta ne ha presa dalla rete - + Efficiency, domestic hot water Efficienza, acqua calda - + Efficiency, heating Efficienza, riscaldamento - + Efficiency (COP) is how much heat the pump delivers for each unit of electricity: higher is better. L'efficienza (COP) è quanto calore la pompa produce per ogni unità di elettricità: più alta è, meglio è. - + Your savings, last %1 days I tuoi risparmi, ultimi %1 giorni - + Saved on the bill Risparmiati in bolletta - + Compared with a gas boiler Rispetto a una caldaia a gas - + CO₂ avoided CO₂ evitata - + Not released into the air Non immessa nell'aria - + Smart Tariff Control Smart Tariff Control - + Saving from heating when energy is cheaper Risparmio scaldando quando l'energia costa meno - + Efficiency month by month Efficienza mese per mese - + Savings month by month Risparmi mese per mese @@ -659,165 +623,200 @@ Impianto - + Status Stato - + Connection Connessione - + Mode Modalità - + Away mode Modalità assenza - + Night mode Modalità notte - + Forced heating Riscaldamento forzato - + Inline heater Resistenza interna - + + Manual mode + Modalità manuale + + + + Defrost + Sbrinamento + + + + Signature lights + Luci di firma + + + + Pump + Pompa + + + + Allowed modes + Modi consentiti + + + Outdoor temperature Temperatura esterna - + Indoor temperature (pump) Temperatura interna (pompa) - + Zones Zone - + Last reading Ultima lettura - + Accessories Accessori - + No wall thermostat Nessun termostato a parete - + Aira wall thermostat Termostato a parete Aira - + Zone %1 · Connected Zona %1 · Connesso - + Measured temperature Temperatura misurata - + Humidity Umidità - + Signal Segnale - + Battery Batteria - + low scarica - + ok ok - + Article Articolo - + Versions Versioni - + Device ID ID dispositivo - + Platform Piattaforma - + Connectivity Connettività - + Climate control Controllo climatico - + Outdoor unit Unità esterna - + Outdoor unit EEPROM EEPROM unità esterna - + Errors Errori - + No errors reported by the plant. Nessun errore segnalato dall'impianto. - + Errors reported Errori segnalati + + + Diagnostics + Diagnostica + + + + Plant check + Verifica impianto + SetpointsPage @@ -859,7 +858,7 @@ What the plant is keeping now - Quello che l'impianto mantiene adesso + Quello che l'impianto mantiene adesso @@ -934,7 +933,7 @@ Commands go through the bridge and may take a few seconds. The status refreshes by itself afterwards. - I comandi passano dal bridge e possono richiedere qualche secondo. Dopo l'invio lo stato si aggiorna da solo. + I comandi passano dal bridge e possono richiedere qualche secondo. Dopo l'invio lo stato si aggiorna da solo. @@ -987,19 +986,9 @@ Indirizzo e token si leggono sul server: il token è in aira_bridge.json accanto al bridge. Senza schema viene usato http://. - - Save - Salva - - - - settings saved - impostazioni salvate - - - - Test connection - Prova connessione + + Save and test + Salva e prova