-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathideas.html
964 lines (920 loc) · 47.5 KB
/
ideas.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
---
permalink: /ideas.html
layout: detailpage
containsCopylinks: "true"
containsCode: "true"
containsImage: "true"
description: "Idee e modifiche per il progetto201"
---
<!-- CONTAINER CON IL RESTO DELLA PAGINA -->
<div class="container grow">
<h3>Idee e modifiche</h3>
<p>
Questa pagina include idee, miglioramenti, considerazioni riguardo
al progetto201.
</p>
<!-- INDICE DEI CONTENUTI -->
<h5>Indice dei contenuti:</h5>
<br>
{% include html/parsedtoc.html %}
<br><hr><br>
<ul class="collection">
<li class="collection-item">
<h4 id="autoinstall">
Relative a Autoinstall, sistema operativo e sicurezza informatica
</h4>
<blockquote>
Se possibile automatizzare il tutto in autoinstall e "sotto-script".
</blockquote>
<ol class="collection">
<li class="collection-item">
Automatizzare l'installazione e la configurazione di Mosquitto, il broker MQTT.
<blockquote>
<p>
Occorre automatizzare <a href="/start.html#mosquitto">questa sezione</a> della pagina "Come iniziare".
</p>
<p>
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.
</p>
</blockquote>
</li>
<li class="collection-item">
<p>
Impedire a utenti non autorizzati di leggere file contenenti credenziali
</p>
<p>
I file con credenziali non sono modificabili ma sono comunque leggibili da utenti non autorizzati...
</p>
<p>
File a rischio:
</p>
<p>
* ```/var/www/credentials/credentials.ini```: file utilizzato da PHP per accedere al database
</p>
<p>
* ```/etc/hostapd/hostapd.conf```: file utilizzato da hostapd per creare l'access point
</p>
<p>
* file di configurazione dello script python "mqtt_manager" (contiene password di MySQL e del broker MQTT)
</p>
<p>
Inoltre il file ```/etc/dhcpcd.conf``` e' modificabile da un utente non-root...
</p>
<p>
Per risolvere il problema eseguire i comandi:
</p>
<pre class="language-bash">
<code class="language-bash">
{% capture code %}
# 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
{% endcapture %}
{% include html/code.html html=code %}
</code>
</pre>
</li>
<li class="collection-item">
<p>
Imporre l'inserimento della password di root all'utente "pi" quando si esegue un comando sudo.
</p>
<p>
E' possibile imporre la password all'utente ```pi```
utilizzando i seguenti comandi:
</p>
<pre class="language-bash">
<code class="language-bash">
{% capture code %}
echo 'pi ALL=(ALL) PASSWD: ALL' >> ./pi
sudo chown root:root ./pi
sudo mv ./pi /etc/sudoers.d/
{% endcapture %}
{% include html/code.html html=code %}
</code>
</pre>
<p>
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...
</p>
<p>
Due possibilita':
</p>
<p>
* La piu' veloce e' modificare autohostapd per impostare l'esecuzione dei comandi dopo l'ultimo riavvio (lo script riavvia Raspberry Pi 2 volte)
</p>
<p>
* 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
<blockquote>
O creare un altro script e farlo eseguire da autoinstall
</blockquote>
</p>
</li>
<li class="collection-item">
<p>
Aprire le porte del router per permettere la connessione dall'esterno da remoto.
</p>
<p>
In alternativa rendere raspberry un server VPN per lo stesso scopo.
</p>
<blockquote>
Prima mettere in atto tutte le misure di sicurezza necessarie. (firewall, HTTPS, password protette, ...)
</blockquote>
</li>
<li class="collection-item">
<p>
Configurare un DDNS per permettere l'accesso da remoto via nome invece di utilizzare l'IP pubblico (che e' molto probabilmente dinamico)
</p>
<p>
Per evitare di inserire un indirizzo IP possibilmente dinamico occorre utilizzare un servizio DDNS: <a href="https://www.duckdns.org/">duckdns</a> e' molto valido,
e' facilmente automatizzabile e non elimina i domini in caso di mancato aggiornamento.
</p>
<p>
Occorre accedere sul sito e aggiungere un dominio. Verra' fornito un token per l'autenticazione.
</p>
<p>
Comandi da eseguire:
</p>
<pre class="language-bash">
<code class="language-bash">
{% capture code %}
# 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
{% endcapture %}
{% include html/code.html html=code %}
</code>
</pre>
</li>
<li class="collection-item">
<p>Directory dei file di log</p>
<p>
Lo script autohostapd.sh, essendo eseguito dopo un riavvio, salva parte dei log nella home dell'utente "pi".
</p>
<p>
Imporre allo script il percorso del file di log.
</p>
<blockquote>
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")
</blockquote>
</li>
<li class="collection-item">
<p>
SSL/TLS per HTTPS
</p>
<p>
Per cifrare le comunicazioni con il server web e' necessario un certificato. Let's encrypt e' gratuito (piu' informazioni <a href="https://letsencrypt.org/">sul sito ufficiale qui</a>).
</p>
<p>
Comandi:
</p>
<pre class="language-bash">
<code class="language-bash">
{% capture code %}
# 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}"
{% endcapture %}
{% include html/code.html html=code %}
</code>
</pre>
</li>
<li class="collection-item">
<p>
SSL/TLS per MQTT per cifrare le comunicazioni tra nodi e broker MQTT.
</p>
</li>
<li class="collection-item">
<p>
Modificare le regole del Firewall per permettere solo ai servizi DHCP, DNS, MQTT, HTTPS
di comunicare con altri dispositivi.
</p>
<blockquote>
<p>
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...
</p>
<p>
Se non si abilita SSH potrebbe essere impossibile ricollegarsi via SSH al Raspberry.
</p>
<p>
Se qualcosa smette di funzionare dopo aver configurato il firewall assicurarsi che le porte
dei relativi servizi non siano state bloccate.
</p>
</blockquote>
<p>
Il metodo piu' semplice di applicare regole sul firewall e' utilizzando ```ufw```.
</p>
<p>
<a href="https://www.raspberrypi.org/documentation/configuration/security.md">Ulteriori informazioni da raspberry.org</a>
</p>
<p>
<a href="https://wiki.ubuntu.com/BasicSecurity/Firewall">Ulteriori informazioni da wiki.ubuntu.com</a> piu' specifico sui servizi
</p>
</li>
<li class="collection-item">
<p>
Unit testing degli script
</p>
<p>
Creare script con <a href="https://github.com/bats-core/bats-core">Bats-core</a> (Bash Automated Testing System) per creare test automatici.
</p>
</li>
<li class="collection-item">
<p>
Permette solo agli utenti autenticati di visualizzare i dati del sistema di monitoraggio.
</p>
<p>
Per fare questo occorre autenticare l'utente sia per l'API sia per la frontend: niente deve essere accessibile di default.
</p>
<p>
Esistono diversi metodi per permettere l'autenticazione degli utenti.
</p>
<p>
Esempi:
</p>
<p>
* Session based: basati sulle sessioni
</p>
<p>
* Token based: basati sui token, come JWT (JSON web token)
</p>
<blockquote>
<p>
Valutare tutti i vantaggi e i rischi dei vari metodi.
</p>
<p>
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.
</p>
</blockquote>
<blockquote>
<p>
Un passo aggiuntivo sarebbe quello di permettere di configurare il sistema per funzionare in modalita' diverse.
</p>
<p>
Esempi:
</p>
<p>
1. permettere di visualizzare i dati e di modificare le impostazioni sempre, anche se l'utente non e' autenticato
</p>
<p>
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.
</p>
<p>
3. Tutto non accessibile di default: solo l'utente autenticato e autorizzato puo' visualizzare i dati e modificare le impostazioni.
</p>
</blockquote>
<blockquote>
<p>
Un altro extra sarebbe permettere di creare account diversi con permessi diversi.
</p>
<p>
Esempio:
</p>
<p>
* Permettere ad un account amministratore di modificare le impostazioni, visualizzare i dati
e creare altri account
</p>
<p>
* Permettere agli altri account solo di visualizzare i dati
</p>
</blockquote>
</li>
<li class="collection-item">
Creare un nuovo utente MySQL con meno permessi dell'utente root
e utilizzare quello per eseguire operazioni sul database del progetto.
</li>
</ol>
</li>
<li class="collection-item">
<h4 id="api">Relative alla API</h4>
<ol class="collection">
<li class="collection-item">
<p>
Alcuni script restituiscono status 500 quando la tabella del database non contiene record
o se non vengono applicate modifiche ai record.
</p>
<p>
Questo non influisce sul funzionamento del sistema, semplicemente il codice di status non e' coerente
con il corretto/normale stato della tabella.
</p>
<p>
Occorre modificare questi script per restituire lo status code 200 quando sono presenti 0 elementi nel database.
</p>
<blockquote>
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.
</blockquote>
<blockquote>
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.
</blockquote>
</li>
<li class="collection-item">
<p>
Aggiungere uno script per permettere all'utente via Frontend la possibilita' di aggiungere,
modificare, togliere delle location dalla tabella t_locations.
<blockquote>
Non permettere all'utente di cancellare la location di default, "sconosciuta"
</blockquote>
</p>
<p>
Inoltre permettere all'utente di modificare la location dei nodi stessi.
</p>
</li>
</ol>
</li>
<li class="collection-item">
<h4 id="frontend">
Relative alla frontend
</h4>
<ol class="collection">
<li class="collection-item">
Documentare completamente la frontend. (anche i label)
</li>
<li class="collection-item">
Permettere all'utente di aggiungere, modificare e cancellare le location
inviando una HTTP request al relativo script PHP della API.
<blockquote>
La location "sconosciuta" non deve essere modificabile.
</blockquote>
</li>
<li class="collection-item">
Permettere all'utente di modificare la location di un nodo inviando una HTTP
request al relativo script PHP della API.
</li>
<li class="collection-item">
Gestire gli errori provenienti dalla backend API.
</li>
<li class="collection-item">
Utilizzare i Vue file components ( <a href="https://vuejs.org/v2/guide/single-file-components.html">link documentazione</a> ).
<blockquote>
Occorre implementare webpack o simili per convertire i file .vue in javascript
</blockquote>
</li>
<li class="collection-item">
<p>
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, ...
</p>
<p>
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.
</p>
<blockquote>
<p>
Lato frontend occorre aggiungere un color picker per il colore ( <a href="https://mdbootstrap.com/plugins/jquery/color-picker/">link documentazione di mdbootstrap</a> )
e un input per la trasparenza.
</p>
<p>
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.
</p>
</blockquote>
</li>
<li class="collection-item">
<p>
Aggiornare la pagina delle informazioni (contiene informazioni del vecchio 100+100).
</p>
<blockquote>
<p>
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.
</p>
<p>
Prima di fare questo occorrerebbe aggiungere una pagina alternativa alle informazioni
per mantenere la struttura della pagina principale (due pulsanti in fondo alla pagina).
</p>
<p>
Una pagina che avrebbe senso aggiungere e' un modo alternativo alla planimetria
di visualizzare i dati:
</p>
<p>
potrebbe visualizzare delle card che visualizzano i nodi o le stanze... Magari dare la possibilita' all'utente di scegliere...
</p>
<p>
Inoltre, se si riesce ad aggiungere attuatori al sistema, questa pagina puo' essere utile anche
a controllare manualmente gli attuatori.
</p>
<p>
Ecco una immagine che rappresenta questa idea:
</p>
<div class="center-align container">
<img class="materialboxed mx-auto resp-img" src="/assets/svg/ideas/managenodes.svg" alt="Pagina che gestisce i nodi">
</div>
<p>
Cliccando su "GRAFICI" e' possibile visualizzare i grafici delle rilevazioni mentre "DETTAGLI" potrebbe visualizzare altre informazioni
(ad esempio indirizzo IP, MAC, RSSI, ...)
</p>
<blockquote>
Questo e' solo un esempio, non ha senso solo accendere e spegnere un condizionatore: occorre poter controllare la temperatura...
</blockquote>
</blockquote>
</li>
<li class="collection-item">
<p>
Migliorare il codice relativo ai label: non sempre alcune modifiche vengono apportate immediatamente.
</p>
<p>
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.
</p>
</li>
</ol>
</li>
<li class="collection-item">
<h4 id="mqtt_manager">
Relative a mqtt_manager
</h4>
<ol class="collection">
<li class="collection-item">
<p>
Imporre il percorso del file di log nella cartella dello script oppure in una cartella in "/var/log".
</p>
<blockquote>
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.
</blockquote>
</li>
<li class="collection-item">
Aggiungere un controllo per garantire che tutti i percorsi relativi allo script siano assoluti
per essere certi della posizione dei file.
</li>
<li class="collection-item">
<p>
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.
</p>
<p>
Lato frontend occorre utilizzare una libreria per connettersi al server websocket da implementare in mqtt_manager.
</p>
<p>
I websocket ( <a href="https://www.html.it/articoli/introduzione-ai-websocket/">link ad un articolo di html.it</a> )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.
</p>
<blockquote>
Se si implementa una forma di autenticazione dell'utente occorre anche verificare l'autenticazione dei websocket
e attuare tutte le misure di sicurezza necessarie. ( <a href="https://devcenter.heroku.com/articles/websocket-security">link articolo a riguardo</a> )
</blockquote>
</li>
</ol>
</li>
<li class="collection-item">
<h4 id="sketch-arduino">
Relative agli sketch Arduino
</h4>
<ol class="collection">
<li class="collection-item">
<p>
Permettere ai nodi di essere aggiornati/programmati da remoto via aggiornamenti OTA (Over The Air updates).
</p>
<p>
Questo puo' risultare utile se i nodi sono in luoghi lontani e/o difficilmente raggiungibili.
</p>
<p>
Magari vogliamo sistemare un bug dello sketch o aggiornare una libreria che contiene una vulnerabilita' grave.
</p>
<p>
Esistono diversi metodi per poter aggiornare i microcontrollori basati su ESP8266 ( <a href="https://arduino-esp8266.readthedocs.io/en/latest/ota_updates/readme.html">link alla documentazione</a> )
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.
</p>
<blockquote>
<p>
Occorre impedire ad utenti non autorizzati di programmare i nodi a distanza.
</p>
<p>
Per fare questo e' necessario poter autenticare nodo e Raspberry e
utilizzare tecniche di crittografia per proteggere la comunicazione dei segreti (HTTPS)
</p>
<p>
Esempio di codice che verifica gli aggiornamenti attraverso una chiave pubblica: <a href="https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266httpUpdate/examples/httpUpdateSigned/httpUpdateSigned.ino">link</a>.
</p>
<p>
Software per firmare gli aggiornamenti: <a href="https://github.com/esp8266/Arduino/blob/master/tools/signing.py">link</a>
</p>
</blockquote>
</li>
<li class="collection-item">
<p>
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.
</p>
<p>
Verificare se e' possibile implementare un metodo per riavviare il nodo (magari dopo aver ricevuto un messaggio via MQTT).
</p>
<p>
Si potrebbe inoltre permettere all'utente di cambiare da interfaccia grafica le impostazioni.
</p>
<blockquote>
Occorre gestire dalla API la modifica dei valori sul database.
</blockquote>
</li>
<li class="collection-item">
<p>
Attualmente tutte le rilevazioni vengono inviate immediatamente via MQTT a Raspberry senza alcun log.
</p>
<p>
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.
</p>
<p>
Sulle schede basate su ESP8266 esisterebbe una memoria chiamata SPIFFS che e' un vero file system.
</p>
<p>
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).
( <a href="https://github.com/pellepl/spiffs/wiki/FAQ#how-long-will-my-spi-flash-live">link a favore di questa tesi</a> )
</p>
<p>
Una alternativa alla memoria SPIFFS sarebbe aggiungere alla scheda un lettore di schede SD e utilizzare la scheda SD per memorizzare i log.
</p>
</li>
</ol>
</li>
</ul>
<hr>
<h4 id="actuators">Extra: aggiungere attuatori al sistema</h4>
<p>
Questa e' molto probabilmente l'idea piu' complessa: richiede di modificare mqtt_manager, l'API e la frontend.
</p>
<p>
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.
</p>
<p>
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, ...
</p>
<blockquote>
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).
</blockquote>
<ol class="collection">
<li class="collection-item">
<p>
Per prima cosa occorre modificare il database per permettere al sistema di memorizzare i compiti da svolgere:
occorre creare una nuova tabella.
</p>
<p>
Potrebbe chiamarsi "t_tasks" ad esempio...
</p>
<p>
Occorre tenere traccia di diversi campi:
</p>
<ul class="collection">
<li class="collection-item">
L'identificativo del nodo che rileva i dati.
</li>
<li class="collection-item">
L'identificativo del nodo con l'attuatore.
</li>
<li class="collection-item">
Un valore di reference.
</li>
<li class="collection-item">
Cosa deve essere confrontato con il valore di reference.
Potrebbe essere il nome del campo con il dato proveniente da un sensore.
</li>
<li class="collection-item">
Che operazione logica fare tra il valore di reference e il valore rilevato.
(maggiore, minore, maggiore uguale, diverso, ...)
</li>
<li class="collection-item">
<p>
Che azione svolgere quando l'operatore logico restituisce vero.
</p>
<p>
Es. il valore di temperatura rilevato e' maggiore del reference?
se e' vero esegui l'azione X.
</p>
<blockquote>
<p>
Si potrebbe anche aggiungere un campo su cosa svolgere quando l'operatore logico restituisce falso.
</p>
<p>
Es. il valore di temperatura rilevato e' maggiore del reference?
se e' falso esegui l'azione Y.
</p>
<p>
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.
</p>
</blockquote>
</li>
</ul>
</li>
<li class="collection-item">
<p>
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.
</p>
<p>
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.
</p>
<p>
Se memorizziamo l'id del nodo con l'azione non occorre piu' memorizzare l'id del nodo con il compito in t_tasks...
</p>
</li>
<li class="collection-item">
<p>
Seguendo cio' che e' stato detto precedentemente quando un nuovo nodo con attuatore viene aggiunto
potrebbe essere eseguito qualcosa del genere:
</p>
<div class="center-align container">
<img class="materialboxed mx-auto resp-img" src="/assets/svg/ideas/newactuator.svg" alt="Schema: nuovo attuatore aggiunto al sistema">
</div>
<blockquote>
Legenda: azzurro chiaro indica sketch arduino, e' su una scheda a microcontrollore.
Blu indica Python, lo script mqtt_manager.
</blockquote>
<p>
A questo punto so quali azioni puo' eseguire il nodo con l'attuatore.
</p>
<blockquote>
Si potrebbe anche usare degli identificativi al posto di una stringa ma a quel punto
e' consigliato aggiungere un descrittore dell'azione.
<p>
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", ...
</p>
</blockquote>
<blockquote>
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.
</blockquote>
</li>
<li class="collection-item">
<p>
Adesso occorre sapere dall'utente cosa fare e quando farlo...
</p>
<p>
Idea per permettere all'utente di creare compiti (le ho chiamate azioni nell'interfaccia):
</p>
<div class="center-align container">
<img class="materialboxed mx-auto resp-img" src="/assets/svg/ideas/actionsfrontend.svg" alt="Aggiungi e modifica azioni">
</div>
</li>
<li class="collection-item">
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.
</li>
<li class="collection-item">
<p>
Da ora in poi mqtt_manager quando riceve un dato da un sensore deve verificare se sono presenti dei compiti relativi a quel nodo.
</p>
<p>
Ecco che logica potrebbe adottare:
</p>
<div class="center-align container">
<img class="materialboxed mx-auto resp-img" src="/assets/svg/ideas/execaction.svg" alt="Dato ricevuto, Azione eseguita">
</div>
<blockquote>
Legenda: azzurro chiaro indica sketch arduino, e' su una scheda a microcontrollore.
Blu indica Python, lo script mqtt_manager.
</blockquote>
</li>
</ol>
<p>
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 ...
</p>
<p>
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...
</p>
<p>
Si potrebbero anche creare task solo con condizioni e aggiungere una callback_task...
</p>
<p>
Esempio di compito con azione che richiama compito con azione:
</p>
<table class="striped responsive-table break-word">
<thead>
<tr>
<th>id</th>
<th>reading_node_id</th>
<th>data_name</th>
<th>operator</th>
<th>reference</th>
<th>positive_action_id</th>
<th>negative_action_id</th>
<th>callback_task</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1</td>
<td>temp</td>
<td>greater than</td>
<td>28.50</td>
<td>1</td>
<td>none</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>5</td>
<td>hum</td>
<td>lower than</td>
<td>25.00</td>
<td>none</td>
<td>4</td>
<td>none</td>
</tr>
</tbody>
</table>
<p>
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").
</p>
<p>
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)
</p>
<hr>
<p>
Se si volesse invece avere due azioni per una condizione si potrebbe idealizzare qualcosa del genere:
</p>
<table class="striped responsive-table break-word">
<thead>
<tr>
<th>id</th>
<th>reading_node_id</th>
<th>data_name</th>
<th>operator</th>
<th>reference</th>
<th>positive_action_id</th>
<th>negative_action_id</th>
<th>callback_task</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1</td>
<td>temp</td>
<td>greater than</td>
<td>28.50</td>
<td>1</td>
<td>none</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>none</td>
<td>none</td>
<td>always true</td>
<td>none</td>
<td>6</td>
<td>none</td>
<td>none</td>
</tr>
</tbody>
</table>
<p>
Se la temperatura del nodo 1 e' maggiore di 28.50 (reference), esegui l'azione 1 e richiama il compito 2.
</p>
<p>
Il compito 2 viene eseguito sempre (operator = always true, sempre vero), esegue l'azione 6.
</p>
<hr>
<p>
Se si volesse invece avere due condizioni per una azione invece...:
</p>
<table class="striped responsive-table break-word">
<thead>
<tr>
<th>id</th>
<th>reading_node_id</th>
<th>data_name</th>
<th>operator</th>
<th>reference</th>
<th>positive_action_id</th>
<th>negative_action_id</th>
<th>callback_task</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1</td>
<td>temp</td>
<td>greater than</td>
<td>28.50</td>
<td>none</td>
<td>none</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>3</td>
<td>hum</td>
<td>greater than</td>
<td>26.70</td>
<td>6</td>
<td>none</td>
<td>none</td>
</tr>
</tbody>
</table>
<p>
Se la temperatura del nodo 1 e' maggiore di 28.50 richiama il task 2.
</p>
<p>
Il task 2 controlla se l'umidita' del nodo 3 e' maggiore di 26.70 e eventualmente esegue l'azione 6.
</p>
<blockquote>
<p>
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? ...
</p>
<p>
Volendo si puo' aggiungere un nuovo campo alla tabella... Qualcosa tipo "callback_condition".
</p>
<p>
Se callback_condition e' "always" il compito successivo viene richiamato sempre.
</p>
<p>
Se callback_condition e' "on true" il compito successivo viene richiamato solo quando la condizione restituita dall'operatore e' vera.
</p>
<p>
Se callback_condition e' "on false" il compito successivo viene richiamato solo quando la condizione restituita dall'operatore e' falsa.
</p>
<p>
Sappiamo gia' di non eseguire il compito successivo quando callback_task e' none.
</p>
</blockquote>
<hr>
<p>
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".
</p>
<hr>
<p>
Dopo aver automatizzato gli attuatori si potrebbe dare la possibilita' all'utente di
controllarli manualmente.
</p>
<p>
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.
</p>
<blockquote>
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.
</blockquote>
<blockquote>
Per avere un esempio visivo andare <a href="#frontend">nella sezione frontend</a>
e guardare l'immagine con la pagina "Gestisci nodi".
</blockquote>
</div>