mercoledì 2 gennaio 2019

Arduino GPS Datalogger

Ho provato a riconvertire un vecchio progetto in un GPS Datalogger
Lo shield aggiunto presenta un lettore SD Card ed un modulo DS1307 con il GPS collegato su Software Serial


I principali problemi:
1) pur acquisendo a 9600 dalla seriale ho scoperto che la scrittura della SD Card e' piu' lenta ..da questo la necessita' di fare un buffer. La scrittura di un blocco di byte e' molto piu' veloce di aprire il file in append, scrivere il byte, chiudere il file

2) il nome del file di acquisizione (che viene creato sulla base del tempo acquisito dall'RTC del DS1307) deve essere nel formato 8+3 del vecchio MSDOS (nel mio caso ho preferito usare due caratteri per mese, due carattteri per giorni, due caratteri per ora ed infine due caratteri per i minuti di inizio acquisizione)

3) questo semplice script segnala comunque che la memoria dell'Arduino e' ad limite critico

non ho ancora provato la funzione di deep sleep

--------------------------------------------------------------------------------------
// Low Power header
#include "LowPower.h"

// RTC headers
#include <Wire.h>
#include "RTClib.h"

// SD Card headers
#include <SPI.h>
#include <SD.h>

// Seriale dove invia il GPS
#include <SoftwareSerial.h>


RTC_DS1307 rtc;

const unsigned long SECOND = 1000;
const unsigned long HOUR = 3600 * SECOND;

unsigned long contatore = 0;
const unsigned int dim_buffer = 250;
byte SerialData[dim_buffer]; 

Sd2Card card;
SdVolume volume;
SdFile root;

const int chipSelect = 10;
char incomingByte = 0;   // for incoming serial data

char filen[10];
String filename;


// impostazione della seriale secondaria del GPS
SoftwareSerial mySerial(8, 9); // RX, TX

void setup() {
  Serial.begin(9600);
  while (!Serial) {
  }

  // inizializzazione RTC
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  // Inizializzazione SD
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    while (1);
  }

  mySerial.begin(9600);

  contatore = 0;

  Serial.println("Inizializzazione terminata.");
}

void loop() {

  DateTime now = rtc.now();
  
  sprintf(filen, "%02d%02d%02d%02d.txt", now.month(), now.day(), now.hour(), now.minute());
  String filename(filen);
  delay(2500);

  for (int i=0; i <= 100000; i++){
        contatore = 0;
        while (mySerial.available()) {
            SerialData[contatore] = mySerial.read();
            contatore++;
          }

      
        File dataFile = SD.open(filename, FILE_WRITE);
            if (dataFile) {
              for (int s=0; s < contatore; s++){
                  dataFile.print(char(SerialData[s]));
              }
              dataFile.close();
            }
          
  }
  Serial.println("Finita acquisizione");

  //entra in sleep mode
  delay(HOUR);
  //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF,SPI_OFF, USART0_OFF, TWI_OFF);

}

Moltipicazioni con logaritmi

Un tuffo nel passato del calcolo: moltiplicazioni con i logaritmi.



Una proprieta' dei logaritmi e' che una moltiplicazione di due numeri puo' essere effettuata come somma dei rispettivi logaritmi. Questa proprieta' e' usata nei regoli calcolatori ma si inizia con un esempio da un libro dei logaritmi dove sono tabulati (precalcolati) il valore dei logaritmi

Un esempio : calcolare 44 x 36

Dalle tavole si ricava la mantissa dei due numeri. Si legge che la mantissa di 44 e' pari a 64345 mentre la mantissa di 36 e' 55630.  Visto che 44 e 36 sono numeri a due cifre la caratteristica del logaritmo e' pari a 1. (per esempio tra 44,440 e 4400 cambia solo la parte caratteristica mentre la mantissa del logaritmo rimane sempre uguale...ovviamente in base 10)

Per moltiplicare i due numeri si possono sommare quindi 1.64345 e 1.55630 per un risultato di 3.19975.




