Idee e modifiche

Questa pagina include idee, miglioramenti, considerazioni riguardo al progetto201.

Indice dei contenuti:




  • Relative a Autoinstall, sistema operativo e sicurezza informatica

    Se possibile automatizzare il tutto in autoinstall e "sotto-script".
    1. Automatizzare l'installazione e la configurazione di Mosquitto, il broker MQTT.

      Occorre automatizzare questa sezione della pagina "Come iniziare".

      NOTA: l'unico passaggio che occorre automatizzare e' quello della creazione dell'utente con la password, gli altri comandi possono essere utilizzati cosi' come sono.

    2. Impedire a utenti non autorizzati di leggere file contenenti credenziali

      I file con credenziali non sono modificabili ma sono comunque leggibili da utenti non autorizzati...

      File a rischio:

      * ```/var/www/credentials/credentials.ini```: file utilizzato da PHP per accedere al database

      * ```/etc/hostapd/hostapd.conf```: file utilizzato da hostapd per creare l'access point

      * file di configurazione dello script python "mqtt_manager" (contiene password di MySQL e del broker MQTT)

      Inoltre il file ```/etc/dhcpcd.conf``` e' modificabile da un utente non-root...

      Per risolvere il problema eseguire i comandi:

                              
                                  
                                  
      # passo la proprieta' del file a www-data (account di apache e php)
      sudo chown www-data /var/www/credentials/credentials.ini
      # permetto la modifica e la lettura solo al proprietario, gli altri non anno alcun tipo di permessi
      sudo chmod 600 /var/www/credentials/credentials.ini
      # permetto modifica e lettura al proprietario (root) e permetto da lettura agli altri
      sudo chmod 644 /etc/dhcpcd.conf
      # impongo proprieta' e modifica lettura a root
      sudo chown root /etc/hostapd/hostapd.conf
      sudo chmod 600 /etc/hostapd/hostapd.conf
                              
                          
    3. Imporre l'inserimento della password di root all'utente "pi" quando si esegue un comando sudo.

      E' possibile imporre la password all'utente ```pi``` utilizzando i seguenti comandi:

                              
                                  
                                  
      echo 'pi ALL=(ALL) PASSWD: ALL' >> ./pi
      sudo chown root:root ./pi
      sudo mv ./pi /etc/sudoers.d/
                              
                          

      Sarebbe da automatizzare, problema: non e' possibile eseguire i comandi prima di autohostapd perche' altrimenti richiederebbe la password e nello stesso tempo autohostapd riavvia il raspberry...

      Due possibilita':

      * La piu' veloce e' modificare autohostapd per impostare l'esecuzione dei comandi dopo l'ultimo riavvio (lo script riavvia Raspberry Pi 2 volte)

      * La piu' corretta per rispettare il ruolo dei singoli script (uno si occupa solo di Apache e PHP, un altro solo di MySQL, un altro solo di dnsmasq, ...) e' specificare da autoinstall di eseguire i comandi dopo 2 riavvii

      O creare un altro script e farlo eseguire da autoinstall

    4. Aprire le porte del router per permettere la connessione dall'esterno da remoto.

      In alternativa rendere raspberry un server VPN per lo stesso scopo.

      Prima mettere in atto tutte le misure di sicurezza necessarie. (firewall, HTTPS, password protette, ...)
    5. Configurare un DDNS per permettere l'accesso da remoto via nome invece di utilizzare l'IP pubblico (che e' molto probabilmente dinamico)

      Per evitare di inserire un indirizzo IP possibilmente dinamico occorre utilizzare un servizio DDNS: duckdns e' molto valido, e' facilmente automatizzabile e non elimina i domini in caso di mancato aggiornamento.

      Occorre accedere sul sito e aggiungere un dominio. Verra' fornito un token per l'autenticazione.

      Comandi da eseguire:

                              
                                  
                                  
      # dominio ottenuto da duckdns
      duckdns_domain="<domain>"
      # token di autenticazione (ha un ruolo simile a una password, non condividerlo online)
      duckdns_token="<token>"
      # percorso in cui posizionare lo script per mantenere aggiornato duckdns
      duckdns_path="/home/pi/duckdns"
      duckdns_script="${duckdns_path}/duckA.sh"
      duckdns_cronpath="/etc/cron.d/duckdnsa"
      # crea la cartella e lo script per aggiornare la corrispondenza IP pubblico - nome dominio
      mkdir "${duckdns_path}"
      echo "echo url='https://www.duckdns.org/update?domains=${duckdns_domain}&token=${duckdns_token}&ip=' | curl -k -o '${duckdns_path}/duck.log' -K -" > "${duckdns_script}"
      # modifica i permessi per permettere solo a root di leggere e modificare il file
      chmod 700 "${duckdns_script}"
      sudo chown root "${duckdns_script}"
      echo "*/5 * * * * root '${duckdns_script}' >/dev/null 2>&1" | sudo tee "${duckdns_cronpath}" >/dev/null 2>&1
                              
                          
    6. Directory dei file di log

      Lo script autohostapd.sh, essendo eseguito dopo un riavvio, salva parte dei log nella home dell'utente "pi".

      Imporre allo script il percorso del file di log.

      Esempio: salvare i file di log nella cartella dello script oppure creare una cartella nel percorso "/var/log". (Il nome della cartella potrebbe essere ad esempio "autoinstall_logs")
    7. SSL/TLS per HTTPS

      Per cifrare le comunicazioni con il server web e' necessario un certificato. Let's encrypt e' gratuito (piu' informazioni sul sito ufficiale qui).

      Comandi:

                              
                                  
                                  
      # email a cui venire contattati in caso di problemi
      letsencrypt_email="<email>"
      # dominio da registrare (ottenuto da un DDNS o acquistato)
      letsencrypt_domain="<domain>"
      # utilizza certbot (occorre installarlo) per configurare apache,
      # automaticamente (non interattivo),
      # accetta automaticamente i termini di servizio (agree tos),
      # specifica la mail e il dominio da utilizzare
      sudo certbot --apache \
      --non-interactive \
      --agree-tos \
      --email "{$letsencrypt_email}" \
      --domains "{$letsencrypt_domain}"
                              
                          
    8. SSL/TLS per MQTT per cifrare le comunicazioni tra nodi e broker MQTT.

    9. Modificare le regole del Firewall per permettere solo ai servizi DHCP, DNS, MQTT, HTTPS di comunicare con altri dispositivi.

      Attenzione! Se si blocca il protocollo DHCP i dispositivi non riceveranno piu' IP dinamici, se si blocca il DNS non funzionera' piu' la conversione dei nomi in IP, ecc...

      Se non si abilita SSH potrebbe essere impossibile ricollegarsi via SSH al Raspberry.

      Se qualcosa smette di funzionare dopo aver configurato il firewall assicurarsi che le porte dei relativi servizi non siano state bloccate.

      Il metodo piu' semplice di applicare regole sul firewall e' utilizzando ```ufw```.

      Ulteriori informazioni da raspberry.org

      Ulteriori informazioni da wiki.ubuntu.com piu' specifico sui servizi

    10. Unit testing degli script

      Creare script con Bats-core (Bash Automated Testing System) per creare test automatici.

    11. Permette solo agli utenti autenticati di visualizzare i dati del sistema di monitoraggio.

      Per fare questo occorre autenticare l'utente sia per l'API sia per la frontend: niente deve essere accessibile di default.

      Esistono diversi metodi per permettere l'autenticazione degli utenti.

      Esempi:

      * Session based: basati sulle sessioni

      * Token based: basati sui token, come JWT (JSON web token)

      Valutare tutti i vantaggi e i rischi dei vari metodi.

      Esempio: JWT e' un metodo stateless (non viene memorizzato lo stato della autenticazione lato server, avviene la verifica di un segreto) e quindi e' molto comodo per architetture software a microservizi (non e' necessario per ogni servizio e operazione verificare lo stato della sessione contattando un database) ma occorre implementarlo correttamente: e' un metodo a rischio di attacchi XSS e CSRF e occorre attuare delle strategie per renderlo piu' sicuro.

      Un passo aggiuntivo sarebbe quello di permettere di configurare il sistema per funzionare in modalita' diverse.

      Esempi:

      1. permettere di visualizzare i dati e di modificare le impostazioni sempre, anche se l'utente non e' autenticato

      2. Permettere di visualizzare i dati e le impostazioni ma permettere la sola lettura delle impostazioni. Solo l'utente autenticato e autorizzato puo' modificare le impostazioni.

      3. Tutto non accessibile di default: solo l'utente autenticato e autorizzato puo' visualizzare i dati e modificare le impostazioni.

      Un altro extra sarebbe permettere di creare account diversi con permessi diversi.

      Esempio:

      * Permettere ad un account amministratore di modificare le impostazioni, visualizzare i dati e creare altri account

      * Permettere agli altri account solo di visualizzare i dati

    12. Creare un nuovo utente MySQL con meno permessi dell'utente root e utilizzare quello per eseguire operazioni sul database del progetto.
  • Relative alla API

    1. Alcuni script restituiscono status 500 quando la tabella del database non contiene record o se non vengono applicate modifiche ai record.

      Questo non influisce sul funzionamento del sistema, semplicemente il codice di status non e' coerente con il corretto/normale stato della tabella.

      Occorre modificare questi script per restituire lo status code 200 quando sono presenti 0 elementi nel database.

      Esempio: "options/planlabels.php" restituisce errore quando la tabella t_planlabels e' vuota o quando l'utente preme il pulsante "Salva" dalla frontend e uno dei label non e' stato modificato.
      Per trovare gli script guardare sulla tab "Network" degli "Strumenti per sviluppatori" del browser. Verificare se i status code diversi da 200 sono coerenti (esiste veramente un errore/problema) oppure se uno status code e' diverso da 200 ma l'operazione ha avuto successo. In questo ultimo caso: modificare lo script per restituire status code 200 in quel contesto.
    2. Aggiungere uno script per permettere all'utente via Frontend la possibilita' di aggiungere, modificare, togliere delle location dalla tabella t_locations.

      Non permettere all'utente di cancellare la location di default, "sconosciuta"

      Inoltre permettere all'utente di modificare la location dei nodi stessi.

  • Relative alla frontend

    1. Documentare completamente la frontend. (anche i label)
    2. Permettere all'utente di aggiungere, modificare e cancellare le location inviando una HTTP request al relativo script PHP della API.
      La location "sconosciuta" non deve essere modificabile.
    3. Permettere all'utente di modificare la location di un nodo inviando una HTTP request al relativo script PHP della API.
    4. Gestire gli errori provenienti dalla backend API.
    5. Utilizzare i Vue file components ( link documentazione ).
      Occorre implementare webpack o simili per convertire i file .vue in javascript
    6. Permettere all'utente di cambiare il colore e la trasparenza dei label. Possibilmente permettere anche di cambiare il livello dei label (z-index) per indicare i label in primo piano, secondo piano, ...

      Attualmente tutti i label sono leggermente trasparenti ma in alcuni casi, ad esempio per le aree, puo' essere utile non rendere visibile dei label oppure modificare il colore per rispettare l'estetica della planimetria.

      Lato frontend occorre aggiungere un color picker per il colore ( link documentazione di mdbootstrap ) e un input per la trasparenza.

      Lato backend occorre modificare la tabella "t_labels" del database per poter contenere le due nuove proprieta' e nella API occorre aggiungere e gestire le richieste aventi i due nuovi parametri.

    7. Aggiornare la pagina delle informazioni (contiene informazioni del vecchio 100+100).

      Si potrebbe anche rimuovere completamente la pagina e aggiungere una sezione "informazioni" all'interno della pagina delle impostazioni con un link che punta verso queste pagine e i repository Github.

      Prima di fare questo occorrerebbe aggiungere una pagina alternativa alle informazioni per mantenere la struttura della pagina principale (due pulsanti in fondo alla pagina).

      Una pagina che avrebbe senso aggiungere e' un modo alternativo alla planimetria di visualizzare i dati:

      potrebbe visualizzare delle card che visualizzano i nodi o le stanze... Magari dare la possibilita' all'utente di scegliere...

      Inoltre, se si riesce ad aggiungere attuatori al sistema, questa pagina puo' essere utile anche a controllare manualmente gli attuatori.

      Ecco una immagine che rappresenta questa idea:

      Pagina che gestisce i nodi

      Cliccando su "GRAFICI" e' possibile visualizzare i grafici delle rilevazioni mentre "DETTAGLI" potrebbe visualizzare altre informazioni (ad esempio indirizzo IP, MAC, RSSI, ...)

      Questo e' solo un esempio, non ha senso solo accendere e spegnere un condizionatore: occorre poter controllare la temperatura...
    8. Migliorare il codice relativo ai label: non sempre alcune modifiche vengono apportate immediatamente.

      Ad esempio per i label delle rilevazioni e' necessario uscire dalle impostazioni e modificare di nuovo il label per permettere di impostare il nodo se prima del label era stato creato un label di tipo area.

  • Relative a mqtt_manager

    1. Imporre il percorso del file di log nella cartella dello script oppure in una cartella in "/var/log".

      Dopo il primo riavvio (dopo aver configurato l'avvio automatico dello script) lo script verra' eseguito nella home dell'utente "pi" e quindi i log verranno scritti in quella cartella.
    2. Aggiungere un controllo per garantire che tutti i percorsi relativi allo script siano assoluti per essere certi della posizione dei file.
    3. Per ridurre la quantita' di dati inviati tra l'API e la frontend si potrebbe implementare i websocket per inviare le ultime rilevazioni alla frontend. Ad esempio se la frontend richiede all'API delle rilevazioni e l'API risponde con 1000 rilevazioni attualmente la frontend quando richiedera' di nuovo i dati via Ajax per mantenere aggiornati i grafici l'API rispondera' con almeno 1000 rilevazioni (se non ne sono arrivate altre). Implementando i websocket la prima richiesta HTTP verso l'API darebbe sempre 1000 rilevazioni in risposta ma poi se le ultime rilevazioni arrivano tramite websocket queste arrivano una a una.

      Lato frontend occorre utilizzare una libreria per connettersi al server websocket da implementare in mqtt_manager.

      I websocket ( link ad un articolo di html.it )potrebbero anche permettere ai vari client di aggiornare il proprio stato. Ad esempio se un client modifica il colore della interfaccia gli altri client non cambiano immediatamente: e' necessario eseguire il refresh della pagina.

      Se si implementa una forma di autenticazione dell'utente occorre anche verificare l'autenticazione dei websocket e attuare tutte le misure di sicurezza necessarie. ( link articolo a riguardo )
  • Relative agli sketch Arduino

    1. Permettere ai nodi di essere aggiornati/programmati da remoto via aggiornamenti OTA (Over The Air updates).

      Questo puo' risultare utile se i nodi sono in luoghi lontani e/o difficilmente raggiungibili.

      Magari vogliamo sistemare un bug dello sketch o aggiornare una libreria che contiene una vulnerabilita' grave.

      Esistono diversi metodi per poter aggiornare i microcontrollori basati su ESP8266 ( link alla documentazione ) ma il metodo piu' adatto a questo scopo e' il metodo con HTTP server su un altro dispositivo (potrebbe essere Raspberry), non il metodo del Web server sul nodo.

      Occorre impedire ad utenti non autorizzati di programmare i nodi a distanza.

      Per fare questo e' necessario poter autenticare nodo e Raspberry e utilizzare tecniche di crittografia per proteggere la comunicazione dei segreti (HTTPS)

      Esempio di codice che verifica gli aggiornamenti attraverso una chiave pubblica: link.

      Software per firmare gli aggiornamenti: link

    2. Le tabelle t_typeX_options contengono le impostazioni dei nodi. Attualmente e' possibile modificare solo con istruzioni SQL e per attuare le modifiche sui nodi occorre riavviare il nodo.

      Verificare se e' possibile implementare un metodo per riavviare il nodo (magari dopo aver ricevuto un messaggio via MQTT).

      Si potrebbe inoltre permettere all'utente di cambiare da interfaccia grafica le impostazioni.

      Occorre gestire dalla API la modifica dei valori sul database.
    3. Attualmente tutte le rilevazioni vengono inviate immediatamente via MQTT a Raspberry senza alcun log.

      Questo significa che tutte le rilevazioni che non vengono inviate correttamente (magari il nodo cerca di inviare le rilevazioni senza essere connesso al broker MQTT o alla rete Wi-Fi) vengono perse.

      Sulle schede basate su ESP8266 esisterebbe una memoria chiamata SPIFFS che e' un vero file system.

      Esistono articoli e forum in cui sconsigliano di sovrascrivere continuamente la memoria poiche' dopo un certo numero di scritture la memoria diventa inaffidabile... Esistono anche altre risorse che invece dichiarano che con una scrittura al minuto la memoria dura quasi 60 anni (considerando solamente il fattore riscrittura della memoria). ( link a favore di questa tesi )

      Una alternativa alla memoria SPIFFS sarebbe aggiungere alla scheda un lettore di schede SD e utilizzare la scheda SD per memorizzare i log.


Extra: aggiungere attuatori al sistema

Questa e' molto probabilmente l'idea piu' complessa: richiede di modificare mqtt_manager, l'API e la frontend.

In realta' se si desidera implementare pochi attuatori e' possibile modificare lo script mqtt_manager per leggere i dati in arrivo e fare un hard-coding (scrivere codice statico, fissare in variabili i valori) di cosa fare in base alle rilevazioni dei sensori.

Cio' che e' complicato e' permettere dinamicamente e da interfaccia grafica (la frontend) di aggiungere, togliere e modificare compiti che il sistema deve eseguire in base a quali dati arrivano, da quale nodo arrivano, ...

In questa sezione parlo di "compiti" (tasks) e di "azioni" (actions). Un "compito", task, riguarda cosa fare quando il sistema riceve una rilevazione. (esempio: quando la temperatura e' X fai l'azione Y) Una "azione", action, riguarda invece cosa puo' fare un nodo (esempio: accendere o spegnere qualcosa).
  1. Per prima cosa occorre modificare il database per permettere al sistema di memorizzare i compiti da svolgere: occorre creare una nuova tabella.

    Potrebbe chiamarsi "t_tasks" ad esempio...

    Occorre tenere traccia di diversi campi:

    • L'identificativo del nodo che rileva i dati.
    • L'identificativo del nodo con l'attuatore.
    • Un valore di reference.
    • Cosa deve essere confrontato con il valore di reference. Potrebbe essere il nome del campo con il dato proveniente da un sensore.
    • Che operazione logica fare tra il valore di reference e il valore rilevato. (maggiore, minore, maggiore uguale, diverso, ...)
    • Che azione svolgere quando l'operatore logico restituisce vero.

      Es. il valore di temperatura rilevato e' maggiore del reference? se e' vero esegui l'azione X.

      Si potrebbe anche aggiungere un campo su cosa svolgere quando l'operatore logico restituisce falso.

      Es. il valore di temperatura rilevato e' maggiore del reference? se e' falso esegui l'azione Y.

      A quel punto occorre pero' dare la possibilita' all'utente di creare compiti sia con azioni da eseguire in caso positivo sia in caso negativo ma anche di creare compiti che abbiano solo azioni da eseguire con risultato negativo o che abbiano solo azioni da eseguire con risultato positivo.

  2. Per come si puo' fare a gestire quali tipi di azioni permettere all'utente di eseguire... Si potrebbe indicare al nodo di presentarsi specificando le sue azioni.

    Le azioni possono essere salvate in un'altra tabella chiamata ad esempio "t_actions" e ogni azione puo' avere l'id del nodo e un numero o stringa che permette al nodo di capire cosa fare.

    Se memorizziamo l'id del nodo con l'azione non occorre piu' memorizzare l'id del nodo con il compito in t_tasks...

  3. Seguendo cio' che e' stato detto precedentemente quando un nuovo nodo con attuatore viene aggiunto potrebbe essere eseguito qualcosa del genere:

    Schema: nuovo attuatore aggiunto al sistema
    Legenda: azzurro chiaro indica sketch arduino, e' su una scheda a microcontrollore. Blu indica Python, lo script mqtt_manager.

    A questo punto so quali azioni puo' eseguire il nodo con l'attuatore.

    Si potrebbe anche usare degli identificativi al posto di una stringa ma a quel punto e' consigliato aggiungere un descrittore dell'azione.

    Esempio: il nodo ha l'azione "accendi" che ha identificativo 1 per il nodo e l'azione "spegni" ha identificativo 0. Successivamente mandando un "1" indicheremo al nodo di eseguire l'azione "accendi", ...

    Un altro metodo puo' essere semplicemente dire "quando si presenta un nodo di tipo X so che le sue azioni sono Y e Z", senza affidare l'impegno al nodo di indicare quali sono le sue azioni.
  4. Adesso occorre sapere dall'utente cosa fare e quando farlo...

    Idea per permettere all'utente di creare compiti (le ho chiamate azioni nell'interfaccia):

    Aggiungi e modifica azioni
  5. Quando l'utente clicca su aggiungi viene eseguita una POST request all'API, PHP, che si occupa di inserire nella tabella t_tasks il nuovo compito.
  6. Da ora in poi mqtt_manager quando riceve un dato da un sensore deve verificare se sono presenti dei compiti relativi a quel nodo.

    Ecco che logica potrebbe adottare:

    Dato ricevuto, Azione eseguita
    Legenda: azzurro chiaro indica sketch arduino, e' su una scheda a microcontrollore. Blu indica Python, lo script mqtt_manager.

Idee ancora piu' avanzate sarebbero permettere all'utente di concatenare azioni in base a una condizione, avere piu' condizioni per una o piu' azioni, avere come condizione il tempo (esempio: alle ore 15 di ogni giorno accendi X) invece di una rilevazione ...

Per concatenare le azioni si potrebbe aggiungere un campo aggiuntivo, chiamarlo tipo "callback_task" per ogni compito e dire "se callback_task non e' none, esegui quel task". A quel punto si puo' anche eseguire quel task in base alla sua condizione...

Si potrebbero anche creare task solo con condizioni e aggiungere una callback_task...

Esempio di compito con azione che richiama compito con azione:

id reading_node_id data_name operator reference positive_action_id negative_action_id callback_task
1 1 temp greater than 28.50 1 none 2
2 5 hum lower than 25.00 none 4 none

In questo caso se il dato "temp" (temperatura) del nodo "1" e' "greater than" (maggiore di) "28.50" (reference, valore di riferimento) esegui l'azione "1" della tabella t_actions (potrebbe essere "accendi ventilatore"), non fare niente in caso contrario e quando hai finito richiama il compito 2 ("callback_task").

Adesso il compito 2 viene eseguito: se il dato "hum" (umidita') del nodo "5" e' "lower than" (minore di) "25.00" (reference) non fare niente (positive_action_id = none), in caso contrario esegui l'azione "4" della tabella t_actions (potrebbe essere "apro la finestra") e quando hai finito non fare niente (callback_task = none)


Se si volesse invece avere due azioni per una condizione si potrebbe idealizzare qualcosa del genere:

id reading_node_id data_name operator reference positive_action_id negative_action_id callback_task
1 1 temp greater than 28.50 1 none 2
2 none none always true none 6 none none

Se la temperatura del nodo 1 e' maggiore di 28.50 (reference), esegui l'azione 1 e richiama il compito 2.

Il compito 2 viene eseguito sempre (operator = always true, sempre vero), esegue l'azione 6.


Se si volesse invece avere due condizioni per una azione invece...:

id reading_node_id data_name operator reference positive_action_id negative_action_id callback_task
1 1 temp greater than 28.50 none none 2
2 3 hum greater than 26.70 6 none none

Se la temperatura del nodo 1 e' maggiore di 28.50 richiama il task 2.

Il task 2 controlla se l'umidita' del nodo 3 e' maggiore di 26.70 e eventualmente esegue l'azione 6.

A questo punto occorre decidere pero' la logica delle callback... Devono essere sempre eseguite alla fine di un compito? devono essere eseguite solo quando la condizione e' vera? solo quando e' falsa? ...

Volendo si puo' aggiungere un nuovo campo alla tabella... Qualcosa tipo "callback_condition".

Se callback_condition e' "always" il compito successivo viene richiamato sempre.

Se callback_condition e' "on true" il compito successivo viene richiamato solo quando la condizione restituita dall'operatore e' vera.

Se callback_condition e' "on false" il compito successivo viene richiamato solo quando la condizione restituita dall'operatore e' falsa.

Sappiamo gia' di non eseguire il compito successivo quando callback_task e' none.


Per i compiti a tempo si potrebbe creare una nuova tabella che richiama le action di t_action. La tabella potrebbe chiamarsi "t_timed_tasks".


Dopo aver automatizzato gli attuatori si potrebbe dare la possibilita' all'utente di controllarli manualmente.

Nella frontend un pulsante puo' permettere di scegliere il controllo manuale o automatico e poi, a seconda del tipo di attuatore (un pulsante va bene solo se e' un attuatore "on/off", ...), un altro elemento della pagina puo' controllare/visualizzare (a seconda del tipo di controllo abilitato) lo stato dell'attuatore.

Per la visualizzazione dello stato occorre memorizzare lo stato sul database oppure e' necessario ottenere lo stato dal nodo con attuatore ogni volta che il suo stato cambia.
Per avere un esempio visivo andare nella sezione frontend e guardare l'immagine con la pagina "Gestisci nodi".