Sei sulla pagina 1di 15

.

o n i " C i vi t a
a rc v

IS G. Marcon
ec
M
· II S "

ch i

Tec

o
fic
ni

ti
o n
ie
c

e L
iceo Sc

Sebastiano Melita

Node-red & BotTelegram

Utenze a portata di Bot

Marconi Institute Press – 2019


Questa pagina è intenzionalmente bianca
i n dice

1 Node-red 1
2 Bot 2
3 Tempistica dei Flow 3
4 Flows del progetto 4
5 Flow di impostazione 6
6 Flow di comando 7

e l e nco delle figure

4.1 Flow del progetto. 4


4.2 Nodo Comandi e messaggi. 5
4.3 Impostazioni dei nodi. 5
5.4 Metodo post. 6
5.5 http request. 7
5.6 Configurazione del nodo http request. 8
6.7 Nodo salva e comanda. 8

1 node-red

Il ’copyleft’ ha lo scopo di incoraggiare


le persone a cooperare e ad aiutarsi
reciprocamente, e a dare a tutti la stessa
libertà.

Richard Stallman

ode-red è una applicazione web basata su Node.js, Open-Source e di tipo

N Flow-Based. Node.js è un ambiente multipiattaforma per l’esecuzione di


codice Javascript lato server, e in questo senso è un concorrente di linguaggi
come PHP o Python.
Node.js possiede però una peculiarità che lo potrebbe rendere ad essi preferibile:
segue in maniera coerente e pulita il paradigma JavaScript Everywhere, che mira ad
unificare la realizzazione del lato client e del lato server di una applicazione web sotto
un unico linguaggio di programmazione.
Gli elementi funzionali di base di node-red sono oggetti software detti nodi, che
possono rappresentare dispositivi hardware, moduli software locali al sistema e
API di accesso a servizi in rete.
I nodi comunicano tramite messaggi rappresentati in javascript da stringhe o
da oggetti che, lungo il cammino che va dal primo nodo fino all’ultimo, vengono
modificati mediante l’aggiunta di nuovi campi, o la generazione ex-novo di nuove
stringhe o di nuovi oggetti.
node-red è ricco di librerie di nodi già pronte all’uso ma, in ogni caso, permette
all’utente di creare nodi customizzati adatti alle proprie esigenze tramite i linguaggi
di programmazione JavaScript e Python.
È anche possibile esportare da un server ad un altro i nodi in maniera semplice
ed immediata, trasferendoli sotto forma di testo formattato in JSON da copiare da
una finestra di esportazione ad un’altra di importazione. Il formato di scambio è
assimilabile ad un elenco di oggetti JSON.
In un flow chart node-red è utile immaginare i nodi terminali come dei connettori
sul canale di collegamento col mondo esterno. Una ricca libreria di nodi è in grado
di fornire connettori ai più disparati livelli di astrazione:

• I nodi connettori possono fornire una gestione diretta del mezzo fisico (livello 1
OSI), cioè comunicare con i GPIO e i transceiver (RS232, RS485, ecc.), realizzando a
livello superiore (livello 2 pila OSI) i protocolli di arbitraggio più disparati (Profibus,
Modbus, ecc);
2 1 . node - r e d

• oppure possono utilizzare i driver e i servizi di comunicazione offerti dal sistema


operativo (Raspian) per comunicare su interfacce ethernet o wifi utilizzando le
usuali API di rete del livello di trasporto (TCP e UDP).
• Ma veramente impressionante è la semplicità d’uso e realizzazione dei servizi di
rete di livello applicativo (HTTP, MQTT, COAP, WebSocket, Telegram, ecc.) perché,
con pochi nodi e seguendo il naturale flusso logico dei dati, è possibile realizzare, in
modalità per lo più visuale, una catena completa di trasferimento dell’informazione
tra servizi eterogenei.
I nodi intermedi si occupano di leggere, interpretare e smistare i contenuti tra
un servizio e l’altro, eventualmente rielaborandoli runtime anche in base agli input
dell’utente. Il tutto attraverso una programmazione in buona parte di tipo visuale.
Tipici messaggi di risposta, ad esempio, possono essere composti al volo, sotto
forma di stringhe JSON nel caso di un web service, oppure sotto forma di un
template HTML nel caso di una pagina web.
Con lo stesso elementare procedimento si possono creare server MQTT, COAP e
web socket che difficilmente, con gli usuali linguaggi di programmazione, trovano
una forma unificata di realizzazione altrettanto semplice. Si pensi alla realizzazione
niente affatto banale di un server MQTT che in node-red si riduce al solo trascina-
mento sull’area di lavoro dell’oggetto, scelto tra i tanti disponibili, avente quella
funzione.

2 b ot

Riassumiamo brevemente alcune caratteristiche importanti dei BOT:


• sono chat riservate alle macchine, cioè ad utenti non “umani”; essi realizzano lo
smistamento della messaggistica da utenti umani verso applicazioni di terze parti,
ad esempio altri servizi di rete oppure applicazioni che girano su dispositivi HW.
• servono essenzialmente a trasformare messaggi in azioni, o meglio, in una catena
di operazioni; alcune sono standard cioè impostabili all’interno del BOT stesso ed
altre sono invece custom in quanto definite su servizi remoti di terze parti.
• sono essenzialmente utenze (account) Telegram speciali, per attivare le quali non è
necessario fornire all’infrastruttura un numero di telefono.
• sono identificati da un loro id che, di fatto, è indipendente da quello della chat
principale dell’utente.
Gli utenti possono interagire con i BOT in due maniere:
• Possono inviare messaggi e comandi ai bot aprendo una chat con loro o
aggiungendoli a gruppi esistenti.
2. bot 3

• Possono inviare richieste inline direttamente dal campo di testo digitando


@username del bot e una query.

Messaggi, comandi e richieste inviati dagli utenti vengono smistati al software


in esecuzione sui server dell’utente. Il server di Telegram si occupa di gestire in
maniera trasparente all’utente tutte le funzionalità di crittografia e comunicazione
con l’API Telegram.
L’utente comunica con questo server tramite una interfaccia HTTPS che offre
una versione semplificata delle API di Telegram, detta BotApi. Una libreria Telegram
di node-red, in pratica, si occupa proprio di richiamare in maniera semplificata
queste API di rete tramite webservices, permettendo di realizzare connessioni ai
servizi sia in modalità client che in modalità server.
La registrazione di un dispositivo presso il Bot avviene in modalità client di
tipo subscriber: il dispositivo rimane in stato di ascolto delle notifiche da parte del
server, che non è reale ma simulato mediante una tecnica detta long polling.
Il long polling consente di utilizzare un dispositivo in modalità di servizio analoga
a quella di un server pur rimanendo di fatto un client. Questo è fondamentale perché
permette di iniziare dall’esterno connessioni verso server interni ad una LAN senza
dover intervenire in nessun modo sui dispositivi di protezione perimetrale della
rete locale (Firewall o NAT).
L’accesso al servizio Telegram da parte del dispositivo è filtrato tramite la va-
lutazione del token del Bot, che deve essere inserito come parametro di base del
webservice.
L’invio di un messaggio al Bot da parte dell’utente avviene invece solo a partire
da una normale chat di Telegram ed è filtrato in base alla chat id del messaggio
stesso. Il BOT può avere o meno una propria ACL (Access Control List), se questa
esiste. La chat id del mittente deve essere presente nella ACL per poter ottenere il
diritto di accesso al BOT.
Per usare un Bot è necessario inserirlo tra i contatti. Si può fare in due modi. Il
primo: cercarlo con l’opzione “Cerca”. Il secondo: aggiungerlo in automatico alla
rubrica cliccando sul link apposito.

3 t empistica dei flow

Prima di cominciare la descrizione dei singoli nodi è opportuno spiegare la logica


di funzionamento dei flow, cioè in che maniera vengono scambiati i messaggi da
un nodo all’altro, focalizzandoci sul fattore tempo.
Un nodo è assimilabile ad una funzione javascript con un messaggio per input,
un messaggio per output e una serie di parametri prelevati dai campi di testo del
form di configurazione del nodo.
4 3 . t e m p i s t ic a de i f l ow

Ma quando è eseguita questa funzione? Tutte le volte che il nodo viene triggerato,
cioè quando su un suo ingresso arriva un nuovo messaggio.
In pratica un nodo è assimilabile a una funzione javascript che viene chiamata
all’arrivo di un messaggio. Viene cioè implementato una sorta di interrupt di alto
livello che esegue una funzione alla notifica di un nuovo messaggio all’ingresso
di un nodo. È chiaramente una gestione ad eventi che è molto ben supportata dal
sottostante linguaggio node.js per il quale ogni azione sul computer (apertura di
un file, inserimento di un carattere, ecc.) è di fatto nativamente un evento.
Gli eventi non si possono gestire all’interno del codice in maniera sincrona (cioè
prevedibile) a priori come in una normale chiamata di funzione (precisamente
localizzata all’interno del codice e quindi dalla tempistica nota), ma, per forza di
cose, in maniera asincrona. node-red unifica tutta la gamma dei possibili eventi
possibili in un unico evento parametrizzato: l’arrivo di un messaggio.
La particolarità di Node.js (e quindi di node-red) è quella di gestire chiamate di
funzione asincrone in un ambiente di esecuzione rigorosamente single-threaded.
La catena delle varie esecuzioni procede dal primo all’ultimo nodo ma può essere
commutata da un flow all’altro, per mezzo di nodi switch, od oppure filtrata, cioè,
propagata o meno, in base al valore di determinati campi del messaggio.