A questo punto si deve calcolare il numero per il quale il logaritmo e' pari a 19974. Dalle tavole si trova che la mantissa 19976 corrisponde al numero 1584. Dal calcolo della somma il risultato deve essere a 4 cifre (parte caratteristica pari a 3) quindi il risultato e' compreso tra 1000 e 9999
Dalle tavole sembra che il risultato della nostra moltiplicazione sia di poco maggiore di 1584 (tipo 1584.1) ma si tratta di un problema di approssimazione delle tavole. Il risultato della  moltiplicazione di due interi  e' a sua volta un intero. Quindi il risultato di 44x36=1584


Si passa quindi al regolo calcolatore
Usando il cursore si allineano i valori 4.4 su D e 3.6 su CL. Si legge il risultato 1.58x guardando l'indice 1 di C su D




Usando un regolo calcolatore si puo' spostare l'indice 10 della scala C sul valore di 4.4 su D. Con il cursore ci si sposta sul valore 3.6 su C e si legge il risultato 1.58x su D. Dato che in 36x44 le ultime cifre sono 6 e 4 la cifra meno significativa e' 4 (6x4=24) per cui il risultato e' 1.584 ..rimane solo da sistemare la virgola



Lens Flare

Stamani ho provato a fotografare la Luna e Venere molto vicine nella stessa porzione di cielo con un iPhone SE
Questa e' la foto ripresa dalla camera del telefono


Mi sono accorto subito che ci sono troppi punti luminosi (la Luna e Venere sono poco sopra il centro dell'immagine)
Iphone oltre all'immagine registra anche un piccolo video per scegliere la posa migliore


Si vede chiaramente che due punti seguono i movimenti della camera muovendosi intorno al centro dell'immagine. Si tratta dei due riflessi dei lampioni all'interno dell'obbiettivo per un fenomeno conosciuto come lens flare


questa e' l'immagine del cielo senza i lampioni


venerdì 28 dicembre 2018

Scrivere Json File in ElectronJS

Un esempio di come salvare i dati in formato JSon in un progetto ElectronJS

Prima viene creato un nuovo oggetto vuoto e successivamente si inseriscono le coppie chiave-valore per popolare l'oggetto, l'oggetto viene poi convertito in una stringa Json con stringify ed infine la stringa viene salvata sul filesystem

===============================================================
      var sinkhole = {};
      sinkhole.localita = sessionStorage.getItem("localita");
      sinkhole.comune = sessionStorage.getItem("comune");
      sinkhole.data = sessionStorage.getItem("data");
      sinkhole.ora = sessionStorage.getItem("ora");
      sinkhole.compilatore = sessionStorage.getItem("compilatore");
      sinkhole.annotazioni = sessionStorage.getItem("annotazioni");
      sinkhole.nord = sessionStorage.getItem("nord");
      sinkhole.est = sessionStorage.getItem("est");
      sinkhole.sistema = sessionStorage.getItem("sistema");

      sinkhole.diametro = diametro;
      sinkhole.deformazione = deformazione;
      sinkhole.pericolo = pericolo;
      sinkhole.elemento = elemento;
      
      var esportazione = JSON.stringify(sinkhole);

      const fs = require('fs');
      var nome_file = sessionStorage.getItem("comune")+"_"+sessionStorage.getItem("ora")+".json";
      fs.writeFile(nome_file, esportazione, (err) => {
        if(err){
            alert("An error ocurred creating the file "+ err.message)
        }
        else {
          alert("File salvato come " + nome_file);
        }
      });

giovedì 27 dicembre 2018

DynamoDB con NodeJS

per prima cosa si installa nel progetto AWS SDK per NodeJS con

npm install aws-sdk
si lancia poi DynamoDB in locale come visto qui 
per fare  il deploy sui server Amazon del DB si deve decommentare la riga evidenziata in giallo

=========================================================
var AWS = require("aws-sdk");

AWS.config.update({region: "us-west-1",endpoint: "http://localhost:8000"});

// per fare il deploy su DynamoDB sui server Amazon
//AWS.config.update({endpoint: "https://dynamodb.us-west-2.amazonaws.com"});

var dynamodb = new AWS.DynamoDB();

var params = {
    TableName : "Informazioni",
    KeySchema: [
        { AttributeName: "indice", KeyType: "HASH"},  //Partition key
        { AttributeName: "title", KeyType: "RANGE" }  //Sort key
    ],
    AttributeDefinitions: [
        { AttributeName: "indice", AttributeType: "N" },
        { AttributeName: "title", AttributeType: "S" }
    ],
    ProvisionedThroughput: {
        ReadCapacityUnits: 10,
        WriteCapacityUnits: 10
    }
};



dynamodb.createTable(params, function(err, data) {
    if (err) {
        console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
    } else {
        console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2));
    }
});


