diff --git a/README.md b/README.md index 624483e..db3d900 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ sfdk build # RPM in RPMS/ sfdk deploy # sul dispositivo collegato ``` -Sorgenti dal tarball: `tar xzf harbour-aira-0.2.0.tar.gz` e compilare in una +Sorgenti dal tarball: `tar xzf harbour-aira-0.2.1.tar.gz` e compilare in una cartella nuova (mai sopra una estrazione precedente: i Makefile restano stale). ### Verifica locale, senza SDK diff --git a/docs/PIANO.md b/docs/PIANO.md index 4ddee38..ce77ca1 100644 --- a/docs/PIANO.md +++ b/docs/PIANO.md @@ -100,6 +100,10 @@ Non verificabile qui (serve il suo PC + il telefono): build RPM, istallazione, U - **App 0.2.0**: sezione "Riscaldamento" nella dashboard (temperatura in casa, obiettivo, stato zona, slider 5–30 °C a passi di 0,5 con conferma) e copertina con la temperatura di casa. Aggiunte le etichette per gli stati `PUMP_MODE_STATE_*`. Nucleo: **29/29**. +- **App 0.2.1** (richiesta dell'utente): la temperatura della casa diventa il dato + principale **in cima** alla home (valore grande, barra, obiettivo, slider e pulsante), + l'acqua calda sanitaria scende in una propria sezione sotto, lo stato impianto va in + fondo. Stessa gerarchia nella copertina: casa in grande, ACS in una riga secondaria. - **F3 fatto**: `harbour-aira-bridge.service` (systemd utente, `enable --now`, parte al boot grazie a `Linger=yes`, `Restart=on-failure` verificato con SIGKILL → riparte in ~10 s) e regola `52-aira-bridge-lan.conf` in `/etc/nftables.d/`. Il firewall YunoHost ha diff --git a/docs/anteprima/copertina.png b/docs/anteprima/copertina.png index bd5ccd9..598e421 100644 Binary files a/docs/anteprima/copertina.png and b/docs/anteprima/copertina.png differ diff --git a/docs/anteprima/dashboard.png b/docs/anteprima/dashboard.png index 33f0500..6164799 100644 Binary files a/docs/anteprima/dashboard.png and b/docs/anteprima/dashboard.png differ diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml index 4aead36..68e1f60 100644 --- a/qml/cover/CoverPage.qml +++ b/qml/cover/CoverPage.qml @@ -29,7 +29,7 @@ CoverBackground { Label { width: parent.width horizontalAlignment: Text.AlignHCenter - text: cover.t("dhw_current") + " °C" + text: cover.t("zone1_temperature") + " °C" color: Theme.primaryColor font.pixelSize: Theme.fontSizeHuge } @@ -37,7 +37,7 @@ CoverBackground { Label { width: parent.width horizontalAlignment: Text.AlignHCenter - text: qsTr("ACS · obiettivo %1 °C").arg(cover.t("dhw_target")) + text: qsTr("casa · obiettivo %1 °C").arg(cover.t("zone1_setpoint")) color: Theme.secondaryColor font.pixelSize: Theme.fontSizeExtraSmall truncationMode: TruncationMode.Fade @@ -46,7 +46,7 @@ CoverBackground { Label { width: parent.width horizontalAlignment: Text.AlignHCenter - text: qsTr("casa %1 °C · obiettivo %2 °C").arg(cover.t("zone1_temperature")).arg(cover.t("zone1_setpoint")) + text: qsTr("ACS %1 °C · obiettivo %2 °C").arg(cover.t("dhw_current")).arg(cover.t("dhw_target")) color: Theme.secondaryColor font.pixelSize: Theme.fontSizeExtraSmall truncationMode: TruncationMode.Fade diff --git a/qml/pages/MainPage.qml b/qml/pages/MainPage.qml index 75bc29e..22dbb7f 100644 --- a/qml/pages/MainPage.qml +++ b/qml/pages/MainPage.qml @@ -139,46 +139,7 @@ Page { onClicked: page.openSettings() } - // ---- acqua calda ---------------------------------------------- // - Label { - visible: api.configured - anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Acqua calda sanitaria") - color: Theme.secondaryColor - font.pixelSize: Theme.fontSizeSmall - } - - Label { - visible: api.configured - anchors.horizontalCenter: parent.horizontalCenter - text: page.temp("dhw_current") + " °C" - color: Theme.primaryColor - font.pixelSize: Theme.fontSizeHuge - } - - ProgressBar { - visible: api.configured - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - 4 * Theme.horizontalPageMargin - minimumValue: 20 - maximumValue: 65 - value: { - var v = page.raw("dhw_current") - return (v === undefined || v === null) ? 0 : Number(v) - } - } - - Label { - visible: api.configured - anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("obiettivo %1 °C").arg(page.temp("dhw_target")) - color: Theme.secondaryColor - font.pixelSize: Theme.fontSizeExtraSmall - } - - // ---- riscaldamento: temperatura della casa --------------------- // - SectionHeader { visible: api.configured; text: qsTr("Riscaldamento") } - + // ---- casa: temperatura della casa (dato principale) ------------ // Label { visible: api.configured anchors.horizontalCenter: parent.horizontalCenter @@ -192,25 +153,34 @@ Page { anchors.horizontalCenter: parent.horizontalCenter text: page.temp("zone1_temperature") + " °C" color: Theme.primaryColor - font.pixelSize: Theme.fontSizeLarge + font.pixelSize: Theme.fontSizeHuge } - InfoRow { + ProgressBar { visible: api.configured - label: qsTr("Obiettivo casa") - value: page.temp("zone1_setpoint") + " °C" + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - 4 * Theme.horizontalPageMargin + minimumValue: 5 + maximumValue: 30 + value: { + var v = page.raw("zone1_temperature") + return (v === undefined || v === null) ? 0 : Number(v) + } } - InfoRow { + + Label { visible: api.configured - label: qsTr("Stato riscaldamento") - value: api.modeLabel(page.raw("zone1_mode")) + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("obiettivo %1 °C · %2").arg(page.temp("zone1_setpoint")).arg(api.modeLabel(page.raw("zone1_mode"))) + color: Theme.secondaryColor + font.pixelSize: Theme.fontSizeExtraSmall } Slider { id: casaSlider visible: api.configured width: parent.width - label: qsTr("Nuovo obiettivo") + label: qsTr("Nuovo obiettivo casa") minimumValue: 5 maximumValue: 30 stepSize: 0.5 @@ -237,27 +207,39 @@ Page { function() { api.sendCommand("set_heating_setpoint", page.targetCasa) }) } - // ---- stato impianto ------------------------------------------- // - SectionHeader { visible: api.configured; text: qsTr("Impianto") } + // ---- acqua calda sanitaria ------------------------------------- // + SectionHeader { visible: api.configured; text: qsTr("Acqua calda sanitaria") } - InfoRow { visible: api.configured; label: qsTr("Temperatura esterna"); value: page.temp("outdoor_temperature") + " °C" } - InfoRow { visible: api.configured; label: qsTr("Temperatura interna (pompa)"); value: page.temp("indoor_temperature") + " °C" } - InfoRow { visible: api.configured; label: qsTr("Modalità"); value: api.modeLabel(page.raw("operating_status")) } - InfoRow { visible: api.configured; label: qsTr("Assenza"); value: page.flag("away_mode") } - InfoRow { visible: api.configured; label: qsTr("Night mode"); value: page.flag("night_mode") } - InfoRow { visible: api.configured; label: qsTr("Riscaldamento forzato"); value: page.flag("force_heating") } - InfoRow { visible: api.configured; label: qsTr("Resistenze integrative"); value: page.flag("inline_heater") } - InfoRow { visible: api.configured; label: qsTr("Zone"); value: page.count("zones") } - InfoRow { visible: api.configured; label: qsTr("Errori"); value: page.count("errors_count") } - InfoRow { visible: api.configured; label: qsTr("Aggiornato alle"); value: page.shortTime(page.raw("pump_updated")) } + Label { + visible: api.configured + anchors.horizontalCenter: parent.horizontalCenter + text: page.temp("dhw_current") + " °C" + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeLarge + } - // ---- comandi --------------------------------------------------- // - SectionHeader { visible: api.configured; text: qsTr("Comandi acqua calda") } + ProgressBar { + visible: api.configured + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - 4 * Theme.horizontalPageMargin + minimumValue: 20 + maximumValue: 65 + value: { + var v = page.raw("dhw_current") + return (v === undefined || v === null) ? 0 : Number(v) + } + } + + InfoRow { + visible: api.configured + label: qsTr("Obiettivo acqua calda") + value: page.temp("dhw_target") + " °C" + } Slider { visible: api.configured width: parent.width - label: qsTr("Obiettivo acqua calda") + label: qsTr("Nuovo obiettivo") minimumValue: 45 maximumValue: 60 stepSize: 1 @@ -312,6 +294,20 @@ Page { function() { api.sendCommand("hot_water_boost_off") }) } + // ---- stato impianto ------------------------------------------- // + SectionHeader { visible: api.configured; text: qsTr("Impianto") } + + InfoRow { visible: api.configured; label: qsTr("Temperatura esterna"); value: page.temp("outdoor_temperature") + " °C" } + InfoRow { visible: api.configured; label: qsTr("Temperatura interna (pompa)"); value: page.temp("indoor_temperature") + " °C" } + InfoRow { visible: api.configured; label: qsTr("Modalità"); value: api.modeLabel(page.raw("operating_status")) } + InfoRow { visible: api.configured; label: qsTr("Assenza"); value: page.flag("away_mode") } + InfoRow { visible: api.configured; label: qsTr("Night mode"); value: page.flag("night_mode") } + InfoRow { visible: api.configured; label: qsTr("Riscaldamento forzato"); value: page.flag("force_heating") } + InfoRow { visible: api.configured; label: qsTr("Resistenze integrative"); value: page.flag("inline_heater") } + InfoRow { visible: api.configured; label: qsTr("Zone"); value: page.count("zones") } + InfoRow { visible: api.configured; label: qsTr("Errori"); value: page.count("errors_count") } + InfoRow { visible: api.configured; label: qsTr("Aggiornato alle"); value: page.shortTime(page.raw("pump_updated")) } + BusyIndicator { visible: api.busy anchors.horizontalCenter: parent.horizontalCenter diff --git a/rpm/harbour-aira.spec b/rpm/harbour-aira.spec index ce03b83..c9b7f5c 100644 --- a/rpm/harbour-aira.spec +++ b/rpm/harbour-aira.spec @@ -1,6 +1,6 @@ Name: harbour-aira Summary: Control your Aira heat pump through a local bridge -Version: 0.2.0 +Version: 0.2.1 Release: 1 Group: Qt/Qt License: MIT diff --git a/scripts/render_mockups.py b/scripts/render_mockups.py index dbfd21f..76171b8 100644 --- a/scripts/render_mockups.py +++ b/scripts/render_mockups.py @@ -118,44 +118,41 @@ def main_screen() -> Image.Image: page_header(d, "Aira") y = 156 - c = "Acqua calda" - d.text(((W - d.textlength(c, font=font(26))) / 2, y), c, font=font(26), fill=SECONDARY) + # ---- casa: il dato principale, in cima ------------------------------- # + c = "Temperatura in casa" + d.text(((W - d.textlength(c, font=font(24))) / 2, y), c, font=font(24), fill=SECONDARY) y += 40 - big = "51,0 °C" + big = "23,0 °C" d.text(((W - d.textlength(big, font=font(92, bold=True))) / 2, y), big, font=font(92, bold=True), fill=PRIMARY) y += 116 - progress(d, y, (51.0 - 20) / (65 - 20)) + progress(d, y, (23.0 - 5) / (30 - 5)) y += 22 - t = "obiettivo 55,0 °C" + t = "obiettivo 19,0 °C · Riscaldamento" d.text(((W - d.textlength(t, font=font(24))) / 2, y), t, font=font(24), fill=SECONDARY) - - # ---- riscaldamento: temperatura della casa -------------------------- # - y = section_header(d, y + 44, "Riscaldamento") - c = "Temperatura in casa" - d.text(((W - d.textlength(c, font=font(24))) / 2, y), c, font=font(24), fill=SECONDARY) - y += 34 - big2 = "23,0 °C" - d.text(((W - d.textlength(big2, font=font(64, bold=True))) / 2, y), - big2, font=font(64, bold=True), fill=PRIMARY) - y += 88 - y = label_pair(d, y, "Obiettivo casa", "19,0 °C") - y = label_pair(d, y, "Stato riscaldamento", "Riscaldamento") - y = slider(d, y + 14, (19.0 - 5) / (30 - 5), "19,0 °C", "Nuovo obiettivo") + y += 46 + y = slider(d, y, (19.0 - 5) / (30 - 5), "19,0 °C", "Nuovo obiettivo casa") y = button(d, y, "Imposta casa a 19,0 °C", primary=True) - y = section_header(d, y + 24, "Impianto") - y = label_pair(d, y, "Esterna", "18,0 °C") - y = label_pair(d, y, "Interna (pompa)", "22,5 °C") - y = label_pair(d, y, "Modalità", "Automatico") - y = label_pair(d, y, "Errori", "0") - - y = section_header(d, y + 24, "Comandi acqua calda") - y = slider(d, y, (55.0 - 45) / (60 - 45), "55 °C") + # ---- acqua calda sanitaria ------------------------------------------- # + y = section_header(d, y + 24, "Acqua calda sanitaria") + acs = "51,0 °C" + d.text(((W - d.textlength(acs, font=font(56, bold=True))) / 2, y), + acs, font=font(56, bold=True), fill=PRIMARY) + y += 76 + progress(d, y, (51.0 - 20) / (65 - 20)) + y += 22 + y = label_pair(d, y, "Obiettivo acqua calda", "55,0 °C") + y = slider(d, y + 10, (55.0 - 45) / (60 - 45), "55 °C") y = button(d, y, "Imposta obiettivo ACS", primary=True) y = button(d, y, "Avvia boost ACS") y = button(d, y, "Ferma boost ACS") + # ---- stato impianto (scorre sotto) ----------------------------------- # + y = section_header(d, y + 24, "Impianto") + y = label_pair(d, y, "Esterna", "18,0 °C") + y = label_pair(d, y, "Interna (pompa)", "22,5 °C") + d.text(((W - d.textlength("Trascina verso il basso per aggiornare", font=font(22))) / 2, y + 8), "Trascina verso il basso per aggiornare", font=font(22), fill=SECONDARY) @@ -206,19 +203,16 @@ def cover_screen() -> Image.Image: d.line([(card[0] + 34, card[1] + 100), (card[2] - 34, card[1] + 100)], fill=BTN_EDGE, width=2) y = card[1] + 132 - big = "51,0 °C" + big = "23,0 °C" d.text(((W - d.textlength(big, font=font(76, bold=True))) / 2, y), big, font=font(76, bold=True), fill=PRIMARY) y += 108 - for lab, val in (("obiettivo", "55,0 °C"), ("esterna", "18,0 °C")): + for lab, val in (("obiettivo", "19,0 °C"), ("ACS", "51,0 °C"), ("esterna", "18,0 °C")): d.text((card[0] + 34, y), lab, font=font(24), fill=SECONDARY) vw = d.textlength(val, font=font(24)) d.text((card[2] - 34 - vw, y), val, font=font(24), fill=PRIMARY) y += 52 - vw = d.textlength("23,0 °C", font=font(24)) - d.text((card[0] + 34, y), "casa", font=font(24), fill=SECONDARY) - d.text((card[2] - 34 - vw, y), "23,0 °C", font=font(24), fill=PRIMARY) - d.text((card[0] + 34, card[3] - 60), "acqua calda sanitaria", font=font(22), fill=SECONDARY) + d.text((card[0] + 34, card[3] - 60), "temperatura in casa", font=font(22), fill=SECONDARY) return img