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)

Alexa Ask Cli ed Address API

Per installare l'interfaccia di programmazione di Alexa a linea di comando (senza utilizzare gli editor Web online di Alexa ed Aws Lambda) si puo' usare Ask Cli che si installa tramite npm con

npm install -g ask-cli

verra' al termine richiesto di loggardi con la propria user e password su Amazon tramite un finestra di autenticazione web

Nel caso in cui ask risponda con un messaggio di errore del tipo


InvalidClientTokenId: The security token included in the request is invalid.

vuol dire che l'interfaccia Ask Cli non riesce ad auntenticarsi sui server Amazon.
La soluzione e' riprocedere con il comando 
ask init

(attenzione se non gia' configurato a questo punto verra' chiesto di inserire le proprie credenziali.
Su alcuni SO la procedura funziona senza problemi...in altri casi si deve usare ask init --no-browser a causa di un errore di Unhandled promise rejection)

A questo punto si inizia con l'esempio 

ask new --template --url https://skilltemplates.com/templates.json

(altrimenti  se si crea una skill ex novo
ask new --skill-name test --lambda-name test
)

Dal menu si sceglie Device Location API Starter


si entra quindi nella nuova directory del template e si inserisce

ask deploy

ogni volta che e' stato modificato il codice in locale per effettuare l'upload della skill su Amazon si deve effettuare il deploy. Si puo' effettuare anche un deploy parziale della sola funzione Lambda con 

ask lambda upload

Le Address API richiedono una autorizzazione esplicita da parte dell'utente per l'utilizzo. Si deve quindi andare su https://alexa.amazon.it cliccare Le tue Skill, Skill attivate e cercare device-location-api-starter attivare la Skill con l'autorizzazione


a questo punto si e' presentato un problema. La skill era priva di endpoint perche' non e' stata creata la corrispondente funziona Lambda. Ho quindi proceduto a creare una Lambda copiandoci poi sopra il file index.js che ho trovato nel template

----------------------------------------------
/* eslint-disable  func-names */
/* eslint-disable  no-console */

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

const APP_NAME = "Location Starter"
const messages = {
  NOTIFY_MISSING_PERMISSIONS: 'Please enable device location permissions in the Amazon Alexa app.',
  NO_ADDRESS: 'It looks like you don\'t have an address set. You can set your address from the companion app.',
  ERROR: 'Uh Oh. Looks like something went wrong.'
};
const DEVICE_LOCATION_PERMISSION = 'read::alexa:device:all:address';

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = `Hello, welcome to ${APP_NAME}. You can ask about device address by saying what is my location`;
    const repromptText = 'What would you like to know?';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(repromptText)
      .withSimpleCard(APP_NAME, speechText)
      .getResponse();
  },
};

const DeviceLocationIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'DeviceLocationIntent';
  },
  async handle(handlerInput) {
    const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
    try {
      const { deviceId } = requestEnvelope.context.System.device;
      const deviceAddressServiceClient = serviceClientFactory.getDeviceAddressServiceClient();
      const address = await deviceAddressServiceClient.getFullAddress(deviceId);
      let response;
      if (address == undefined || (address.addressLine1 === null && address.stateOrRegion === null)) {
        response = responseBuilder.speak(messages.NO_ADDRESS).getResponse();
        return response;
      } else {
        const completeAddress = `${address.addressLine1}, ${address.stateOrRegion}, ${address.postalCode}`;
        const response = `Your complete address is, ${completeAddress}`;
        return handlerInput.responseBuilder
            .speak(response)
            .withSimpleCard(APP_NAME, response)
            .getResponse();
      }
    } catch (error) {
      console.log(JSON.stringify(error));
      if (error.statusCode == 403) {
        return responseBuilder
        .speak(messages.NOTIFY_MISSING_PERMISSIONS)
        .withAskForPermissionsConsentCard([DEVICE_LOCATION_PERMISSION])
        .getResponse();
      }
      console.log(JSON.stringify(error));
      const response = responseBuilder.speak(messages.ERROR).getResponse();
      return response;
    }
  },
};


const HelpIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    const speechText = 'You can ask for the address';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .getResponse();
  },
};

const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
        || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
  },
  handle(handlerInput) {
    const speechText = 'Goodbye!';

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

const SessionEndedRequestHandler = {
  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 ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('Sorry, I can\'t understand the command. Please say again.')
      .reprompt('Sorry, I can\'t understand the command. Please say again.')
      .getResponse();
  },
};


const skillBuilder = Alexa.SkillBuilders.custom();

exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
    DeviceLocationIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler
  )
  .addErrorHandlers(ErrorHandler)
  .withApiClient(new Alexa.DefaultApiClient())
  .lambda();
 ----------------------------------------------

questo e' il file skill.json. Evidenziato in giallo la richiesta dei permessi
 ----------------------------------------------
{
  "manifest": {
    "publishingInformation": {
      "locales": {
        "en-US": {
          "summary": "Sample Short Description",
          "examplePhrases": [
            "Alexa open location starter",
            "Alexa ask location starter about my adrees"
          ],
          "keywords": [
            "keyword1",
            "keyword2",
            "keyword3"
          ],
          "name": "device-location-api-starter",
          "description": "Sample Full Description",
          "smallIconUri": "https://s3.amazonaws.com/cdn.dabblelab.com/img/skill-template-default-sm.png",
          "largeIconUri": "https://s3.amazonaws.com/cdn.dabblelab.com/img/skill-template-default-lg.png"
        }
      },
      "isAvailableWorldwide": true,
      "testingInstructions": "No special instructions.",
      "category": "EDUCATION_AND_REFERENCE",
      "distributionCountries": []
    },
    "apis": {
      "custom": {
        "endpoint": {
          "sourceDir": "lambda/custom"
        }
      }
    },
    "manifestVersion": "1.0",
    "permissions": [
      {
        "name": "alexa::devices:all:address:full:read"
      }
    ],
    "privacyAndCompliance": {
      "allowsPurchases": false,
      "locales": {
        "en-US": {
          "termsOfUseUrl": "http://dabblelab.com/terms",
          "privacyPolicyUrl": "http://dabblelab.com/privacy"
        }
      },
      "isExportCompliant": true,
      "containsAds": false,
      "isChildDirected": false,
      "usesPersonalInfo": false
    }
  }
}
 ----------------------------------------------





Di default non viene assunto l'indirizzo del dispositivo con quello configurato nel profilo Amazon. Si deve settare in modo esplicito la posizione nella app sul telefono Alexa Amazon per ogni dispositivo



Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...