//////////// INSERT ////////////////////////
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "Informazioni";
var indice = 2015;
var title = "Il nome della rosa";

var params = {
    TableName:table,
    Item:{
        "indice": indice,
        "title": title,
    }
};

console.log("Adding a new item...");
docClient.put(params, function(err, data) {
    if (err) {
        console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
    } else {
        console.log("Added item:", JSON.stringify(data, null, 2));
    }
});

//////////// QUERY ////////////////////////
var params = {
    TableName : table,
    KeyConditionExpression: "#yr = :yyyy",
    ExpressionAttributeNames:{
        "#yr": "indice"
    },
    ExpressionAttributeValues: {
        ":yyyy": 2015
    }
};

docClient.query(params, function(err, data) {
    if (err) {
        console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
    } else {
        console.log("Query succeeded.");
        data.Items.forEach(function(item) {
            console.log(" -", item.indice + ": " + item.title);
        });
    }
});



//////////// DELETE ITEM ////////////////////////
var params = {
    TableName:table,
    Key:{
        "indice": indice,
        "title": title
    },
    ConditionExpression:"indice <= :val",
    ExpressionAttributeValues: {
        ":val": 2016
    }
};

console.log("Attempting a conditional delete...");
docClient.delete(params, function(err, data) {
    if (err) {
        console.error("Unable to delete item. Error JSON:", JSON.stringify(err, null, 2));
    } else {
        console.log("DeleteItem succeeded:", JSON.stringify(data, null, 2));
    }
});


var params = {
    TableName : table
};

//////////// DELETE TABLE////////////////////////

dynamodb.deleteTable(params, function(err, data) {
    if (err) {
        console.error("Unable to delete table. Error JSON:", JSON.stringify(err, null, 2));
    } else {
        console.log("Deleted table. Table description JSON:", JSON.stringify(data, null, 2));
    }
});

Electron Packager

Mediante Electron Packager si possono creare dei pacchetti nativi per Linux, Windows e Mac OSX usando una sola piattaforma di sviluppo (nel mio caso una Linux Box)

Electron Packager si  installa con

npm install electron-packager -g

Per creare i pacchetti Windows in una Linux Box si deve prima installare Wine in modalita' 32 e 64 bit (attezione a questo ultimo aspetto)

Windows 64 bit
electron-packager . sinkholes_electron --overwrite --asar=true --platform=win32 --arch=x64  --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName="Sinkholes ElectronJs"

Windows 32 bit
electron-packager . sinkholes_electron --overwrite --asar=true --platform=win32 --arch=ia32  --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName="Sinkholes ElectronJs"

Mac OsX
electron-packager . --overwrite --platform=darwin --arch=x64  --prune=true --out=release-builds

Linux 64
electron-packager . electron-tutorial-app --overwrite --asar=true --platform=linux --arch=x64  --prune=true --out=release-builds

Per creare pacchetti senza asar si deve usare --no-asar (al contrario di come verrebbe naturale --asar=false)


mercoledì 26 dicembre 2018

Aumentare la RAM su Lenovo T440S

