Eccoci arrivati all’ultima parte di questa piccola serie di post. Dopo aver parlato della parte hardware e della app web, oggi parliamo dell’ultimo blocco che compone il nostro progetto: Alexa.

Per chi se li fosse persi, ecco i post precedenti:

Ricapitolando un po’ tutto quello che ci siamo detti nei post precedenti, il funzionamento di massima del progetto è il seguente:

  • La Skill di Alexa (o l’app per smartphone realizzata in Flutter) inviano un comando alla app web, che non è altro che un Realtime Database di Firebase.
  • In esecuzione sul Raspberry c’è una app realizzata in Node che viene notificata ad ogni modifica al realtime database.
  • Ad ogni modifica al realtime database, l’app node trasforma il valore contenuto nel database nella corrispondente serie di comandi da inviare al televisore tramite LIRC.

Oggi ci focalizzeremo quindi sulla realizzazione della Skill di Alexa.

Cominciamo col dire che esistono diverse tipologie di Skill di Alexa:

https://developer.amazon.com/en-US/docs/alexa/ask-overviews/list-of-skills.html

Nel nostro caso specifico, la tipologia di Skill più calzante era sicuramente “Smart Home”, che ci avrebbe permesso di controllare la nostra TV come gli altri dispositivi smart della nostra casa (es, lampade, termostato ecc.)

L’implementazione di una Skill di questo tipo però richiede il supporto all’account linking che era decisamente out of scope per questo progetto. Abbiamo quindi deciso di realizzare una Skill di tipo “Custom”.

Gli step per la realizzazione di una Skill Custom sono:

  • Creare la Skill sul portale developer di Alexa
  • Definire tutti i possibili messaggi con cui l’utente può interagire con la Skill (interaction model)
  • Creare la funzione Lambda che Alexa invocherà ogni volta che un utente interagisce con la Skill

Per quanto riguarda la creazione della Skill e la definizione dell’interaction model, abbiamo deciso di utilizzare la CLI di Alexa perché è molto più comoda rispetto al portale developer e permette di modificare il codice in locale e fare il deploy unificato di Skill e funzione Lambda.

Ecco alcuni esempi dei messaggi che abbiamo definito nell’interaction model:

  • accendi/spegni il televisore
  • alza/abbassa il volume di {volume}
  • metti il canale {channel}

La Skill può essere attivata dicendo: “Alexa, fammi parlare con TangoTele (per qualche motivo TangoTv non veniva riconosciuto…)”, oppure è possibile direttamente fare una richiesta alla Skill dicendo: “Alexa, chiedi a TangoTele di accendere la televisione”.

Una volta definito l’interaction model, non ci resta che implementare la funzione Lambda che verrà invocata dalla nostra Skill. La CLI di Alexa ci crea uno scaffold della funzione Lambda che possiamo usare come base per i nostri sviluppi. Nel nostro caso l’implementazione è abbastanza semplice: in base alla tipologia di “Intent” che la Skill ci invia (ad esempio accendi o spegni il televisore, abbassa o alza il volume, metti il canale X, …) la funzione Lambda a sua volta aggiorna il realtime database con il relativo valore.

Quindi se ad esempio la Skill invia un intent di tipo “ChannelIntent” (ovvero la richiesta di cambiare canale):

  • in primo luogo estraiamo dall’intent il canale che l’utente vuole impostare (corrisponde al segnaposto che negli esempi precedenti era tra parentesi graffe)
  • successivamente aggiorniamo il realtime database con il valore corrispondente alla richiesta dell’utente
  • infine inviamo indietro alla Skill un messaggio da comunicare all’utente come feedback (ad esempio: “Fatto!”)

Ecco uno snippet del codice della funzione Lambda che, dato in input un tipo di comando e relativo valore, effettua la richiesta per aggiornare il realtime database:

async function setRemoteControllerCommand(type, value) {
    loadConfig();
    initFirebase();
    let userCredential = await login();
    let token = await userCredential.user.getIdToken();

    let newCommand = {
        type: type,
        value: value,
        timestamp: new Date().toISOString()
    };

    let url = process.env.TANGOTV_DB + '/remote_controller_command.json?auth=' + token;
    let response = await axios.put(url, newCommand);
    return response;
}

Firebase offre un SDK per Node, ma sembra che ci siano dei problemi con le funzioni Lambda (sembra che qualcosa resti pendente e la funzione va sempre in timeout), per cui abbiamo deciso di utilizzare le REST API.

Ecco un esempio di chiamata alla funzione:

setRemoteControllerCommand('CHANNEL', 59);

Ora facciamo il deploy della Skill e della funzione Lambda….e il risultato è il seguente:

Conclusione

Che viaggio! Abbiamo sviscerato ogni singolo aspetto della realizzazione di questo piccolo ma interessante progetto. La cosa sicuramente più stimolante di questo progetto è stata la trasversalità degli argomenti che abbiamo affrontato: siamo passati dal saldare insieme dei condensatori alla realizzazione di un interaction model per Alexa. Ho utilizzato degli strumenti che difficilmente trovano spazio nel mio lavoro quotidiano e per questo mi sento molto arricchito. Credo sia fondamentale per noi sviluppatori uscire dalla comfort zone della nostra quotidianità per non fossilizzarci e non “perdere il treno” che, nel nostro settore, viaggia sempre più ad alta velocità.