4 f lows del progetto

Figura 4.1. Flow del progetto.

Il progetto è composto da due flow che eseguono due operazioni diverse.

• Il primo restituisce alla chat telegram, a seguito cella ricezione del comando
/scenario1, la tastiera di configurazione dello scenario richiesto.
• Il secondo interpreta i messaggi sulla chat del Bot alla ricerca di comandi di
attivazione o disattivazione di un relè.
4. flows de l p ro g e t t o 5

Prima di esaminare i due flow separatamente spieghiamo la funzione del nodo


che abbiamo chiamato “Comandi e messaggi”.
Si tratta di un receiver telegram, cioè un
ascoltatore che, realizza un server virtuale tra-
mite long polling. Il nodo, cioè, invia periodica-
mente delle richieste di servizio con le quali si
Figura 4.2. Nodo Comandi e messag-
accerta se è disponibile un nuovo messaggio
gi.
per lui. In questo caso il server Telegram si
comporta come una casella di posta che viene
interrogata con la cadenza del secondo o anche meno.
Il nodo possiede due uscite. La prima solo per i comandi e la seconda, più generale
per tutti gli altri messaggi. Nel primo flow siamo interessati ai comandi, nel secondo
ai messaggi. I messaggi e i comandi in arrivo contengono due tipi di informazioni:
• Informazioni esplicite cioè inviate dall’utente quando preme pulsanti o scrive sulla
chat;
• altre di servizio normalmente non visibili all’utente come la chat_id del mittente.
Il nodo accetterà i messaggi solo se la chat_id del mittente è inclusa nella ACL
degli utenti autorizzati impostata sul nodo dall’utente che ha configurato il servizio.
Se il mittente è autorizzato il nodo triggera, cioè invia, un messaggio con i campi
che ha ottenuto dalla richiesta di servizio cioè il messaggio (o il comando) e la
chat_id del mittente.
Il messaggio è codificato in ascii come un’unica stringa di testo che contiene la
rappresentazione JSON dell’oggetto messaggio, cioè, i campi content e Chat_id.

(a) Impostazione del filtro di comando. (b) Impostazione del Bot.

Figura 4.3. Impostazioni dei nodi.

L’impostazione del Bot (vedi fig. 4.3b) imposta il token per il diritto di accesso al
Bot e, più in basso, anche l’elenco delle chat_id delle chat telegram autorizzate ad
inviare messaggi al Bot. I messaggi con chat_id non presente in elenco verranno
scartati.
6 4 . f l ows de l p ro g e t t o

5 f low di impostazione

Il ruolo di questo flow è selezionare la tastiera adatta ad un particolare scenario di


utilizzo domotico ad esempio lo scenario di illuminazione di un ambiente oppure il
più prosaico scenario di servizio di apertura di un cancello.
La tastiera adatta deve essere scelta con un comando che riporta il nome dello sce-
nario ad esempio /soggiorno oppure /cancello. Come si può intuire immediatamente
i comandi sono sempre preceduti dal prefisso /.
Il flusso delle operazioni è:

1. ricevi il comando tenendo traccia della chat_id del mittente;


2. crea i parametri della richiesta http di tipo POST da inviare alla chat a cui si riferisce
l’id precedente;
3. esegui la richiesta del metodo sendMessage sulla API pubblica rest http pubblicata
sul server remoto di Telegram.

Il metodo sendMessage contiene il riferimento


al token del mittente cioè il token del Bot. Tra i pa-
rametri della richiesta vi è il campo reply_markup
Figura 5.4. Metodo post. che definisce le etichette dei pulsanti della tastiera
da inviare sotto forma di array di array di array di
stringhe. L’etichetta è anche il valore del messaggio Telegram che verrà inviato in
chat alla pressione del pulsante.
L’array rappresenta una tabella che definisce anche il layout dei pulsanti, nel
nostro caso due righe e due colonne.
Il Bot invia il messaggio in chat presentandosi come un qualunque altro utente
della chat tramite il proprio token. Il codice completo del nodo è:
Codice nodo
1 msg . payload = {
2 chat_id : msg . payload . chatId ,
3 text : ' Adesso puoi comandare lo scenario 1 ',
4 reply_markup : JSON . stringify ({
5 keyboard : [
6 [ ' 1 _ON ' , '1 _OFF '] ,
7 [ ' 2 _ON ' , '2 _OFF ']
8 ]
9 })
10 };
11 msg . headers = { ' content - type ': ' application / json '};
12 return msg ;
5. flow di i m p o s ta z ion e 7