Per prima cosa c'e' da indicare che il Lenovo T440S monta 4 Gb di Ram saldati direttamente su scheda madre e c'e' un solo slot di espansione. In secondo luogo e' importante scegliere delle memoria DDR3 1600 a 1.35 V e non le piu' comuni 1.5 V

Lo smontaggio dello chassis prevede la completa rimozione del fondo...questo e' ad incastro e dopo aver tolto le viti si deve procedere a far saltare i fermi uno ad uno con un plettro di plastica. Attenzione operazione delicata




Let's Encrypt e DynDNS su Debian

Ad oggi diventa sempre piu' frequente la necessita' di usare connessioni HTTPS e spesso cio' crea problemi nella fase di sviluppo quando si devono fare delle semplici prove prima di andare in produzione




Una soluzione puo'essere quella di usare i certificati di Let's Encrypt, che garantiscono un traffico criptato su canale HTTPS ma non danno garanzie sull'autenticita' del host da cui le informazioni vengono (Let's Encrypt non effettua nessuna verifica sulla veridicita' dell'host e dell'organizzazione che lo mantiene)

Prima di procedere bisogna pero' indicare che i certificati di Let's Encrypt vengono forniti a server identificati via DNS e non via semplice IP (come spesso accade nelle macchine di sviluppo). Si puo' ovviare con un DNS Dinamico come DynDNS

Su Debian si procede installando il client di DynDNS

apt-get install ddclient

creando il nuovo dyndns nell'interfaccia web di https://dyndns.it/ 

e configurando il file /etc/ddclient.conf (il nome host e' quello inserito nella pagina web e scelto dall'utente nel formato xxxx.ns0.it

# /etc/ddclient.conf
protocol=dyndns2
use=web, web=checkip.dyndns.it
server=update.dyndns.it
login=INSERIRE_IL_PROPRIO_USERNAME
password='INSERIRE_LA_PROPRIA_PASSWORD'
INSERIRE_IL_NOME_HOST


si controlla che tutto sia a posto con

ddclient -debug

e poi si lancia il servizio

/etc/init.d/ddclient restart

si passa quindi a configurare la parte https

wget https://dl.eff.org/certbot-auto 
chmod a+x certbot-auto

e si lancia lo script specificando il web server

certbot-auto --apache

durante la configurazione si dovra' rispondere ad alcune domande

I certificati di Let's Encrypt hanno validita' di 90 giorni e quindi ci puo' essere la necessita' di mettere in cron il rinnovo automatico con

certbot-auto renew

venerdì 21 dicembre 2018

Integrazione di JQuery in ElectronJS

Il porting di una web app in modalita' desktop con ElectronJS e' una operazione piuttosto indolore ma sorgono dei problemi quando si utilizzano librerie esterne come JQuery

Come riporta da questo link la soluzione e' quella di inserire prima e dopo le righe di importazione le linee evidenziate in giallo


<!-- Insert this line above script imports  -->
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>

<!-- normal script imports etc  -->
<script src="scripts/jquery.min.js"></script>    
<script src="scripts/vendor.js"></script>    

<!-- Insert this line after script imports -->
<script>if (window.module) module = window.module;</script>

semplice ed efficace

DynamoDB su localhost

Per configurare DynamoDB in localhost si devono seguire un serie di passi

1) Si deve installare AWS Cli tramite pip install awscli

2) Si deve lanciare il comando aws configure per vedere se e' stata impostata la region (di solito e' su None). Nel mio caso ho impostato eu-west-1

3) da questo link si scarica la versione locale di DynamoDB. Si scompatta e si lancia il comando (viene aperto un server in ascolto sulla porta 8000, se la porta e' occupata si possono modificare le impostazioni)

java -D"java.library.path=./DynamoDBLocal_lib" -jar DynamoDBLocal.jar

4) a questo punto si apre un altro terminale si puo' iniziare ad interagire con DynamoDB per esempio con

aws dynamodb list-tables --endpoint-url http://localhost:8000

