0.7.0: assenza con date, conto alla rovescia del boost, catalogo rifatto

- AwayDialog.qml: popup con inizio e fine (Dialog, ValueButton,
  DatePickerDialog: componenti e proprieta' verificati nella guida Silica
  dell'SDK). Il tasto Assenza apre il popup invece di mandare il comando;
- conto alla rovescia 30:00 -> 00:00 in Home e in copertina, con i secondi
  dell'impianto (AiraJson::countdown + timer che si riallinea a ogni lettura);
- commandPayload accetta campi extra (start/end);
- catalogo: lupdate leggeva solo i C++ e marcava 151 voci QML come obsolete.
  Ora src e qml espliciti: 197 stringhe, 0 non tradotte;
- versione 0.7.0 (spec, main.cpp), PROTOCOL, README/PIANO, post per il forum.
This commit is contained in:
Carlo
2026-10-11 12:05:59 +02:00
parent 6ef6f5bfd8
commit d2cb9d692f
17 changed files with 481 additions and 80 deletions
+43 -2
View File
@@ -149,8 +149,8 @@ check("senza termostato: nessun errore",
from pyairahome.commands import CommandBase # noqa: E402
boost = b.build_command("hot_water_boost_on")
check("boost: un'ora di default",
boost._message.hot_water_boost_duration.seconds == 3600,
check("boost: mezz'ora di default (richiesta dell'utente)",
boost._message.hot_water_boost_duration.seconds == 1800,
f"{boost._message.hot_water_boost_duration.seconds}s")
check("boost: durata scelta in minuti (90 -> 5400s)",
b.build_command("hot_water_boost_on", 90)
@@ -199,6 +199,47 @@ for cid in b.COMMANDS:
check(f"whitelist: {cid} si costruisce e si incarta", False,
f"{type(exc).__name__}: {exc}")
# --- 6) assenza con date, e conto alla rovescia del boost ----------------- #
from datetime import datetime, timedelta, timezone # noqa: E402
from zoneinfo import ZoneInfo # noqa: E402
ROMA = ZoneInfo("Europe/Rome")
def a_roma(t):
return t.ToDatetime(tzinfo=timezone.utc).astimezone(ROMA).strftime("%Y-%m-%d %H:%M")
date_di = b.build_command("away_mode_on", None, 1, ctx,
{"start": "2026-11-01", "end": "2026-11-08"})
m3 = date_di._message
check("assenza con date: inizio = 1 novembre a mezzanotte",
a_roma(m3.current_time) == "2026-11-01 00:00", a_roma(m3.current_time))
check("assenza con date: fine = mezzanotte dell'8 (da li' riparte)",
a_roma(m3.end_time) == "2026-11-08 00:00", a_roma(m3.end_time))
check("assenza con date: casa a 15 °C", abs(m3.target_room_temperature - 15.0) < 1e-6)
rifiuta("assenza: fine prima dell'inizio", "away_mode_on", None, 1, ctx,
{"start": "2026-11-08", "end": "2026-11-01"})
rifiuta("assenza: data illeggibile", "away_mode_on", None, 1, ctx,
{"start": "domani", "end": "2026-11-08"})
rifiuta("assenza: oltre 180 giorni", "away_mode_on", None, 1, ctx,
{"start": "2026-11-01", "end": "2028-11-08"})
fra = (datetime.now(timezone.utc) + timedelta(minutes=20)).isoformat().replace("+00:00", "Z")
check("boost in corso: restano ~1200 secondi",
abs(b.boost_restanti({"hot_water_boost": {"end_time": fra}}) - 1200) <= 3)
check("boost finito: zero secondi",
b.boost_restanti({"hot_water_boost": {"end_time": "2026-01-01T00:00:00Z"}}) == 0)
check("boost mai avviato: zero secondi", b.boost_restanti({}) == 0)
riassunto = b.summarize({"hot_water_boost": {"end_time": fra}})
check("riassunto: dice che il boost e' in corso", riassunto.get("boost_active") is True)
check("riassunto: porta i secondi del boost",
abs(riassunto.get("boost_remaining_seconds", 0) - 1200) <= 3,
str(riassunto.get("boost_remaining_seconds")))
check("riassunto senza boost: spento e zero",
b.summarize({}).get("boost_active") is False
and b.summarize({}).get("boost_remaining_seconds") == 0)
# --- esito ---------------------------------------------------------------- #
falliti = [n for n, ok, _ in esiti if not ok]
print(f"\n{len(esiti) - len(falliti)}/{len(esiti)} controlli superati")