Repository navigation
Expand file tree
/
Copy pathdmdconf.py
More file actions
1448 lines (1390 loc) · 69.1 KB
/
Copy pathdmdconf.py
File metadata and controls
1448 lines (1390 loc) · 69.1 KB
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
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""Gestione della configurazione persistente del DMD.
La configurazione vive in un unico file JSON. Ogni modifica dalla web UI
viene scritta subito su disco, cosi' sopravvive al riavvio.
"""
import json
import os
import threading
CONFIG_PATH = os.environ.get("DMD_CONFIG", "/etc/dmd/config.json")
DEFAULTS = {
"panel": {
"rows": 64,
"cols": 128,
"chain": 2,
"parallel": 1,
"hardware_mapping": "regular",
# Taratura trovata sul campo con i pannelli FM6373 + DP32020B: sono i
# valori che il pannello vuole davvero, non quelli generici della
# libreria. Chi installa da zero parte da qui invece di ripercorrere
# la campagna di prove.
"slowdown": 5,
"panel_type": "fm6373",
"spwm_row_address_type": 1,
"spwm_scan_rows": 64,
"spwm_data_layout": 0,
"spwm_register_config": 2,
"limit_refresh": 0,
"pwm_bits": 10,
# Durata del bit meno significativo. Accorciarla riduce il tempo di
# un frame intero, quindi alza il refresh senza togliere profondita'.
"pwm_lsb_nanoseconds": 200,
# Bit bassi resi con dithering temporale invece che con tempo di
# accensione: 1 bit di dithering raddoppia il refresh a parita' di
# profondita' dichiarata.
"pwm_dither_bits": 1,
"profile_dir": "/home/gillo/rpi-rgb-led-matrix_pwm_experiment/lib/spwm/registertest/data",
# Blocco di registro RGB scritto a mano, che scavalca quello del
# profilo. Vuoto = si usa il profilo, ed e' quello che vuole il 99%
# dei casi: serve per provare una parola sola alla volta quando
# nessun profilo del catalogo va bene del tutto.
"spwm_force_register": "",
# I parametri dei pannelli **classici**, quelli a indirizzamento
# diretto che girano con la libreria standard di hzeller. Vuoto =
# predefinito della libreria, ed e' cosi' per tutti finche' qualcuno
# non ha un pannello che li vuole: i profili S-PWM non li usano.
"row_address_type": "",
"multiplexing": "",
"scan_mode": "",
"pixel_mapper": "",
"led_rgb_sequence": "",
"disable_hardware_pulsing": "",
"show_refresh": False,
# Cartella del fork della libreria matrice. Vuoto = dedotta da
# profile_dir, che ne e' una sottocartella.
"library_dir": "",
# Profilo hardware. Un nome noto applica in blocco tutti i parametri
# di quel tipo di pannello; "custom" li lascia come sono. Serve a
# tornare indietro dopo una configurazione sbagliata senza dover
# ricordare venti numeri.
"preset": "fm6373_dp32020b",
# Regolazioni fini del driver S-PWM, applicate come variabili d'ambiente.
# Vuoto = valore predefinito della libreria.
"spwm_env": {
"SPWM_END_OF_FRAME_EXTRA_ROW_CYCLES": "1",
"SPWM_FRAME_END_SLEEP_US": "300",
},
},
"display": {
"brightness": 50,
"night_enabled": False,
"night_start": "22:00",
"night_end": "07:00",
"night_brightness": 15,
# E il volume, con la stessa logica della luminosita': di notte il DMD
# abbassa la voce insieme alla luce. Zero = muto, ed e' il predefinito
# perche' un avviso a volume pieno alle tre di notte non lo vuole
# nessuno -- vale solo se il night mode e' acceso, quindi chi non lo
# usa non se ne accorge.
#
# Vale per quello che il DMD dice **di sua iniziativa**: un aereo, un
# compleanno, una notifica. Non per una partita, che e' una cosa che
# stai facendo tu adesso -- la stessa eccezione che Sleep mode fa gia'
# per chi tiene il pannello occupato.
"night_volume": 0.0,
"sleep_enabled": False,
"sleep_start": "01:00",
"sleep_end": "06:00",
"sleep_wake_on_zedmd": True,
# E lo stesso per una partita aperta a mano. Sono due cose diverse:
# i frame da Batocera arrivano da soli, una partita la apre qualcuno
# che e' li' davanti -- e spegnergli il pannello in faccia perche'
# sono le due di notte e' proprio il caso in cui lo Sleep sbaglia.
"sleep_wake_on_giochi": True,
# Spegnimento a mano del pannello. Non e' Sleep mode: quello segue un
# orario, questo e' una decisione presa adesso e che dura finche' non
# si cambia idea -- anche dopo un riavvio, che e' voluto: uno
# spegnimento che si annulla da solo aggiornando il servizio non
# sarebbe uno spegnimento. Il Raspberry resta acceso e continua a fare
# tutto -- il radar registra, le notifiche arrivano, la pagina web
# risponde -- e' solo il vetro a essere nero.
"off": False,
},
"clock": {
"time_color": "#ff8c1a",
"date_color": "#00a0d0",
"format_24h": True,
"show_date": True,
"language": "it",
"blink_colon": True,
# Un colore diverso per le cifre a ogni ora, invece di `time_color`.
# Spento di serie: chi ha scelto un colore se lo aspetta fermo.
"colore_casuale": False,
# Di quanti pixel spostare le cifre in verticale: negativo in alto,
# positivo in basso. Vale sempre, e con il World Time acceso si somma
# all'alzata automatica.
#
# Esiste perche' la posizione buona non e' la stessa per tutti: la
# decide l'altezza a cui sta il pannello e da dove lo si guarda, e
# sono due cose che da qui non si possono sapere.
#
# Due e non zero: il centro geometrico non e' il centro che si vede.
# Un pannello sta su una mensola e lo si guarda **dal basso**, e da
# li' le cifre centrate sembrano alte. Questi due pixel sono misurati
# sul pannello vero, non calcolati -- e chi ha un'altra mensola muove
# il cursore.
"offset_v": 2,
# World Time: che ore sono adesso dall'altra parte del mondo. Fino a
# cinque localita', tre per volta sul pannello, in una banda sotto le
# cifre -- che per farle stare salgono di tre pixel.
#
# Si salva il **fuso IANA** e non un'ora di scarto: «New York = UTC-5»
# e' sbagliato per meta' dell'anno, e sbagliato in silenzio. Le regole
# dell'ora legale stanno gia' nel database dei fusi del sistema.
"world": {
"enabled": False,
# Le cinque caselle, riempite al primo caricamento da
# sources.clock.normalizza_mondo.
"voci": [],
# Vuoti = il nome prende il colore della data e l'ora il grigio
# chiaro. Sono due colori perche' sono due informazioni, e con tre
# citta' affiancate uno solo si legge come una frase.
"colore_nome": "",
"colore_ora": "#c8c8c8",
},
},
"mediaplayer": {
"media_dir": "/srv/dmd/media",
"min_interval": 20,
"max_interval": 30,
"image_duration": 5,
"video_duration": 8,
"video_fps": 20,
# Quanta parte dei contenuti deve essere animata, in percentuale.
# Senza questa manopola si pescava a caso da tutta la libreria, e una
# cartella fatta per nove decimi di PNG mostrava PNG nove volte su
# dieci: le animazioni, che sono quelle che si guardano, comparivano
# una volta ogni tanto. 60 vuol dire sei contenuti animati su dieci,
# qualunque cosa ci sia nella cartella.
"quota_animazioni": 60,
"scale_mode": "fit",
"pixel_art": True,
# Fascia oraria del Media Player, con la stessa forma di Night mode.
# Il flag spento — cioe' il predefinito — vuol dire "lavora sempre":
# chi aggiorna non si accorge di niente.
"timer_enabled": False,
"timer_start": "08:00",
"timer_end": "23:00",
},
"banner": {
"min_interval": 30,
"max_interval": 60,
"fps": 30,
"shuffle": False,
# Dieci caselle, riempite al primo caricamento da sources.banner.
"items": [],
},
"birthdays": {
# Con quanto anticipo comincia il promemoria, e ogni quanto ricompare.
"lead_hours": 48,
"interval_minutes": 20,
"seconds": 12,
"speed": 40,
"color": "#ff40a0",
"size": "medium",
"blink": False,
# Mostra anche l'eta' compiuta, quando l'anno di nascita c'e'.
"show_age": True,
},
"rifiuti": {
# Il promemoria compare alle 18 della sera prima e sparisce alle 8 del
# giorno di raccolta: si espone il bidone la sera, e dopo il passaggio
# ricordarlo ancora sarebbe rumore. Per le attivita' comunali la fine
# e' quella del divieto, che ogni comune fissa a modo suo.
"ora_avviso": 18,
"ora_fine": 8,
# Le frazioni che quasi ogni comune ha. Nascono senza giorni: finche'
# non se ne spunta almeno uno la voce non compare da nessuna parte, e
# un pannello che ricorda raccolte inventate sarebbe peggio di niente.
"voci": [
{"nome": "Carta", "colore": "#ffffff", "tipo": "rifiuto",
"giorni": [], "cadenza": "settimanale", "riferimento": "",
"attiva": True, "ora_inizio": 0, "ora_fine": 6},
{"nome": "Plastica", "colore": "#2060ff", "tipo": "rifiuto",
"giorni": [], "cadenza": "settimanale", "riferimento": "",
"attiva": True, "ora_inizio": 0, "ora_fine": 6},
{"nome": "Vetro", "colore": "#ff8c1a", "tipo": "rifiuto",
"giorni": [], "cadenza": "settimanale", "riferimento": "",
"attiva": True, "ora_inizio": 0, "ora_fine": 6},
{"nome": "Umido", "colore": "#20c040", "tipo": "rifiuto",
"giorni": [], "cadenza": "settimanale", "riferimento": "",
"attiva": True, "ora_inizio": 0, "ora_fine": 6},
{"nome": "Secco", "colore": "#c07830", "tipo": "rifiuto",
"giorni": [], "cadenza": "settimanale", "riferimento": "",
"attiva": True, "ora_inizio": 0, "ora_fine": 6},
{"nome": "Sosta", "colore": "#ff2020", "tipo": "attivita",
"giorni": [], "cadenza": "settimanale", "riferimento": "",
"attiva": True, "ora_inizio": 0, "ora_fine": 6},
],
},
"scadenze": {
# Soglie del semaforo, in giorni. Sopra la verde il semaforo resta
# spento: una scadenza fra un mese non e' una notizia, e un pannello
# che segnala sempre qualcosa non segnala piu' niente.
"soglia_verde": 10,
"soglia_giallo": 7,
"soglia_rosso": 3,
# Mostra le tre lampade anche quando sono tutte spente. Spento: il
# pannello resta pulito quando non c'e' niente in scadenza.
"semaforo_sempre": False,
# Segnaposto della migrazione 4.1: dice che l'interruttore del
# servizio e' stato deciso una volta, cosi' chi lo spegne lo trova
# ancora spento al prossimo aggiornamento.
"servizio_scelto": False,
# L'avviso periodico sul pannello, con la stessa forma di quello del
# radar: ogni quanto compare, e quanto resta.
"interval_minutes": 20,
"seconds": 10,
"speed": 40,
},
# I giochi scritti per il pannello. Non sono un servizio e non compaiono
# fra gli interruttori: sono una partita che comincia e finisce, come Doom
# dalla 3.2. Qui stanno solo i comandi e i record.
"giochi": {
"keyboard": True,
"keyboard_device": "",
# Come per Doom: un tasto del cabinato non fa cominciare una partita a
# meno che non lo si chieda, Options sul pad si'.
"keyboard_starts": False,
"joystick": True,
"joystick_device": "",
"joystick_starts": True,
# Dopo quanti secondi senza comandi la partita si chiude da sola e il
# pannello torna alle sorgenti. Zero per non chiuderla mai.
"session_timeout": 180,
"ultimo": "breakout",
"record": {},
# I tre migliori per gioco, con le tre lettere di chi li ha fatti:
# {"breakout": [{"punti": 1200, "nome": "GIL", "livello": 4, ...}]}.
# Il `record` qui sopra resta e si aggiorna da solo: lo leggono la
# pagina web, Home Assistant e il tabellone dentro la partita.
"classifica": {},
# La musica di sottofondo dei giochi. Separata dagli effetti: chi
# gioca la sera tardi vuole spesso gli uni e non l'altra.
"musica": True,
# Quanto e' forte il computer di Pongo: facile, normale, difficile.
"pongo_livello": "normale",
# Super Quiz. Le quattro musiche sono nomi di file dentro `suoni/`,
# senza estensione: cambiandole si cambia il carattere del gioco
# senza toccare il codice.
"quiz_musica": {
"domanda": "quiz_domanda",
"attesa": "quiz_attesa",
"giusta": "quiz_giusta",
"sbagliata": "quiz_sbagliata",
},
"quiz_tempo": 60,
# Gli identificativi delle domande gia' uscite, per non rifarle. Si
# riempie da solo e si accorcia da solo.
# Le domande gia' uscite, **una lista per lingua**. Erano una sola,
# perche' le due banche erano gemelle e la stessa domanda aveva lo
# stesso identificativo; dalla 15.0 le banche sono indipendenti -- le
# domande inglesi non sono la traduzione di quelle italiane -- e un
# conto solo mescolerebbe numeri che non vogliono dire la stessa cosa.
"quiz_viste": {},
# Il tasto Start del cabinato scorre i giochi: premuto una volta si
# gioca, premuto ancora si passa al successivo. I codici sono quelli
# di una tastiera normale (invio, escape) ma su una pulsantiera da
# flipper sono altri, e si imparano dalla pagina premendo il pulsante.
# I motori del pad: si sentono solo i colpi grossi, e la tabella di
# quali sta in sources/giochi. La forza e' in percento perche' i pad
# non sono tutti uguali: un DualShock scuote piu' di un pad leggero.
"vibrazione": True,
"vibrazione_forza": 70,
# Anche i dettagli -- mattoncini, spari, bocconi -- o solo i colpi
# grossi. I dettagli stanno sul motorino piccolo e durano trenta
# millesimi: si sentono in punta di dita e non scuotono.
"vibrazione_dettagli": True,
"tasto_ciclo": 28,
"tasto_esci": 1,
# Doom nel giro del tasto Start. **Acceso**: dalla 3.8.2 il pad non
# apre piu' Doom da solo, quindi il giro e' l'unica strada che resta
# dal cabinato, e nascere spenta la rendeva irraggiungibile. Non c'e'
# il rischio di finirci dentro per sbaglio con un Doom non preparato:
# senza un WAD valido Doom non entra proprio nel giro.
"ciclo_doom": True,
# E il Game Boy, alle stesse condizioni: entra nel giro solo se PyBoy
# e' installato e la cartuccia scelta e' valida, quindi una casella su
# cui Start non fa niente non puo' esistere.
"ciclo_gameboy": True,
# Marca che la scelta l'ha fatta una persona: serve solo alla
# migrazione, per non sovrascrivere due volte una decisione altrui.
"ciclo_scelto": False,
},
"doom": {
# Il binario lo compila doom/setup_doom.sh: non arriva gia' fatto
# perche' i sorgenti di doomgeneric sono GPL2 e questo progetto e'
# GPLv3 (vedi doom/setup_doom.sh).
"binary": "/var/lib/dmd/doom/doom-dmd",
"wad": "/srv/dmd/doom/freedoom1.wad",
# Doom scrive configurazione e salvataggi nella cartella di lavoro:
# fuori da /opt/dmd, che deve restare uguale alle sue impronte.
"work_dir": "/var/lib/dmd/doom/stato",
# La fascia ritagliata dai 200 righe di Doom. Doom e' 1,6:1 e il
# pannello e' 4:1: schiacciare tutto renderebbe un nemico alto otto
# pixel. La taratura vera si fa guardando il pannello.
"band_top": 36,
"band_height": 96,
# Sotto 1 schiarisce, sopra 1 scurisce. Trovato sul pannello vero:
# schiarire sbiancava e rendeva illeggibili i menu.
"gamma": 1.15,
# Tastiera USB collegata al Pi. Vuoto = tutte quelle che trova.
"keyboard": True,
"keyboard_device": "",
# Se un tasto sul cabinato puo' *far cominciare* una partita. Spento:
# il DMD sta in mezzo a un flipper, e un tasto sfiorato per caso non
# deve portarsi via il pannello a meta' partita. A sessione aperta la
# tastiera comanda comunque il gioco.
"keyboard_starts": False,
# Joystick: PS4 e compatibili, o un pad da PC. Sotto Linux sono
# dispositivi di /dev/input come le tastiere, quindi si leggono con lo
# stesso codice. Qui l'avvio della partita e' **acceso**: un pulsante
# preciso su un pad che si tiene in mano non si preme per sbaglio.
"joystick": True,
"joystick_device": "",
"joystick_starts": True,
# Dopo quanti secondi senza comandi la partita si chiude da sola e i
# servizi riprendono. Zero = mai.
"session_timeout": 180,
# Difficolta' (1-5) e livello da cui parte una partita.
"skill": 3,
"start_map": "1 1",
},
"gameboy": {
# L'emulatore gira come processo separato: qui c'e' il programma che
# lo ospita, non una libreria da importare. Vedi gb/gb_dmd.py.
"host": "/opt/dmd/gb/gb_dmd.py",
# Condivisione SMB dove finiscono le ROM. La crea gb/setup_gb.sh.
"rom_dir": "/srv/dmd/rom",
# Nome con cui la condivisione compare in rete: \\<ip>\dmd-rom.
"share": "dmd-rom",
# L'ultima cartuccia scelta: si riapre quella, senza doverla ricercare.
"rom": "",
# Sotto 1 schiarisce, sopra 1 scurisce. Stessa convenzione di Doom.
"gamma": 1.0,
# Percentuale di righe tolte sopra e sotto alla sorgente. A 0 lo
# schermo Game Boy sta in 71 pixel al centro del pannello; salendo
# perde cielo e terreno ma diventa piu' largo, fino a riempirlo.
"overscan": 0,
# Di quante righe spostare la finestra visibile dentro lo schermo del
# Game Boy: negativo verso l'alto, positivo verso il basso. L'overscan
# taglia simmetrico, ma i giochi non sono simmetrici — il punteggio
# sta in alto, la barra della vita in basso.
"spostamento": 0,
# I quattro livelli dello schermo. Un nome dall'elenco di
# sources/gameboy.py, oppure "personalizzata" e i colori qui sotto.
"palette": "verde",
"palette_custom": ["#e0f8d0", "#88c070", "#346856", "#081820"],
# Fotogrammi al secondo mandati al pannello. Il Game Boy ne fa 59,7:
# trenta bastano all'occhio e dimezzano il traffico sulla pipe.
"fps": 30,
"keyboard": True,
"keyboard_device": "",
# Se un tasto della tastiera puo' *far cominciare* una partita.
# Spento come in Doom: dal pad non si apre mai (vedi sources/gameboy.py).
"keyboard_starts": False,
"joystick": True,
"joystick_device": "",
# Dopo quanti secondi senza comandi la sessione si chiude da sola.
# Piu' lungo di Doom: a un gioco di ruolo si sta fermi a leggere.
"session_timeout": 300,
},
# Dove sta questo DMD. Una posizione sola per tutto il progetto: la usano
# il radar, i satelliti e il meteo, e finche' e' stata una voce dentro
# `air_radar` sembrava una preferenza del radar -- chi accendeva i
# satelliti andava a cercarla nella pagina sbagliata.
#
# Nessuna coordinata preimpostata, mai. Il valore vero vive soltanto qui,
# nella configurazione locale, e non esiste da nessuna parte nel codice
# distribuito. Zero-zero sta in mezzo all'Atlantico ed e' il modo in cui il
# progetto dice "nessuno l'ha ancora messa": ogni servizio che ne ha
# bisogno se ne accorge e tace invece di mostrare il golfo di Guinea.
"posizione": {
"latitude": 0.0,
"longitude": 0.0,
},
"air_radar": {
# Le coordinate **non** stanno piu' qui: vedi `posizione`. Le due
# chiavi restano, a zero, perche' una configurazione salvata da una
# versione precedente le contiene e la migrazione le legge da li'.
"latitude": 0.0,
"longitude": 0.0,
"radius_km": 3.0,
# Quanto guardare **oltre** il raggio, senza mostrarlo. Gli aerei che
# cadono in questa fascia non vanno sul pannello ne' nel registro: la
# pagina Radar li elenca con la loro distanza, e la domanda "era
# fuori dal raggio?" smette di essere un'opinione.
"margine_km": 2.0,
"provider": "adsb.fi",
# Venti secondi, non trenta. Con un raggio di pochi chilometri un
# aereo di linea attraversa il cerchio in meno di mezzo minuto:
# interrogare piu' lentamente del transito vuol dire non vederlo mai.
# Sotto i 15 non si scende comunque — e' un servizio gratuito della
# comunita'.
"poll_interval": 20,
"display_seconds": 10,
"cooldown": 600,
"max_altitude_ft": 0,
"log_route": True,
"fields": ["route", "type", "altitude", "speed", "distance"],
"log_enabled": True,
"log_path": "/var/lib/dmd/flights.csv",
"callsign_color": "#00d0ff",
"info_color": "#ff8c1a",
# Colore della rotta, che ora ha una riga sua al centro. Vuoto =
# segue il colore dei dettagli: chi non tocca nulla non vede
# cambiare niente.
"route_color": "",
# Che fare quando i campi scelti non stanno su una riga sola:
# crop la riga viene accorciata buttando via campi (com'era)
# pages i campi si alternano a gruppi, senza perderne nessuno
# scroll la riga scorre da destra a sinistra
# Unita' di misura dei parametri di volo. I dati arrivano sempre in
# piedi e nodi: la conversione e' solo per la lettura.
"unit_altitude": "ft", # ft | m
"unit_speed": "kt", # kt | kmh | mph
"unit_distance": "km", # km | mi | nm
"overflow": "pages",
"page_seconds": 3,
"scroll_speed": 40,
"scroll_fps": 30,
},
"mqtt": {
# Predefinito: un Mosquitto sul Raspberry stesso. Cosi' la funzione
# lavora senza Home Assistant. Chi ha gia' un broker sotto Home
# Assistant mette qui quell'indirizzo e ottiene le due cose insieme.
"enabled": False,
"host": "127.0.0.1",
"port": 1883,
"username": "",
"password": "",
"client_id": "dmd",
"base_topic": "dmd",
# Topic su cui shairport-sync pubblica i metadati AirPlay.
"shairport_topic": "shairport",
# Topic facoltativo su cui qualsiasi altra cosa (tipicamente
# un'automazione di Home Assistant) puo' scrivere un JSON con il
# brano corrente. Vuoto = non si ascolta nulla.
"external_topic": "dmd/external/nowplaying",
# Entita' create automaticamente in Home Assistant.
"discovery": True,
"discovery_prefix": "homeassistant",
"node_id": "dmd",
"device_name": "kWGillo DMD Server",
},
"nowplaying": {
# Quanto resta a schermo un brano in pausa prima di lasciare il posto.
"hold_seconds": 90,
# Per quanti secondi l'orologio del brano continua ad avanzare senza
# ricevere nulla. E' un fondo di sicurezza per sorgenti che non
# annunciano la pausa: durante la riproduzione normale il silenzio di
# decine di secondi e' normale, quindi il valore va tenuto largo.
"advance_timeout": 600,
# I metadati di AirPlay letti dalla pipe locale di shairport-sync
# invece che dal broker. E' la strada principale dalla 7.4: i due
# programmi girano sulla stessa macchina, e farli parlare via rete
# voleva dire tenere allineati un indirizzo e una password in due file
# diversi -- che e' esattamente quello che si e' rotto.
#
# MQTT resta acceso e continua a funzionare: chi ha gia' tutto
# configurato non deve toccare niente, e le due strade finiscono nella
# stessa funzione. Spegnendo questa si torna al comportamento di
# prima.
"pipe": True,
# Vuoto = il percorso predefinito di shairport-sync,
# /tmp/shairport-sync-metadata. Si scrive qui solo se lo si e'
# spostato a mano.
"pipe_percorso": "",
"title_color": "#ffffff",
"artist_color": "#00ffff",
"album_color": "#0000ff",
"bar_color": "#ffff00",
# Ogni componente portata a 0 o 255: restano gli otto colori pieni,
# gli unici che su questo pannello non producono sfarfallio.
"safe_colors": True,
# La copertina del brano. L'indirizzo lo manda la sorgente insieme al
# titolo: qui si decide solo se scaricarlo e disegnarlo.
"artwork": True,
# Dove sta Home Assistant, per gli indirizzi relativi. `entity_picture`
# e' una strada -- `/api/media_player_proxy/...` -- non un indirizzo
# intero: senza questo non si sa a chi chiederla. Vuoto vuol dire
# niente copertine dagli indirizzi relativi, e nessun tentativo di
# indovinare un host.
#
# Non e' una credenziale: quell'indirizzo porta **dentro di se'** un
# token firmato da Home Assistant, quindi sul Raspberry non resta
# nessun segreto da custodire. Se un giorno servisse un token vero,
# questa strada andrebbe riprogettata invece che allargata.
"artwork_base": "",
# Il tetto alla larghezza, in pixel. Zero vuol dire meta' pannello.
# Serve perche' l'altezza e' fissa e la larghezza no: una locandina
# panoramica alta 64 sarebbe larga centoquaranta e lascerebbe i titoli
# senza spazio.
"artwork_larghezza_massima": 0,
# Livelli di colore per canale, come la Funcam. **Non e' una
# preferenza estetica**: fino alla 7.2 la copertina non si mostrava
# affatto, e il motivo scritto nel progetto era che e' fatta quasi solo
# di mezzi toni, cioe' il contenuto peggiore possibile per un pannello
# S-PWM -- lo stesso motivo per cui esiste `safe_colors`.
#
# Quattro livelli con il disordine di Floyd-Steinberg tengono la
# copertina riconoscibile togliendo la maggior parte dei mezzi toni.
# Due danno gli otto colori pieni, quelli che non sfarfallano mai.
# Zero lascia l'immagine com'e': da provare, non da dare per buono.
"artwork_livelli": 4,
# `sempre` come e' sempre stato, oppure `rotazione`: in rotazione il
# brano prende il pannello una volta ogni `ogni_n_media` foto, per
# `durata_turno` secondi. Senza Media Player acceso non c'e' niente
# con cui alternarsi, e la rotazione decade a `sempre`.
"modo": "sempre",
"ogni_n_media": 5,
"durata_turno": 20,
},
"spotify": {
# Copre la musica che non passa da AirPlay: Spotify Connect verso
# casse vere, il computer, un Echo. I token non stanno qui ma in
# /var/lib/dmd/spotify.json, per non finire in un export condiviso.
"enabled": False,
"client_id": "",
"redirect_uri": "http://127.0.0.1:8080/api/spotify/callback",
"poll_interval": 8,
},
"google": {
# Google Calendar in sola lettura. I token non stanno qui ma in
# /var/lib/dmd/google.json, per non finire in un export condiviso; il
# segreto del client viene tolto dall'esportazione come la password
# del broker MQTT.
"client_id": "",
"client_secret": "",
"redirect_uri": "http://localhost:8080/api/google/callback",
# Quanti giorni prima l'appuntamento comincia a comparire. Tre: sotto,
# il pannello mostrerebbe la fine del mese, che non e' una notizia.
"giorni": 3,
# Ogni quanto si chiede a Google, in minuti. Un calendario non cambia
# trenta volte al secondo.
"poll_minutes": 15,
"max_eventi": 10,
# L'avviso periodico sul pannello, con la stessa forma di quello delle
# scadenze: ogni quanto compare, quanto resta, quanto scorre.
"interval_minutes": 20,
"seconds": 10,
"speed": 40,
},
"time": {
"ntp_server": "pool.ntp.org",
"timezone": "Europe/Rome",
"dst_auto": True,
"utc_offset": 1,
},
"web": {
"port": 8080,
# Lingua dell'interfaccia: "it", "en", oppure vuoto per lasciar
# decidere al browser tramite Accept-Language. Non ha effetto sul
# testo mostrato sul pannello, che segue clock.language.
"language": "",
},
"ota": {
"repo": "kWGillo/zedmd-pi",
"branch": "main",
"auto_check": True,
"check_interval_hours": 24,
# Il puntino verde nell'angolo in alto a destra dell'orologio quando
# c'e' una versione nuova. Acceso di serie: il controllo quotidiano
# esisteva gia' dalla 1.5 e scriveva soltanto una riga di log, cioe'
# parlava a nessuno. Chi non lo vuole lo spegne qui, e il pannello
# torna identico a com'era.
"segnale": True,
},
# -------------------------------------------------------------- pulizia
#
# Le briciole che i Mac lasciano nelle condivisioni: `.DS_Store` e i file
# AppleDouble (`._qualcosa`), uno per ogni file copiato. Non fanno danno,
# ma su una libreria di migliaia di foto raddoppiano il numero di voci che
# il Raspberry deve elencare a ogni giro.
"pulizia": {
"enabled": True,
# Ogni quante ore. "Di tanto in dopo" e' abbastanza: le briciole si
# accumulano quando si copia, cioe' di rado.
"ore": 12,
# Vuoto = le condivisioni note, prese dalla configurazione. Si
# riempie solo per aggiungerne altre.
"cartelle": [],
# Il tetto per un solo giro: non e' contro i Mac, e' contro un errore
# nostro. Meglio accorgersene a cinquemila file che a mezza libreria.
"massimo": 5000,
},
# ------------------------------------------------------------ notifiche
#
# I messaggi che arrivano da Home Assistant. Il contratto e' piccolo di
# proposito: un topic, un JSON con testo, livello e secondi. La politica
# -- quando parlare, quando tacere, con che parole -- sta di la', dove
# stanno gia' le automazioni.
"notifiche": {
"topic": "dmd/notifica",
"secondi": 8,
"velocita": 60,
"fps": 30,
"altezza": 0.5,
# Quante notifiche si accodano prima di buttare via la piu' vecchia.
# Una notifica di mezz'ora fa non interessa piu' a nessuno.
"massimo_in_coda": 20,
# Il colore dei tre livelli. Chi pubblica puo' scavalcarlo con il
# campo `colore`, ma non deve essere obbligato a saperne niente.
"colori": {
"info": "#7ad7ff",
"avviso": "#ffa000",
"allarme": "#ff2a20",
},
},
# OnAir: il pannello dice che si sta registrando. Lo stato `in_onda` sta
# qui e non in memoria perche' deve sopravvivere a un riavvio del
# servizio, e perche' l'interruttore di Home Assistant deve ritrovarlo
# dov'era. Home Assistant lo riafferma comunque appena il DMD torna
# disponibile, cosi' una porta aperta nel frattempo non lascia acceso un
# ON AIR falso.
"onair": {
"testo": "ON AIR",
"colore_sfondo": "#c00000",
"colore_testo": "#000000",
"ogni_n_media": 2,
"al_massimo_dopo": 180,
"secondi": 0,
"in_onda": False,
},
# --------------------------------------------------------- status player
#
# Chi sta giocando, e a cosa. Due sorgenti che si sommano: l'API di
# RetroAchievements, che da' il punteggio -- l'unico numero confrontabile
# fra uno che gioca a Sonic e uno che gioca a Metal Slug -- e l'agente
# sulla Batocera degli amici, che vede anche i giochi senza cheevos.
#
# La chiave API si comporta come la password del broker: esce
# dall'esportazione. E' una credenziale personale, e un file di
# configurazione gira -- finisce in un backup, in un allegato, in una
# segnalazione.
"status_player": {
# Il tuo nickname su RetroAchievements e la chiave web dell'API
# (retroachievements.org -> Settings -> Keys).
"utente_ra": "",
"chiave_ra": "",
# Ogni quanti secondi si chiede all'API come stanno gli amici. Due
# minuti e' il compromesso: una partita dura piu' di due minuti,
# quindi non se ne perde nessuna, e sono trenta richieste all'ora per
# amico invece di centoventi.
"intervallo": 120,
# Dopo quanti minuti di silenzio una persona non e' piu' "in gioco".
# Chi spegne la macchina non manda nessun avviso: sparisce.
"minuti_vivo": 10,
# Gli amici: nome mostrato sul pannello e nickname RetroAchievements.
# Chi ha solo l'agente lascia il nickname vuoto.
"amici": [],
# Il segreto che l'agente su Batocera deve presentare. Vuoto vuol
# dire che nessuno puo' scrivere: un indirizzo che accetta qualunque
# POST e' un cartello "scrivi qui" per chiunque sia sulla rete.
"token": "",
# La notifica quando qualcuno comincia, e il giro di chi sta
# giocando. Si spengono separatamente perche' rispondono a due
# domande diverse: "e' appena successo" e "chi c'e' adesso".
"notifica": True,
"durata_notifica": 8,
"giro": True,
"durata_schermata": 7,
"intervallo_giro": 300,
},
# ------------------------------------------------------------- satelliti
#
# I passaggi visibili della Stazione Spaziale. Le coordinate non stanno
# qui: sono quelle dell'Air Radar, perche' la casa e' una sola e due
# posizioni che possono divergere sono due posizioni sbagliate.
"satelliti": {
# I gruppi di CelesTrak da scaricare. Solo le stazioni: i "cento piu'
# luminosi" sono per due terzi stadi di razzo con sigle illeggibili.
"gruppi": ["stazioni"],
# Sopra quanti gradi dall'orizzonte vale la pena annunciare.
#
# Erano dieci, ed era la soglia sbagliata presa dal posto sbagliato:
# dieci gradi e' la regola dei radioamatori, sotto cui l'atmosfera
# attenua il segnale. Ma qui il ricevitore sono gli occhi di qualcuno
# in terrazzo, e davanti a quegli occhi ci sono case.
#
# I conti, per un oggetto a 420 km: a dieci gradi e' a 1390 km di
# distanza al suolo, la distanza obliqua passa da 420 a 1500 km, ed e'
# **2,8 magnitudini piu' fioco** -- un fattore tredici. La ISS allo
# zenit e' come Venere; a dieci gradi e' una stellina, quando non e'
# dietro un tetto.
#
# Trenta gradi e' un terzo del cielo: bisogna alzare il naso, non
# guardare fra i comignoli, e si perde solo 1,3 magnitudini. Costa
# circa meta' degli annunci -- la frazione di passaggi che sopravvive a
# una soglia e' la larghezza della fascia di tracce al suolo che la
# produce, e da 10 a 30 gradi quella fascia passa da 1390 a 630 km.
# Piu' su non si poteva andare: a sessanta gradi resterebbe il 16%
# degli annunci, e con due soli oggetti noti il servizio smetterebbe
# di esistere.
"elevazione_minima": 30.0,
# Quanto prima avvisare, e ogni quanto ricordarlo. Un passaggio dura
# dai due ai sette minuti: annunciarlo mentre succede vuol dire
# arrivare in terrazzo a cose finite.
"preavviso_minuti": 10,
"cadenza_minuti": 5,
# Quanto resta a schermo un promemoria. Non e' una sorgente che occupa
# il pannello per un quarto d'ora: sono due lampi brevi.
"durata_avviso_secondi": 20,
"finestra_ore": 24,
"ricalcola_minuti": 360,
"cartella_tle": "/var/lib/dmd/tle",
# Registro dei passaggi, come quello dei voli. Ci finiscono anche i
# passaggi **non** visibili, con la colonna che lo dice e l'altezza
# del Sole che spiega perche': e' l'unico modo per rispondere a
# "stasera il pannello non ha detto niente".
"log_enabled": True,
"log_path": "/var/lib/dmd/satelliti.csv",
# Mostrare anche i passaggi che non si vedono. Non e' riempitivo: sono
# quattro al giorno contro uno, e sul pannello arrivano in
# grigio-azzurro, senza preavviso e senza lampeggio -- il colore dice
# da solo che non e' un invito a uscire. Si vede l'arco, che e' la
# parte bella, e la riga sotto dice perche' non si vede.
"mostra_non_visibili": True,
"durata_spento_secondi": 25,
# Se acceso mostra anche gli oggetti di cui **non** conosciamo la
# luminosita': si vedranno annunci per CubeSat invisibili a occhio
# nudo. Sta qui per chi vuole sperimentare, non per l'uso normale.
"tutti_gli_oggetti": False,
# Quanto in alto puo' stare il Sole perche' il pannello mostri ancora i
# satelliti, in gradi sopra l'orizzonte. A 3 il servizio si apre un
# quarto d'ora prima del tramonto e si chiude poco dopo l'alba; a 0
# esattamente al tramonto; a 90 non si chiude mai, che e' com'era prima
# della 7.2.
#
# Non e' -6, la soglia con cui si decide se un passaggio si vede, e la
# differenza e' il punto: il preavviso scatta dieci minuti prima che il
# satellite sorga, e in dieci minuti il Sole scende di due o tre gradi.
# Con il cancello a -6 sparirebbe proprio l'avviso della prima sera.
"sole_massimo": 3.0,
},
# La sveglia. Le voci nascono spente: un orario predefinito che suona da
# solo il giorno dopo l'aggiornamento sarebbe uno scherzo, non un valore
# di fabbrica.
# Le fasce orarie dei servizi, una voce per servizio che ne ha una accesa.
# Vuoto = nessuno ha fasce, cioe' tutti lavorano sempre: e' il
# comportamento di sempre, e chi aggiorna non si accorge di niente.
#
# Il Media Player non sta qui: la sua fascia esisteva gia' dalla 3.5 sotto
# `mediaplayer.timer_*` e resta li'. Spostarla avrebbe voluto dire o
# perdere l'impostazione di chi aggiorna, o tenerne due copie che prima o
# poi divergono. La pagina Timing le mostra insieme lo stesso: dove un dato
# e' scritto e' un fatto del programma, non dell'utente.
"timing": {},
"sveglia": {
"colore": "#ff3b30",
# Mentre squilla, il pulsante fisico e' suo. Vedi
# `dmdd.Runtime._pulsante_di_turno`: prima il piedino lo apriva solo
# la telecamera, quindi con la Funcam spenta una sveglia non si
# poteva zittire col pulsante — e sembrava un guasto di saldatura.
"pulsante": True,
# Per quanto suona un timer scaduto. Piu' corta di una sveglia: chi ha
# messo un timer e' in casa e a pochi metri, non sta dormendo.
"durata_timer": 90,
# Il suono del timer. Vuoto: quello della prima sveglia attiva, come
# fino alla 10.0 -- che era l'unico modo di sceglierlo, e non si
# sapeva.
"suono_timer": "",
# Le durate proposte dalla pagina, in minuti. Un elenco e non un campo
# libero perche' con le mani bagnate si preme, non si digita -- il
# campo libero c'e' lo stesso, accanto.
"timer_rapidi": [3, 5, 10, 15],
"voci": [
{"enabled": False, "ora": "07:00", "giorni": [0, 1, 2, 3, 4],
"suono": "", "etichetta": "", "durata": 120},
{"enabled": False, "ora": "08:30", "giorni": [5, 6],
"suono": "", "etichetta": "", "durata": 120},
{"enabled": False, "ora": "07:00", "giorni": [],
"suono": "", "etichetta": "", "durata": 120},
{"enabled": False, "ora": "07:00", "giorni": [],
"suono": "", "etichetta": "", "durata": 120},
],
},
# La rete. Una levetta sola, e non e' un vezzo: il risparmio energetico
# del wifi ha reso il Raspberry irraggiungibile -- pannello acceso, SSH e
# pagina web morti -- e si e' ripreso solo staccando la corrente.
"rete": {
"wifi_sveglio": True,
},
"services": {
"zedmd": True,
# Acceso di suo, e non e' una contraddizione con le voci spente: qui
# si dice che il servizio esiste, non che qualcuno debba svegliarsi.
# Con tutte le voci spente non succede niente, e chi imposta un orario
# si aspetta che suoni senza dover accendere un secondo interruttore.
"sveglia": True,
"clock": True,
"mediaplayer": False,
"banner": False,
"nowplaying": False,
"birthdays": False,
# Acceso di suo: senza scadenze inserite non disegna niente, quindi
# non ruba spazio a nessuno, e chi inserisce una scadenza si aspetta
# di vederla senza dover accendere un secondo interruttore.
"scadenze": True,
# Spento di suo: finche' non si collega un account Google non c'e'
# niente da mostrare, e un interruttore acceso su un servizio muto
# fa credere che qualcosa non funzioni.
"calendario": False,
"status_player": False,
"air_radar": False,
# Spento di suo, e non e' prudenza formale: e' una telecamera accesa
# in soggiorno. Si accende quando qualcuno decide di accenderla.
"webcam": False,
# Spento di suo perche' senza coordinate non ha niente da dire, e le
# coordinate non le mettiamo noi: le prende da quelle del radar,
# quando ci sono.
"satelliti": False,
# Spento di suo: senza un'automazione in Home Assistant che pubblichi
# qualcosa, questa sorgente resta muta per sempre. Si accende quando
# dall'altra parte c'e' qualcuno che parla.
"notifiche": False,
# Spento di suo: finche' non c'e' qualcosa che dica «in onda»
# non ha niente da mostrare, e un interruttore acceso su un
# servizio muto fa credere che qualcosa non funzioni.
"onair": False,
# Il Cielo: acceso di serie. Parla solo di notte, e solo quando il
# turno con il meteo gli da' lo spazio -- non ha niente da configurare
# prima di funzionare. Senza posizione mostra comunque la fase.
"moon": True,
"inutili": True,
# Come i satelliti: senza coordinate non ha niente da dire, e le
# coordinate non le mettiamo noi.
"meteo": False,
# Energia: spento di suo, come le notifiche. Il numero lo pubblica
# un'automazione di Home Assistant, e finche' dall'altra parte non c'e'
# nessuno che parla questo servizio avrebbe solo due trattini da
# mostrare -- cioe' un posto sotto l'orologio occupato per dire che non
# si sa niente.
"energia": False,
},
# Energia: un numero che arriva da Home Assistant e sta sotto l'orologio,
# al centro della banda del World Time. Il pannello non misura niente --
# misurare la casa e' il mestiere di Home Assistant -- e qui arriva il
# numero gia' fatto.
#
# I due modi hanno **soglie diverse e separate**, e non e' pignoleria: la
# potenza deve stare dentro un intervallo, la batteria di un accumulo
# scende lungo una scala. Un solo insieme di soglie avrebbe voluto dire,
# in meta' dei casi, campi che non vogliono dire niente. Separate, cambiare
# modo avanti e indietro non cancella i numeri dell'altro.
"energia": {
"topic": "dmd/energia",
"modo": "potenza", # «potenza» o «batteria»
# Due parole davanti al numero: «CASA 1250 W» invece di «1250 W». Con
# due fusi accanto sulla stessa banda, dire di che cosa si parla vale
# i pixel che costa.
"etichetta": "",
"unita": "W",
"decimali": 0,
# Dopo quanti minuti di silenzio il valore diventa «--». Un numero
# fermo da mezz'ora e' peggio di nessun numero: ci si fida di un dato
# che non esiste piu'. Zero vuol dire «tienilo per sempre».
"scade_minuti": 5,
# Le tre soglie della potenza, come il quadrante di Home Assistant:
# la potenza dell'impianto, dove comincia il giallo, dove comincia il
# rosso. Tutto il resto e' verde, e sopra la massima -- il punto in cui
# il contatore salta -- lampeggia. Tutte facoltative: vuota vuol dire
# spenta, e niente numeri di serie, perche' un impianto da tre
# chilowatt e uno da sei hanno soglie diverse e un valore inventato qui
# sarebbe un allarme che suona in casa di qualcuno senza motivo.
"potenza": {
"giallo": None,
"rosso": None,
"massima": None,
},
# Le tre della batteria. Qui i numeri di serie vogliono dire qualcosa,
# perche' la scala e' sempre la stessa -- da 0 a 100 -- e questi sono
# quelli che userebbe chiunque abbia un accumulo.
"batteria": {
"gialla": 50,
"rossa": 20,
# Il lampeggio ha la sua soglia, sotto il rosso: un accumulo al 18%
# e' rosso e si guarda, al 10% e' rosso e chiama.
"lampeggio": 10,
},
"colori": {
"verde": "#3ccf5a",
"giallo": "#ffc000",
"rosso": "#ff3b30",
},
},
# Info inutili. Il calendario (santo, nomi, giornata mondiale) e' nei CSV
# del programma e non chiede niente a nessuno; i personaggi famosi
# arrivano da Wikipedia e sono l'unica parte che vuole la rete: spenta
# quella casella, il servizio resta intero, solo piu' corto.
"inutili": {
"calendario": True,
# I fatti storici del giorno, dalla pagina «24 settembre» di
# Wikipedia: due righe da leggere, quindi la loro durata e' piu'
# lunga di quella delle altre schermate.
"eventi": True,
"durata_eventi": 10,
"personaggi": True,
"morti": True,
"durata_slide": 6,
},
"moon": {
# Quanti secondi sta a schermo una schermata del servizio Moon.
"durata": 15,
},
"meteo": {
# L'ora del bollettino del mattino, quello che racconta la giornata.
# Sette e' l'ora della colazione; chi si alza alle cinque lo sposta.
"ora_bollettino": 7,
# Ogni quante ore si **chiedono** i dati a Open-Meteo. Quattro bastano
# e avanzano: una previsione non cambia ogni venti minuti, e ogni
# chiamata in piu' e' traffico verso un servizio gratuito che non
# chiede niente in cambio.
"ogni_ore": 4,
# Ogni quanti minuti il meteo **prende il pannello**, con i dati che
# ha gia' in mano. E' una cosa diversa dalla riga qui sopra, e fino
# alla 7.4 erano la stessa: con il solo `ogni_ore` il meteo compariva
# sei volte al giorno, un minuto e mezzo su ventiquattro ore, cioe'
# praticamente mai. Adesso gira come il Rolling Banner.
#
# Zero spegne il giro e lascia solo il bollettino del mattino, gli
# aggiornamenti a ogni chiamata e le allerte: e' il comportamento
# della 7.4, per chi lo preferiva.
#
# Dalla 8.3 sono dieci e non venti. Venti erano stati scelti quando il
# turno si bruciava anche senza mostrarsi: con quel difetto le
# comparse vere erano 48 al giorno invece delle 72 previste, cioe' una
# ogni mezz'ora. Corretto quello, dieci minuti danno circa 139
# comparse al giorno e il due per cento del tempo del pannello.
"ogni_minuti": 10,
# La fascia del mattino: il meteo compare molto piu' spesso e mostra
# il bollettino della giornata invece della temperatura del momento.
# Dalle 6 alle 9, con i soli dieci minuti qui sopra, il meteo stava a
# schermo il 2% del tempo: chi fa colazione davanti al pannello ne
# vedeva uno se capitava. Fuori dalla fascia non cambia niente.
"mattino": True,
"mattino_inizio": "06:30",
"mattino_fine": "09:00",
"mattino_ogni_minuti": 2,
"durata_bollettino": 22,
"durata_aggiornamento": 12,
"durata_allerta": 25,
# Le allerte di MeteoAlarm. Il feed copre un paese intero e divide per
# regione: senza regione scelta non si mostra **niente**, e non tutto.
# Far comparire l'allerta della Sicilia a chi sta in Piemonte non e'
# un'approssimazione, e' un allarme falso.
"allerte": True,
"regione": "",
"paese": "italy",
# "C" o "F". Le previsioni si chiedono **sempre** in Celsius e si
# convertono al momento di scrivere: cosi' cambiare unita' non
# invalida la previsione gia' scaricata, e su MQTT esce comunque
# Celsius, che e' quello che Home Assistant sa riconvertire da solo.
"unita": "C",
},
"webcam": {
# Vuoto = la prima telecamera collegata. Si scrive un /dev/videoN
# solo quando ce n'e' piu' d'una e si vuole scegliere.
"device": "",
# Quanto si chiede alla telecamera, non quanto si mostra.
"capture_width": 640,