puo'essere necessario indicare la region usando aws configure

per creare una tabella (esempi da questo link)
Attenzione : se si lavora in locale si deve sempre utilizzare lo switch --endpoint-url http://localhost:8000 ed aggiungerlo ai comandi sottostanti

aws dynamodb create-table \ --table-name Music \ --attribute-definitions \ AttributeName=Artist,AttributeType=S \ AttributeName=SongTitle,AttributeType=S \ --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

e per inserire una riga

aws dynamodb put-item \ --table-name Music \ --item \ '{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}, "AlbumTitle": {"S": "Somewhat Famous"}}' \ --return-consumed-capacity TOTAL


venerdì 7 dicembre 2018

UserId su Alexa Skills

Non ho trovato da solo il modo di richiedere ad Alexa l'identificativo dell'utente che utilizza la skill (serve per poi fornire risposte personalizzate basate su DB)...e' venuto incontro il forum
https://forums.developer.amazon.com/questions/193367/userid-alexa.html

la soluzione e' 

const userId = handlerInput.requestEnvelope.context.System.user.userId
attenzione che l'userId mostrato non e' relativo all'account Amazon dell'utente. Ogni skill genera un proprio UserID legato all'account Amazon (quest'ultimo rimane sempre mascherato)..se uno stesso  utente usa due skill differenti gli UserId che si leggeranno da Alexa  risulteranno differenti





Alexa Flash Briefing Skill

Un modo rapido di creare una Alexa Skill e' quella di utilizzare un feed RSS mediante la Flash Briefing. Di fatto non e' necessaria nessuna programmazione di un backend lambda ma e' sufficiente creare un file RSS di un certo formato su un sito disponibile in HTTPS ed indicare il link


il feed puo' essere testuale (quindi letto dalla voce di Alexa) oppure un audio MP3

--------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <ttl>30</ttl>
    <item>
      <guid>urn:uuid:1335c695-cfb8-4ebb-abbd-80da344efa6b</guid>
      <title>Prova di riunione veloce</title>
      <description>
        prova di trasmissione di Luca Innocenti
      </description>
      <pubDate>2018-12-07T09:30:00.0Z</pubDate>
     </item>
  </channel>
</rss>
--------------------------------------------------------------
La cosa interessante e' che una volta che l'utente si iscrive al flash briefing non e' necessario richiamare la skill ma questa viene attivata ogni volta che vengono richieste le notizie (per esempio "Alexa dammi le notizie" fa partire SkyTG24 ma prima viene proposto il Flash Briefing)
Esiste un limite di 4000 carattere per la parte descrizione

giovedì 6 dicembre 2018

Alexa Skill Code Generator

Uno strumento per avere uno scheletro completo di una funzione Lambda partendo da uno schema di Alexa e' dato CodeGenerator



E' sufficiente copiare il file Json derivante da Alexa Console ed avere la funzione Lambda (anche troppo dettagliata)

Permssi DynamoDB per Alexa Skill

Prima di poter utilizzare DynamoDB in connessione con una Skill si devono settare i permessi del database tramite IAM. Si va quindi in IAM Roles e si ha la lista delle proprie skill. Si clicca su quella di interesse. Di default  c'e' solo il permesso per l'uso di Cloudwatch.

Si puo' procedere cliccando su Attach Policy e spuntando AmazonDynamoDBFullAccess
Altrimenti si puo' usare un approccio piu' granulare creando una inline policy  e selezionando quanto di interesse


Alexa Dev Day Milano 5 dicembre

Partecipazione alla giornata per sviluppatori Alexa a Milano. Un paio di commenti: posto prestigiosissimo, pensavo che fosse piu' utile







mercoledì 5 dicembre 2018

Deploy Alexa Skill con Ask Cli

Per poter effettuare il deploy di  una skill ad Amazon ad ASK Cli e' necessario impostare le proprie credenziali, Si procede con

ask init --aws-setup


