Nel precedente articolo su Pushover abbiamo visto come creare il nostro personale sistema di notifiche, mentre in quest’altro articolo come estrapolare dati dal nostro server Home Assistant tramite le API ufficiali.
Sfruttando le conoscenze acquisite, vedremo come creare un widget dinamico per smartwatch tramite le “Glances API” di Pushover.
Partiamo con il precisare che sebbene questo tutorial mostrerà come gestire dati derivanti da Home Assistant, il tutto è totalmente svincolato e sfruttabile in assenza di questo. Attualmente in base alla documentazione ufficiale sembra che l’unico dispositivo a supportare questo widget sia Apple Watch, ma in futuro il supporto dovrebbe estendersi ad altre piattaforme. Per poter sfruttare quanto segue sarà necessario installare l’applicazione Pushover all’interno del nostro smartwatch ed aggiungerlo come complicazione.
Preparazione
Dopo aver creato un’apposita applicazione per il nostro widget sul portale di Pushover, chiamandola ad esempio “GLANCE”, presupponendo di utilizzare come sistema operativo una distribuzione Linux Debian, o una basata su di essa come ad esempio Ubuntu server, procediamo installando i pacchetti necessari ad eseguire i nostri script Bash e Perl, ed a creare il percorso dove andremo a posizionare i nostri file:
# sudo apt-get update
# sudo apt-get install build-essential libwww-perl grep gawk fping
# sudo perl -MCPAN -e 'install Bundle::LWP'
# mkdir /etc/push/
Scaricare questo pacchetto contenente i seguenti file:
- /etc/init.d/:
- glance: Eseguibile che aggiornerà il widget all’avvio e spegnimento del server;
- /etc/push/:
- pushover_apitoken_glance.key: Token dell’applicazione “GLANCE”;
- pushover_apiuser.key: User Key del nostro account Pushover;
- pushover_glance.pl: Eseguibile Perl per aggiornare il widget;
- /sbin/:
- cmd_glance: Eseguibile Bash per comporre il widget.
Copiati i file è necessario associargli i giusti permessi ed abilitare gli script di init:
# chmod 755 /etc/push/*.pl
# chmod 644 /etc/push/*.key
# chmod 755 /sbin/cmd_glance
# chmod 755 /etc/init.d/glance
# chmod a+x /etc/init.d/glance
# sudo update-rc.d glance defaults
# sudo update-rc.d glance enable // Per alcune versioni di Ubuntu potrebbe essere necessario
Struttura e configurazione base
Lo script di composizione del widget “cmd_glance” è stato realizzato altamente configurabile.
Per verificare le varie personalizzazioni che eseguiremo editando il file è possibile impostare le chiavi di “Debug” come segue:
# Debug
status_debug_output="on" // Normalmente "off", se "on" output valori
status_debug_values="on" // Normalmente "off", se "on" valori dimostrativi
Un esempio dell’output di debug:
Importanti per l’automazione del widget i seguenti ritardi:
# Delay
delay_poweron="10" // Attesa (s) aggiornamento all'accensione
delay_poweroff="0.5" // Attesa (s) aggiornamento allo spegnimento
delay_delay="20" // Attesa (s) aggiornamento su richiesta
La prima personalizzazione a discrezione dell’utente è la possibilità di modificare l’aspetto del widget con 4 combinazioni possibili:
# Show
status_more_devices="on" // Normalmente "on", se "off" dispositivi ridotti
status_more_details="on" // Normalmente "on", se "off" dettagli ridotti
Di seguito 3 esempi di configurazione possibile:
Per comprendere il significato dei vari valori visualizzati è necessario approfondire ciò che questo widget è predisposto a mostrare.
Di seguito le specifiche della configurazione predefinita:
- 1: Stato UPS ⚡️ 🔋 ❌
- A – C: Stato UPS
- B: Nome + Stato UPS
- 2: Stato Dispositivi/Server 📱 📵 + 🔗 ⛔️ + 🌐 ⛔️
- A – C: Stato dashboard + Stato server VPN + Stato server remoto
- B: Modalità dispositivi ridotti attiva
- 3: Antifurto 🔐 🔓 ❌ + 🔕 🔔 🛑 ❗️❌
- A – C: Stato inserimento antifurto + Stato intrusione antifurto
- B: Modalità dispositivi ridotti attiva
- 4: Dettagli UPS 📈 📉 + 🕑
- A – B: % carica + Minuti durata residua
- C: % carica + Minuti durata residua (in modalità dettagli ridotti attiva)
- 5: Dettagli sistema 👁 💾
- A – B: Stato CCTV + % occupazione disco
- C: modalità dettagli ridotti attiva
Ognuno di questi elementi può derivare da fonti differenti e presentare molteplici condizioni di analisi, ma prima di soffermarci su come questi vengono recuperati, considero opportuno spiegare alcuni tecnicismi.
Lo script esegue di fatto una composizione dinamica dei valori suddividendola in 4 macro elementi principali sotto identificabili, e nel caso il widget venga aggiunto come complicazione ridotta mostrerà uno solo dei valori che è stato definito come la % delle batterie dell’UPS. I campi sfruttabili possono essere identificati nella documentazione ufficiale delle Glances API.
Definizione degli elementi
Prima di eseguire la composizione è necessario definire ogni singola variabile che fornisce lo stato degli elementi. Per i dati relativi all’UPS nel mio personale setup questi vengono forniti da apcupsd, ma possono essere recuperati anche da altri fonti come ad esempio dalle entità di Home Assistant seguendo le indicazioni di questo articolo. Di seguito le variabili relative all’UPS:
# UPS
status_ups_battery="ONBATT" // Valore verifica stato UPS: A batteria
status_ups_online="ONLINE" // Valore verifica stato UPS: Alimentato
status_ups_offline="COMMLOST" // Valore verifica stato UPS: Non disponibile
status_ups_name="$(apcaccess | grep -e UPSNAME)" // Valore rilevato da UPS: Nome
status_ups_status="$(apcaccess | grep -e STATUS)" // Valore rilevato da UPS: Stato
status_ups_charge_percent="$(apcaccess | grep -e BCHARGE)" // Valore rilevato da UPS: Carica (% pura)
status_ups_charge_text="$(apcaccess | grep -e BCHARGE)" // Valore rilevato da UPS: Carica (% testo)
status_ups_timeleft="$(apcaccess | grep -e TIMELEFT)" // Valore rilevato da UPS: Durata residua
Le variabili successive alle sopra elencate, che verranno sfruttate nella reale composizione, permettono un ulteriore livello di elaborazione del valore rilevato.
In questo specifico esempio il valore rilevato presenta la struttura: “STATUS : ONLINE“, ma il valore a noi necessario é: “ONLINE“. Per estrapolarlo è possibile sfruttare il tool gawk come segue:
extract_ups_status="$(echo $status_ups_status | awk -F'.' '{print $1}' | awk -F':' '{print $2}' | awk -F' ' '{print $1}')"
Procediamo con le variabili relative all’antifurto, recuperate da Home Assistant:
# Alarm
alarm_status_entity_id_1="binary_sensor.alarm_arm_status" // Entità 1: Stato inserimento
alarm_status_entity_id_2="sensor.alarm_intrusion_status" // Entità 2: Stato intrusione
alarm_status_value_1="$(cmd_hass read $alarm_status_entity_id_1 extract | jq '.state' | tr -d \")" // Estrazione entità 1
alarm_status_value_2="$(cmd_hass read $alarm_status_entity_id_2 extract | jq '.state' | tr -d \")" // Estrazione entità 2
Infine le variabili relative allo stato dei dispositivi/server.
Queste possono essere recuperate sia da Home Assistant che definendo direttamente i valori come un DNS o un indirizzo IP. Nella configurazione predefinita i primi 3 elementi verranno analizzati tramite il tool fping, di cui è possibile personalizzarne il comando, mentre gli ultimi 2 recuperati da Home Assistant.
Nel caso sì voglia utilizzare fping indicare come entità “ping” oppure indicare “off” per definire un parametro personalizzato.
# Devices
status_device_cmd="fping -c1 -t150" // Tool di analisi
status_device_entity_id_1="ping" // Entità 1: Ping diretto
status_device_entity_id_2="ping" // Entità 2: Ping diretto
status_device_entity_id_3="ping" // Entità 3: Ping diretto
status_device_entity_id_4="sensor.cctv_status" // Entità 4: Stato CCTV
status_device_entity_id_5="off" // Entità 5: Non usato
status_device_1="192.168.0.30" // Indirizzo IP dashboard
status_device_2="myvpn.duckdns.org" // DNS server VPN
status_device_3="myserver.duckdns.org" // DNS server remoto
status_device_4="$(cmd_hass read $status_device_entity_id_4 extract | jq '.state' | tr -d \")" // Estrazione entità 1
status_device_5="$(df -h | grep /dev/sda1 | awk '$5 ~ /\%$/ {print $5 }' | awk '{gsub(/\ |\%/,"")}1')" // Estrazione entità 2
Analisi degli elementi
Ogni singolo elemento definito presenta una specifica analisi degli stati e restituisce un simbolo o un valore (I simboli sono delle semplicissime Emoji).
L’analisi dei dati relativi all’UPS, che generalmente può avere 3 stati principali (a batteria, alimentato o non disponibile), è gestita da 3 parametri configurabili mentre altri sono “integrati” all’interno del codice per rendere più flessibile la personalizzazione delle condizioni.
Nell’esempio sottostante sì può vedere come avviene l’analisi del dispositivo “Dashboard” semplicemente analizzando gli stati “on” ed “off” ricavati dall’entità di Home Assistant.
# Device 1
if [ "$2" = "$status_test" ]; then
pushover_glance_event_2="$default_test_1"
elif [ "$extract_device_1" = "on" ]; then
pushover_glance_event_2="📱"
elif [ "$extract_device_1" = "off" ]; then
pushover_glance_event_2="📵"
else
pushover_glance_event_2="$default_ignote_1"
fi
Un esempio di analisi più complessa è invece lo stato di intrusione dove è possibile definire le varie fasi, come ad esempio “Attesa” o “Ferma”, recuperate dall’entità sensore di Home Assistant. Le fasi possono ad esempio equivalere all’avviso del nostro sistema di notifiche intrusione o allo stato di registrazione di un eventuale NVR.
# Alarm 2
if [ "$2" = "$status_test" ]; then
pushover_glance_event_6="$default_test_1"
elif [ "$alarm_status_value_2" = "Attesa" ]; then
pushover_glance_event_6="🔕"
elif [ "$alarm_status_value_2" = "Prima" ]; then
pushover_glance_event_6="🔔"
elif [ "$alarm_status_value_2" = "Continuazione" ]; then
pushover_glance_event_6="🔔"
elif [ "$alarm_status_value_2" = "Ultima" ]; then
pushover_glance_event_6="🔔"
elif [ "$alarm_status_value_2" = "Ferma" ]; then
pushover_glance_event_6="🛑"
elif [ "$alarm_status_value_2" = "Errore" ]; then
pushover_glance_event_6="❗️"
elif [ "$alarm_status_value_2" = "unavailable" ]; then
pushover_glance_event_6="❌"
else
pushover_glance_event_6="$default_ignote_1"
fi
Una particolarità che contraddistingue i valori numerici, come ad esempio lo stato di carica dell’UPS, è che il valore rilevato può variare dallo 0% al 100% rendendo gli elementi all’interno della griglia non allineati.
Per minimizzare questo difetto entra in gioco un meccanismo che analizza il valore rilevato e ne adatta il contenuto dinamicamente sfruttando la spaziatura consentita e ne è un esempio il codice sottostante:
# Compose
if [ "$2" = "$status_test" ]; then
compose_notification_1=" "
compose_notification_2=""
elif [ "$status_poweroff_todo" = "$status_trigger" ]; then
compose_notification_1=" "
compose_notification_2=""
else
if [ "$extract_ups_status" = "$status_ups_offline" ]; then
compose_notification_1=" "
elif [ "$extract_notification_1" -ge "100" ]; then
compose_notification_1=" "
elif [ "$extract_notification_1" -le "9" ]; then
compose_notification_1=" "
else
compose_notification_1=" "
fi
Come sì può notare al termine di ogni analisi, oltre i relativi stati di errore recuperabili dagli elementi, è presente uno stato di “ignoto” che scatta nel caso in cui nessuna condizione venga soddisfatta. Nella prima immagine vi è un esempio di come apparirebbe il widget nel caso in cui fosse impossibile recuperare alcuni valori dal server Home Assistant.
Poiché secondo le Glances API un aggiornamento troppo frequente può essere dannoso, questo avverrà periodicamente e di conseguenza non essendo “Live” è necessario ipotizzare diversi scenari, come il caso in cui il nostro server di gestione venga spento temporaneamente trovandoci nella situazione di avere un widget statico senza il reale stato degli elementi.
Per ovviare a questa problematica, sfruttando lo script di init abilitato all’inizio, quando il server verrà spento avremo una schermata di attesa visibile nella seconda immagine.
Nel caso invece sì volesse testare rapidamente l’effettivo funzionamento è possibile eseguire il comando “cmd_glance send test” per visualizzare una serie di simboli di verifica come visibile nell’ultima immagine.
Periodicità aggiornamento
Terminata tutta la fase di configurazione e di test è giunto il momento di attivare l’aggiornamento periodico del widget.
Sfruttando crontab andremo ad impostare la periodicità di esecuzione, eseguire quindi:
# crontab -e
Aggiungere le seguenti regole:
# Glance
*/10 * * * * sudo /sbin/cmd_glance onbattery
*/20 * * * * sudo /sbin/cmd_glance online
La logica dietro questi comandi è molto basilare: ogni 10 minuti viene eseguito il comando con il parametro “onbattery”, che aggiornerà il widget soltanto se l’UPS verrà rilevato nello stato a batteria, questo per monitorare più frequentemente lo stato della batteria e durata residua nel caso di assenza di alimentazione elettrica, ma che nelle normali condizioni verrà ignorato non sovrapponendosi col comando “online” eseguito ogni 20 minuti come consigliato dalle Glances API.
Nel caso in cui non sì volesse questa doppia gestione sì può gestire il semplice comando “cmd_glance send” ogni 20 minuti. Ridurre questo tempo, oltre a poter generare problemi di aggiornamento, incrementerebbe le notifiche fino a rendere necessario l’acquisto di un maggior numero di notifiche mensili.
Integrazione in Home Assistant
Sfruttando le automazioni di Home Assistant è possibile aggiornare lo stato del widget automaticamente al cambio di uno degli elementi visualizzati. Poniamo ad esempio il caso in cui il nostro indirizzo relativo al server VPN diventi irraggiungibile, sfruttando l’integrazione Ping creiamo un sensore:
binary_sensor:
- platform: ping
name: 'VPN'
scan_interval: 30
host: !secret ping_vpn_host
count: 5
Creare delle automazioni che eseguano un comando verso il nostro server per avviare l’aggiornamento immediato al cambio di stato dell’entità di “Ping”, oppure all’avvio del sistema con un ritardo che consenta il corretto caricamento degli elementi:
automation:
- alias: 'Widget - Aggiornamento Stato Elementi Ping'
initial_state: true
mode: single
max_exceeded: silent
trigger:
- platform: state
entity_id: binary_sensor.vpn
from: 'off'
to: 'on'
- platform: state
entity_id: binary_sensor.vpn
from: 'on'
to: 'off'
action:
- delay: '00:00:02'
- service: script.turn_on
entity_id: script.pushover_widget_refresh
- alias: 'Widget - Aggiornamento Stato Elementi Avvio'
initial_state: true
mode: single
max_exceeded: silent
trigger:
- platform: homeassistant
event: start
action:
- delay: '00:01:00'
- service: script.turn_on
entity_id: script.pushover_widget_refresh
lo script “script.pushover_widget_refresh” può essere creato per eseguire delle command_line SSH, oppure come vedremo in uno dei prossimi articoli sfruttando un client MQTT sul server ospitante la gestione del widget.
script:
pushover_widget_refresh:
alias: 'Aggiornamento Widget'
icon: mdi:watch-import-variant
sequence:
- service: mqtt.publish
data:
topic: 'pushover/glance/refresh'
payload: 1
Possibilità aggiuntive
Sulla base di quanto definito sopra nulla vieta di costruirsi la propria variante degli script per adempiere a scopi differenti.
Una variante che ho realizzato è stata la verifica attiva dello stato della mia VPN e tempo trascorso dall’ultima comunicazione tra il mio server locale ed un server remoto, che nel caso non fosse raggiungibile o il tempo trascorso dall’ultima “comunicazione” risulti essere superiore ad un tempo stabilito (escluso lo spegnimento volontario del server locale), mostri una vista di allerta come la seguente:
Conclusione
Spero con questo tutorial di aver mostrato un esempio valido ed applicabile più o meno a tutte le installazioni di come potersi creare il proprio widget personalizzato, oppure di aver fornito una valida base per creare la propria variante cucita su misura.