Una assegnazione è richiesta per tradurre il parametro chat_id che, pur avendo
uguale significato, ha nome diverso nelle due API che andiamo ad usare, quella di
ricezione che fa l’aggiornamento dei contenuti di una chat e quella di trasmissione
che esegue il metodo sendMessage sul nodo che pubblica i contenuti sul Bot.
Il metodo di classe JSON.stringify(obj) ha per argomento un riferimento ad
un oggetto creato inline, cioè direttamente come argomento di una funzione, e
restituisce la sua rappresentazione sotto forma di stringa. Viene usato per creare il
valore del campo reply_markup che deve essere di tipo stringa.
In genere le stringhe sono, assieme ai byte, i soli formati per i dati utilizza-
bili per le trasmissioni di tipo seriale che sono tipicamente usate sui canali di
comunicazione. In un momento successivo, in ricezione, viene effettuata l’operazio-
ne contraria di parallelizzazione ricostruendo l’oggetto originale per mezzo della
funzione JSON.stringify(obj).
Dobbiamo adesso utilizzare il sender di Tele-
gram. In libreria ce ne è già uno pronto all’uso che
però al momento è inutilizzabile probabilmente
perché sono state recentemente aggiornate le API
del metodo sendMessage. Figura 5.5. http request.
È l’occasione per crearne uno nostro a partire
dal nodo generico http request che è, più in generale, in grado di interfacciarsi con
qualunque api rest http.
Il nodo può lavorare in modalità get o post a seconda del tipo di richiesta
http. Nel nostro caso le API del metodo richiedono parametri di tipo post che
devono essere forniti come messaggio in ingresso e verranno inseriti nel corpo del
messaggio http in uscita.
In un campo del form del metodo deve poi essere inserito l’URL https della
richiesta. Consultando la documentazione delle API di Telegram ricaviamo che la
struttura deve essere:
https://api.telegram.org/bot<bot_id>:<token>/sendMessage

6 f low di comando

Questo flow serve a gestire le richieste di apertura e chiusura del relè provenienti
da una chat Telegram. Ogni nodo Telegram di base esegue una particolare richiesta
di servizio richiamando una API di rete (webservices rest http) pubblicata sul
server Telegram.
La risposta all’arrivo di un messaggio può essere valutata con un nodo di debug
che restituisce:
8 6 . f l ow di c om a n d o

Figura 5.6. Configurazione del nodo http request.

msg.payload : Object
{chatId: 256332767, messageId: 1158, type:"message", content: "1_ON"}
Il flusso delle operazioni è:

1. ricevi il messaggio corrispondente allo stato del pulsante;


2. mappa lo stato del pulsante sulla chat di Telegram con il valore dello stato richiesto
da openhab;
3. esegui la richiesta del metodo sendMessage sulla API pubblica rest http pubblicata
sul server remoto (in realtà localhost) di node-red.

Il nodo successivo è un nodo funzione, cioè un nodo generico di node-red che si


occupa di eseguire programmi javascript elaborando gli ingressi e generando un
messaggio in uscita nel momento in cui il nodo viene triggerato.
Il suo ruolo è di mappare il comando della chat
sul comando di node-red, cioè tradurre i comandi
dalla sintassi della chat nella particolare sintassi
Figura 6.7. Nodo salva e coman- richiesta da node-red.
da. In linea del tutto generale questa operazione
deve essere fatta avendo cura di non inviare nulla
in presenza di messaggi spuri cioè non previsti, in questo caso non riconosciuti da
node-red, cosa che può sempre avvenire provenendo questi da chat pubbliche.
6. flow di c om a n d o 9

Per il comando dei relè un canonico costrutto if-else-if è sufficiente allo


scopo, basta inserire tanti rami quanti sono i messaggi da riconoscere.
Comando relè.
1 var m = msg . payload . content ;
2 msg . headers = { ' content - type ': ' application / json '};
3 if ( m ==" 1 _ON ") {
4 msg . payload =" ON ";
5 return msg ;
6 } else if ( m ==" 1 _OFF ") {
7 msg . payload =" OFF ";
8 return msg ;
9 }

A questo punto inviamo i comandi opportunamente tradotti al nodo


sender di Nodered che utilizza i valori come parametri di una propria
richiesta http presso un web service in ascolto sull’indirizzo localhost.
Come al solito, un sender node-red altro non è che un
particolare nodo http che compone la richiesta per noi
liberandoci dall’incombenza di studiarci le API di quel
particolare servizio di rete (in questo caso di Openhab).
MarconiInstitutePress

This work is licensed under the Creative Commons Attribution 3.0. To view a copy of this
license, visit

http://creativecommons.org/licenses/by-nc-nd/3.0/

or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, Cali-
fornia, 94041, USA.

Typeset with LATEX 2ε


MarconiInstitutePress

Potrebbero piacerti anche