a questo punto vengono richieste la Access Key ID e la Secret Access Key che si recuperano dalla console AWS sotto Security Credentials/Access Keys

per poter dialogare con la skill, una volta effettuato il deploy, si usa

ask simulate -l it-IT -t "testo del messaggio"

se la skill e' in inglese si dovra' usare en-US. Nello switch -t si inserisce invece il testo del parlato

martedì 4 dicembre 2018

Alexa Address API

Esempio di API Address di Alexa

------------------------------------------------------------
{
    "interactionModel": {
        "languageModel": {
            "invocationName": "indirizzo",
            "intents": [
                {
                    "name": "GetAddressIntent",
                    "slots": [],
                    "samples": [
                        "dove vivo"
                    ]
                },
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                }
            ],
            "types": []
        }
    }
}
------------------------------------------------------------

------------------------------------------------------------
/* eslint-disable  no-console */

const Alexa = require('ask-sdk-core');

const messages = {
  WELCOME: 'Benvenuto, cosa vuoi?',
  WHAT_DO_YOU_WANT: 'Cosa vuoi chiedere?',
  NOTIFY_MISSING_PERMISSIONS: 'Abilita i permessi sulla app Alexa',
  NO_ADDRESS: 'Non è stato impostato un indirizzo',
  ADDRESS_AVAILABLE: 'Ecco il tuo indirizzo completo: ',
  ERROR: 'Errore.',
  LOCATION_FAILURE: 'C\'è un errore.Riprova',
  GOODBYE: 'Arriverderci',
  UNHANDLED: 'This skill doesn\'t support that. Please ask something else.',
  HELP: 'Chiedimi dove vivo?',
  STOP: 'Stop',
};

const PERMISSIONS = ['read::alexa:device:all:address'];

const LaunchRequest = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder.speak(messages.WELCOME)
      .reprompt(messages.WHAT_DO_YOU_WANT)
      .getResponse();
  },
};

const GetAddressIntent = {
  canHandle(handlerInput) {
    const { request } = handlerInput.requestEnvelope;

    return request.type === 'IntentRequest' && request.intent.name === 'GetAddressIntent';
  },
  async handle(handlerInput) {
    const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;

    const consentToken = requestEnvelope.context.System.user.permissions
      && requestEnvelope.context.System.user.permissions.consentToken;
    if (!consentToken) {
      return responseBuilder
        .speak(messages.NOTIFY_MISSING_PERMISSIONS)
        .withAskForPermissionsConsentCard(PERMISSIONS)
        .getResponse();
    }
    try {
      const { deviceId } = requestEnvelope.context.System.device;
      const deviceAddressServiceClient = serviceClientFactory.getDeviceAddressServiceClient();
      const address = await deviceAddressServiceClient.getFullAddress(deviceId);

      console.log('Address successfully retrieved, now responding to user.');

      let response;
      if (address.addressLine1 === null && address.stateOrRegion === null) {
        response = responseBuilder.speak(messages.NO_ADDRESS).getResponse();
      } else {
        const ADDRESS_MESSAGE = `${messages.ADDRESS_AVAILABLE + address.addressLine1}, ${address.stateOrRegion}, ${address.postalCode}`;
        response = responseBuilder.speak(ADDRESS_MESSAGE).getResponse();
      }
      return response;
    } catch (error) {
      if (error.name !== 'ServiceError') {
        const response = responseBuilder.speak(messages.ERROR).getResponse();
        return response;
      }
      throw error;
    }
  },
};

const SessionEndedRequest = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
  },
  handle(handlerInput) {
    console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);

    return handlerInput.responseBuilder.getResponse();
  },
};

const UnhandledIntent = {
  canHandle() {
    return true;
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
      .speak(messages.UNHANDLED)
      .reprompt(messages.UNHANDLED)
      .getResponse();
  },
};

const HelpIntent = {
  canHandle(handlerInput) {
    const { request } = handlerInput.requestEnvelope;

    return request.type === 'IntentRequest' && request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
      .speak(messages.HELP)
      .reprompt(messages.HELP)
      .getResponse();
  },
};

