0.6.9: azione sulla copertina (P8) — il boost senza aprire l'app
Sulla copertina compare l'icona del boost (CoverAction). Una sola azione: la documentazione Silica ammette piu' di una azione ma dice che lo stile delle app di sistema ne mostra una. CoverBackground eredita da Cover, quindi l'elenco dichiarato li' si attacca; l'icona e' iconSource (non icon.source del Button: sbagliarlo non da' errore, l'icona semplicemente non compare). La riga in fondo si ancora a Cover.coverActionArea, cosi' l'icona non la copre. Nuovo controllo statico tests/cover_action_check.py: elenco figlio diretto del Cover, una sola azione, icona che esiste, id del comando presente nella tabella del bridge. Il componente e' nominato nella guida Silica dell'SDK di Carlo.
This commit is contained in:
@@ -166,6 +166,7 @@ nessuno. Impiegano millisecondi e dicono il file di cui non sono contenti:
|
||||
python3 tests/qml_check_bindings.py qml # nessuna proprietà assegnata due volte
|
||||
python3 tests/qml_focus_check.py # un tocco apre solo quello che ha toccato
|
||||
python3 tests/label_fit_check.py # le etichette dei tasti ci stanno
|
||||
python3 tests/cover_action_check.py # l'azione di copertina c'e' ed e' collegata
|
||||
```
|
||||
|
||||
## Stato
|
||||
@@ -236,6 +237,7 @@ tests/ui_contract_check.py chiavi dei QML contro quelle del bridge (bridge atti
|
||||
tests/qml_check_bindings.py assegnazioni duplicate di proprieta' nei QML
|
||||
tests/qml_focus_check.py un tocco apre solo le regolazioni di quell'oggetto
|
||||
tests/label_fit_check.py le etichette dei tasti ci stanno accanto all'icona
|
||||
tests/cover_action_check.py azione di copertina: figlia diretta del Cover, icona e comando esistono
|
||||
bridge/aira_bridge.py servizio HTTP/1.1 + JSON verso il cloud (gRPC)
|
||||
bridge/setup_creds.py salva le credenziali Aira (file 600)
|
||||
bridge/aira_probe.py sonda di sola lettura: login + stato impianto
|
||||
|
||||
@@ -149,6 +149,7 @@ milliseconds and name the file they are unhappy about:
|
||||
python3 tests/qml_check_bindings.py qml # no property assigned twice in one object
|
||||
python3 tests/qml_focus_check.py # a tap opens only the object it touched
|
||||
python3 tests/label_fit_check.py # quick-action labels fit the button
|
||||
python3 tests/cover_action_check.py # the cover action is declared and wired
|
||||
```
|
||||
|
||||
## Status
|
||||
@@ -250,6 +251,7 @@ tests/ui_contract_check.py QML keys vs bridge keys (bridge must be running)
|
||||
tests/qml_check_bindings.py duplicate property assignments in the QML
|
||||
tests/qml_focus_check.py a tap opens only that object's settings
|
||||
tests/label_fit_check.py quick-action labels fit next to the icon (measured on a phone)
|
||||
tests/cover_action_check.py cover action: direct child of Cover, icon exists, command exists
|
||||
bridge/ local HTTP bridge -> see bridge/README.md
|
||||
translations/ harbour-aira-it.ts (English is the source language)
|
||||
docs/PIANO.md project plan and decisions (Italian)
|
||||
|
||||
+16
-1
@@ -367,6 +367,21 @@ una si allunga il controllo lo segnala) e `docs/icona/tasti-alla-misura-vera.png
|
||||
per l'occhio. Il pulsante Silica non espone il carattere, quindi non si può
|
||||
rimpicciolire il testo: l'unica leva è l'icona.
|
||||
|
||||
**Stato attuale (0.6.8).** 85/85 verifiche del nucleo, 48/48 del bridge,
|
||||
**0.6.9 — l'azione sulla copertina (P8).** Sulla copertina c'è l'icona del
|
||||
boost: si avvia il boost dell'acqua calda senza aprire l'app. Una sola azione,
|
||||
perché la documentazione Silica dice che due si possono avere ma che lo stile
|
||||
delle app di sistema ne mostra una. Il componente esiste: `CoverActionList` e
|
||||
`CoverAction` sono nominati nella guida Silica che sta nell'SDK, e
|
||||
`CoverBackground` eredita da `Cover`, quindi l'elenco dichiarato lì si attacca
|
||||
(non serve avvolgere la copertina in un `Cover`). L'icona si dichiara con
|
||||
`iconSource`, non con `icon.source` del pulsante `Button`: sbagliarlo non dà
|
||||
errore, semplicemente l'icona non compare. La riga in fondo (esterna o «non
|
||||
aggiornato») si ancora a `Cover.coverActionArea`, così l'icona dell'azione non
|
||||
la copre; se l'area non c'è la riga resta dov'era. Nuovo controllo statico
|
||||
`tests/cover_action_check.py`: elenco figlio diretto, una sola azione, icona che
|
||||
esiste, e l'id del comando confrontato con la tabella del bridge — un id
|
||||
sbagliato sarebbe un 400 scoperto solo sul telefono.
|
||||
|
||||
**Stato attuale (0.6.9).** 85/85 verifiche del nucleo, 48/48 del bridge,
|
||||
191/191 stringhe tradotte, `qmllint` pulito, contratto UI↔bridge rispettato.
|
||||
Nessun comando è stato inviato all'impianto: tutto quanto sopra è a sola lettura.
|
||||
|
||||
+9
-8
@@ -24,14 +24,14 @@ Le quattro scelte, applicate:
|
||||
compatta con il pallino colorato.
|
||||
2. "Altri comandi" è diventata **"Comandi impianto"**.
|
||||
3. Le azioni rapide sono **pulsanti visibili** in Home.
|
||||
4. Azioni sulla copertina: **rimandate** a dopo la prova di `CoverAction` sul kit.
|
||||
4. Azioni sulla copertina: **fatte** in 0.6.9 (il boost). Il componente c'è: la guida Silica dell'SDK nomina `CoverActionList` e `CoverAction`.
|
||||
|
||||
Problemi risolti: P1 (Home di consultazione), P2 (feedback ancorato), P3
|
||||
(interruttori), P4 (un solo percorso per pagina), P5 (un blocco per
|
||||
grandezza, valori rapidi), P6 (numeri vecchi spenti + ora), P7 ("esterna"),
|
||||
P9, P10 (diagnostica in Impianto), P11.
|
||||
|
||||
Rimandato: P8 (azioni sulla copertina), in attesa della prova sul telefono.
|
||||
Fatto in 0.6.9: P8 (azione boost sulla copertina), con `tests/cover_action_check.py`.
|
||||
|
||||
Verifiche fatte qui: **85 verifiche del nucleo** (erano 79), `qmllint` pulito su
|
||||
tutti i file QML, 191/191 stringhe tradotte, anteprime rigenerate in inglese e
|
||||
@@ -242,9 +242,10 @@ Resta com'è nelle letture (casa grande, ACS, esterna), con due migliorie basse:
|
||||
|
||||
- Dicitura chiara: "esterna" invece di "est." (P7).
|
||||
- **Azioni sulla copertina**: Silica ha `CoverAction`/`CoverActionList`
|
||||
documentati ("un'azione per una copertina"). Una o due icone — boost e
|
||||
assenza — permettono di agire senza aprire l'app (P8). È rifinitura: da
|
||||
provare sul kit prima di contarla, come ultimo passo.
|
||||
documentati ("un'azione per una copertina"). Fatto in 0.6.9 con **una** icona —
|
||||
il boost — perché la documentazione dice che due si possono avere ma che lo
|
||||
stile delle app di sistema ne mostra una. Si può aggiungere l'assenza quando
|
||||
serve. Attenzione: qui l'icona è `iconSource`, non `icon.source` del `Button`.
|
||||
|
||||
### 4.6 Feedback e stato non aggiornato (P2, P6)
|
||||
|
||||
@@ -336,7 +337,7 @@ davvero: **quanti tocchi per T2 e T3**, prima e dopo.
|
||||
| Rischio | Come si governa |
|
||||
|---|---|
|
||||
| `TextSwitch` documentato ma non istanziabile sul kit 5.1 | Provarlo in una pagina di prova alla prima build; ripiego: si resta sui pulsanti (già funzionanti) |
|
||||
| `CoverAction` idem | È l'ultimo passo; se non c'è, la copertina resta com'è |
|
||||
| `CoverAction` | C'è: nominato nella guida Silica dell'SDK. Usato in 0.6.9, con `tests/cover_action_check.py` |
|
||||
| Le anteprime non rappresentano lo scroll reale | La riga di log con `contentHeight` dà la misura vera |
|
||||
| Un interruttore che anticipa lo stato (bug classico) | `checked` legato ai dati del bridge, non al tocco (§4.4) |
|
||||
| Il cambio lingua ricarica la radice (Qt 5.6) | Nessun impatto: già così |
|
||||
@@ -363,5 +364,5 @@ una build e un controllo tuo sul telefono.
|
||||
2. **"Altri comandi" → "Comandi impianto"**: rinominare?
|
||||
3. **Azioni rapide**: pulsanti visibili sulla Home (proposta) o menù dal basso
|
||||
all'altezza del pollice?
|
||||
4. **Copertina con le azioni**: la facciamo (dopo aver verificato `CoverAction`
|
||||
sul kit) o resta di sola lettura?
|
||||
4. **Copertina con le azioni**: fatto in 0.6.9 — il boost sulla copertina. Se
|
||||
serve, si aggiunge l'assenza (una sola per lo stile di sistema).
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
+17
-1
@@ -149,7 +149,10 @@ CoverBackground {
|
||||
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
// Sopra l'area delle azioni: quando l'azione c'e', Silica disegna li'
|
||||
// l'icona del boost, che non deve coprire questa riga. Se l'area non
|
||||
// c'e' la riga resta attaccata al fondo, come prima.
|
||||
anchors.bottom: cover.coverActionArea ? cover.coverActionArea.top : parent.bottom
|
||||
anchors.bottomMargin: Theme.paddingMedium
|
||||
// Qui c'e' posto per dire che i numeri non sono freschi.
|
||||
text: api.stale
|
||||
@@ -160,4 +163,17 @@ CoverBackground {
|
||||
truncationMode: TruncationMode.Fade
|
||||
width: parent.width - 2 * Theme.paddingMedium
|
||||
}
|
||||
|
||||
// ---- azione sulla copertina (P8) --------------------------------------- //
|
||||
// Agire senza aprire l'app: il boost dell'acqua calda. Una sola, perche' la
|
||||
// documentazione Silica dice che due azioni si possono avere ma che lo stile
|
||||
// delle app di sistema ne mostra una. CoverBackground eredita da Cover,
|
||||
// quindi l'elenco si attacca: e' il posto documentato dove dichiararlo.
|
||||
// Il componente e' in Sailfish.Silica 1.0 (verificato nella guida dell'SDK).
|
||||
CoverActionList {
|
||||
CoverAction {
|
||||
iconSource: "../icons/quick-boost.png"
|
||||
onTriggered: api.sendCommand("hot_water_boost_on")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -1,6 +1,6 @@
|
||||
Name: harbour-aira
|
||||
Summary: Control your Aira heat pump through a local bridge
|
||||
Version: 0.6.8
|
||||
Version: 0.6.9
|
||||
Release: 1
|
||||
Group: Qt/Qt
|
||||
License: MIT
|
||||
@@ -37,6 +37,17 @@ Address and token of the bridge live in the app settings (AppConfigLocation);
|
||||
no personal data is stored in the binary.
|
||||
|
||||
%changelog
|
||||
* Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.6.9-1
|
||||
- copertina: azione rapida (P8 del piano). Sulla copertina compare l'icona del
|
||||
boost: si avvia il boost dell'acqua calda senza aprire l'app. Una sola azione,
|
||||
perche' la documentazione Silica dice che due si possono avere ma che lo stile
|
||||
delle app di sistema ne mostra una. La riga dei dati non freschi si sposta
|
||||
sopra l'area delle azioni (Cover.coverActionArea), cosi' l'icona non la copre.
|
||||
Componente e nomi verificati nella guida dell'SDK (CoverActionList, CoverAction,
|
||||
iconSource, onTriggered: attenzione, e' iconSource, non icon.source del Button)
|
||||
- nuovo controllo statico tests/cover_action_check.py: elenco figlio diretto del
|
||||
Cover, una sola azione, icona che esiste, comando presente nella tabella del
|
||||
bridge (un id sbagliato sarebbe un 400 scoperto solo sul telefono)
|
||||
* Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.6.8-1
|
||||
- Home: le icone dei tasti rimpicciolite. Il pulsante Silica, se non si dice
|
||||
altro, disegna l'icona a Theme.iconSizeMedium (64 px): in un tasto largo un
|
||||
|
||||
@@ -586,8 +586,14 @@ def cover_screen() -> Image.Image:
|
||||
d.text((card[0] + colonna + d.textlength(ora, font=font(30, bold=True)), y + 4),
|
||||
"/" + obiettivo + " °C", font=font(24), fill=SECONDARY)
|
||||
y += 72
|
||||
d.text((card[0] + 34, card[3] - 62),
|
||||
d.text((card[0] + 34, card[3] - 118),
|
||||
T("cover_outdoor") + " " + dec(18.0) + " °C", font=font(22), fill=SECONDARY)
|
||||
# l'azione sulla copertina (P8): il boost, nell'area in basso. Dove la
|
||||
# disegni Silica lo decide lei; qui si mostra che c'e'.
|
||||
icona = Image.open(OUT.parent.parent / "qml" / "icons" / "quick-boost.png").convert("RGBA")
|
||||
lato = 64
|
||||
icona = icona.resize((lato, lato), Image.LANCZOS)
|
||||
img.paste(icona, (int(card[0] + (card[2] - card[0] - lato) / 2), card[3] - 96), icona)
|
||||
return img
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
|
||||
app->setOrganizationName(QStringLiteral("harbour"));
|
||||
app->setApplicationName(QStringLiteral("aira"));
|
||||
app->setApplicationVersion(QStringLiteral("0.6.8"));
|
||||
app->setApplicationVersion(QStringLiteral("0.6.9"));
|
||||
|
||||
Settings settings;
|
||||
ApiClient api(&settings);
|
||||
@@ -36,7 +36,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
|
||||
// Prima riga del log: dice subito se il device sta girando la build nuova
|
||||
// o una stale (trappola nota del build engine).
|
||||
qDebug() << "harbour-aira v0.6.8 build" << __DATE__ << __TIME__;
|
||||
qDebug() << "harbour-aira v0.6.9 build" << __DATE__ << __TIME__;
|
||||
|
||||
view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings);
|
||||
view->rootContext()->setContextProperty(QStringLiteral("api"), &api);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Controllo statico dell'azione sulla copertina (P8 del piano).
|
||||
|
||||
Verifica le regole che la documentazione Silica pone sulle azioni di copertina,
|
||||
e che il componente sia usato come va usato:
|
||||
|
||||
- il `CoverActionList` sta come figlio diretto della copertina (qui la radice
|
||||
e' `CoverBackground`, che eredita da `Cover`: e' li' che va dichiarato).
|
||||
Un elenco annidato dentro un altro elemento non si attaccherebbe;
|
||||
- al massimo una azione: la documentazione dice che due si possono avere, ma
|
||||
che lo stile delle app di sistema ne mostra una;
|
||||
- l'icona si dichiara con `iconSource` (non `icon.source`, che e' del
|
||||
pulsante Button: sbagliarlo qui non da' errore, l'icona semplicemente non
|
||||
compare);
|
||||
- l'icona esiste davvero, risolta rispetto al file QML che definisce la
|
||||
copertina;
|
||||
- il comando che l'azione invia esiste nella tabella dei comandi del bridge
|
||||
(un id sbagliato lo scoprirebbe solo il telefono, con un 400).
|
||||
|
||||
Non serve il kit Silica: legge i file. Uscita 0 = tutto a posto.
|
||||
"""
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
RADICE = pathlib.Path(__file__).resolve().parent.parent
|
||||
COPERTINA = RADICE / "qml" / "cover" / "CoverPage.qml"
|
||||
BRIDGE = RADICE / "bridge" / "aira_bridge.py"
|
||||
|
||||
|
||||
def main():
|
||||
if not COPERTINA.exists():
|
||||
print("ESITO: manca %s" % COPERTINA)
|
||||
return 1
|
||||
testo = COPERTINA.read_text(encoding="utf-8")
|
||||
righe = testo.splitlines()
|
||||
problemi = []
|
||||
|
||||
radice = next((r.strip() for r in righe
|
||||
if re.match(r"^[A-Z][A-Za-z]*\s*\{", r)), "")
|
||||
if not re.match(r"CoverBackground|Cover\s*\{", radice):
|
||||
problemi.append("la radice della copertina e' %r: le azioni vanno dentro "
|
||||
"un Cover (CoverBackground eredita da Cover)" % radice[:40])
|
||||
|
||||
# il CoverActionList deve stare al primo livello dentro la radice
|
||||
elenchi = [(i, r) for i, r in enumerate(righe) if re.match(r"^\s*CoverActionList\s*\{", r)]
|
||||
if not elenchi:
|
||||
print("nessuna azione sulla copertina: niente da controllare (0.6.8 e prima)")
|
||||
return 0
|
||||
for i, r in elenchi:
|
||||
rientro = len(r) - len(r.lstrip())
|
||||
if rientro != 4:
|
||||
problemi.append("riga %d: il CoverActionList e' rientrato di %d spazi, "
|
||||
"deve essere figlio diretto della copertina" % (i + 1, rientro))
|
||||
|
||||
azioni = [r for r in righe if re.match(r"^\s*CoverAction\s*\{", r)]
|
||||
if len(azioni) > 1:
|
||||
problemi.append("%d azioni: la documentazione Silica ne ammette piu' di una "
|
||||
"ma lo stile delle app di sistema ne mostra una" % len(azioni))
|
||||
|
||||
if re.search(r"^\s*icon\.source\s*:", testo, re.M):
|
||||
problemi.append("usa `iconSource`, non `icon.source`: `icon.source` e' del "
|
||||
"pulsante Button e qui non darebbe errore, solo nessuna icona")
|
||||
|
||||
for m in re.finditer(r'iconSource\s*:\s*"([^"]+)"', testo):
|
||||
icona = (COPERTINA.parent / m.group(1)).resolve()
|
||||
if not icona.exists():
|
||||
problemi.append("l'icona %s non esiste (risolta da %s)"
|
||||
% (m.group(1), COPERTINA.name))
|
||||
else:
|
||||
print("icona: %s -> %s" % (m.group(1), icona.relative_to(RADICE)))
|
||||
|
||||
comandi = re.findall(r'sendCommand\("([^"]+)"', testo)
|
||||
if not comandi:
|
||||
problemi.append("l'azione non invia nessun comando: a che serve?")
|
||||
for c in comandi:
|
||||
if not BRIDGE.exists():
|
||||
print("salto il controllo del comando %r: manca %s" % (c, BRIDGE))
|
||||
continue
|
||||
ponte = BRIDGE.read_text(encoding="utf-8")
|
||||
if not re.search(r'"%s"\s*:' % re.escape(c), ponte):
|
||||
problemi.append("il comando %r non e' nella tabella del bridge: "
|
||||
"il telefono riceverebbe un 400" % c)
|
||||
else:
|
||||
print("comando: %s -> presente nel bridge" % c)
|
||||
|
||||
if problemi:
|
||||
for p in problemi:
|
||||
print("PROBLEMA: %s" % p)
|
||||
print("ESITO: %d problemi sull'azione di copertina" % len(problemi))
|
||||
return 1
|
||||
print("ESITO: azione di copertina a posto")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user