const CancelIntent = {
  canHandle(handlerInput) {
    const { request } = handlerInput.requestEnvelope;

    return request.type === 'IntentRequest' && request.intent.name === 'AMAZON.CancelIntent';
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
      .speak(messages.GOODBYE)
      .getResponse();
  },
};

const StopIntent = {
  canHandle(handlerInput) {
    const { request } = handlerInput.requestEnvelope;

    return request.type === 'IntentRequest' && request.intent.name === 'AMAZON.StopIntent';
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
      .speak(messages.STOP)
      .getResponse();
  },
};

const GetAddressError = {
  canHandle(handlerInput, error) {
    return error.name === 'ServiceError';
  },
  handle(handlerInput, error) {
    if (error.statusCode === 403) {
      return handlerInput.responseBuilder
        .speak(messages.NOTIFY_MISSING_PERMISSIONS)
        .withAskForPermissionsConsentCard(PERMISSIONS)
        .getResponse();
    }
    return handlerInput.responseBuilder
      .speak(messages.LOCATION_FAILURE)
      .reprompt(messages.LOCATION_FAILURE)
      .getResponse();
  },
};


const skillBuilder = Alexa.SkillBuilders.custom();

exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequest,
    GetAddressIntent,
    SessionEndedRequest,
    HelpIntent,
    CancelIntent,
    StopIntent,
    UnhandledIntent,
  )
  .addErrorHandlers(GetAddressError)
  .withApiClient(new Alexa.DefaultApiClient())
  .lambda();
------------------------------------------------------------


Per la skill deve essere abilitato il permesso nella parte Permission di Alexa Console



Si devono abilitare i permessi di localizzazione su https://alexa.amazon.it
(attenzione : per ricevere una risposta di deve inserire l'indirizzo di ogni dispositivo all'interno della app Alexa, non esiste un geocoding basato su IP o su GPS




Atom Editor

Alcuni comandi per Atom Editor


CTRL + e - aumeta e diminuisce il carattere
CTRL N nuovo tab
CTRL O apre un file
ALT 1, ALT 2, ALT n sposta il focus tra i vari tab

CTRL SHIFT P apre l'help di Atom in cui ci sono gli shortcuts da tastiera

Per usare i Pane si usano combinazioni di tasto
CTRL K CTRL W chiude un pane
CTRL K CTRL N sposta il focus sul prossimo pane

per aprire l'albero a sinistra con i file "Toggle TreeView" oppure CTRL + \ (backslash)

per avere una finestra di shell si puo' installare il pacchetto Platformio-ide-terminal. Per aprire un terminale si usa ALT SHIFT T, per avere il focus sul terminale CTRL "apice inverso" la stessa combinazione di tasti esce dal terminale e ritorna nell'editor

RHEL 8 beta

AGGIORNAMENTO : dopo circa un mese di utilizzo ho provato a personalizzare un po' l'installazione con Wine ed altri pacchetti e mi sono accorto che era impossibile..questa la risposta del forum di RedHat

No, it's not possible in a "normal way" by using yum, because the EPEL
repository is not configured for RHEL 8 yet.
The only way to install it currently would be to do it manually. But I don't
recommend to do this, the beta version is
meant to be used for normal testing purposes in first place, which means
testing native applications and services. :)


tempo di tornare a Debian
------------------------------------------------------------------

Sto provando la beta di RHEL 8 (Red Hat Enterprise). A differenza di Centos 7 l'interfaccia e' passata a Gnome Shell (e quindi ho perso il vantaggio di usare Centos 7 rispetto a Debian)



L'installazione non ha creato problemi. L'unica cosa da ricordare e' che per avere accesso agli aggiornamenti tramite yum con

subscription-manager register
subscription-manager attach --auto

venerdì 30 novembre 2018

Testare Lambda NodeJS in locale per Alexa Skills

Per testare una Lambda function per Alexa Skills non e' strettamente necessario utilizzare Amazon AWS con l'editor on line

per esempio si parte dallo scaricare il progetto Basic Starter da SkillTemplates (ask new --template --url https://skilltemplates.com/templates.json)

Si crea quindi nella root del progetto (dove e' contenuta la directory Lambda per capirsi) una directory test e si inseriscono i seguenti tre files

test.js
-------------------------------------------------------------------
var lambda = require('../lambda/custom/index.js');
var context = require('./context.js');
var mockEvent = require('./event.json');

var mockContext = new context();

function callback(error, data) {
  if(error) {
      console.log('error: ' + error);
  } else {
      console.log(data);
  }
}

lambda.handler(mockEvent, mockContext, callback);
-------------------------------------------------------------------


context.js
-------------------------------------------------------------------
module.exports = function() {
    return {
        succeed: function(result) {
            console.log(JSON.stringify(result, null,'\t') );
        },
        fail: function(err) {
            console.log(JSON.stringify(err, null,'\t') );
        },
        done: function(err, result) {
            //console.log("CONTEXT DONE:", err, result);
        },
        functionName: 'local_functionName',
        awsRequestId: 'local_awsRequestId',
        logGroupName: 'local_logGroupName',
        logStreamName: 'local_logStreamName',
        clientContext: 'local_clientContext',
        identity: {
            cognitoIdentityId: 'local_cognitoIdentityId'
        }
    };
};
-------------------------------------------------------------------

event.json
-------------------------------------------------------------------
{
  "session": {
    "new": false,
    "sessionId": "mock_sessionId",
    "application": {
      "applicationId": "mock_applicationId"
    },
    "attributes": {},
    "user": {
      "userId": "mock_userId"
    }
  },
  "request": {
    "type": "LaunchRequest",
    "requestId": "mock_requestId",
    "locale": "en-US",
    "timestamp": "2017-09-19T11:46:23Z"
  },
  "context": {
    "AudioPlayer": {
      "playerActivity": "IDLE"
    },
    "System": {
      "application": {
        "applicationId": "mock_applicationId"
      },
      "user": {
        "userId": "mock_userId"
      },
      "device": {
        "supportedInterfaces": {}
      }
    }
  },
  "version": "1.0"
}
-------------------------------------------------------------------

se si lancia il file test.js con

node.js

si ha come riposta
-------------------------------------------------------------------

{ version: '1.0',
  response:
   { outputSpeech:
      { type: 'SSML',
        ssml: '<speak>Hello there. What is your name?</speak>' },
     reprompt: { outputSpeech: [Object] },
     shouldEndSession: false,
     card:
      { type: 'Simple',
        title: 'Example Card Title',
        content: 'Example card body content.' } },
  userAgent: 'ask-node/2.0.5 Node/v10.4.1',
  sessionAttributes: {} }
-------------------------------------------------------------------

questo e' il file che normalmente Lambda passa a Alexa per poi essere trasformato in messaggio vocale ed spedito al dispositivo Alexa
In pratica viene generata l'intent LaunchRequest (event.json) della funzione Lambda che corrisponde a 

-------------------------------------------------------------------
const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'Hello there. What is your name?';
    const repromptText = 'Can you tell me your name?';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(repromptText)
      .withSimpleCard('Example Card Title', "Example card body content.")
      .getResponse();
  },
};
-------------------------------------------------------------------

giovedì 29 novembre 2018

Vi yank and paste

Piccolo promemoria ...visto che me lo scordo sempre

Nell'editor vi per selezionare e copiare testo si deve, in modalita' comando, premere v (comparira' la scritta -- VISUAL --  in basso) e si evidenzia il testo da copiare con i tasti freccia

A questo punto si puo' copiare (yank) con il tasto y o cancellare con d
Per incollare il testo precedentemente copiato usa p (paste)

LLama3 Anita

A seguito di questo post ho provato a vedere ho provato a vedere cosa accadeva ad utilizzare un modello specifico per la lingua italiana in...