Showing posts with label Amazon_Alexa. Show all posts
Showing posts with label Amazon_Alexa. Show all posts

Wednesday, February 15, 2017

SAP d-shop’s Virtual House – Un viaje de los físico a lo Virtual


Este post fué posteado originalmente en SAP d-shop’s Virtual House – A journey from Physical to Virtual.





Hace algún tiempo…nuestro buenos amigos del SAP d-shop Newtown Square (Llamados John Astill y companía) construyeron una Casa IoT para SAP Insurance. Esta pequeña casa (Por cierto, hecha y pintada a mano) utiliza un Arduino Nano, varios sensores y luces LED…y…lo cual es impresionante dicho sea de paso…una lavadora impresa en 3D con un sensor de agua…y por supuesto…estaba y está…habilitada para IoT.




Pensamos que era impresionate…así que tenemos una en nuestro propio SAP d-shop en Silicon Valley y se ha convertido en pieza clave de todos nuestros tours por el d-shop.

Luego…un tiempo después, nuestro amigos de HCP Marketing (Llamados Joe Binkley y compañía) e Intel construyeron un Edificio Inteligente. Un edificio muy bonito…controlado por Amazon Alexa que utiliza un Intel Galileo, algunos Arduinos así como también servos (motores), luces, un panel solar y hasta un ventilador…nuevamente todo esto…habilitado para IoT…pero como se deben haber imaginado ya…controlado por voz…así que puedes enviar el elevador de arriba para abaja…abrir o cerrar las puertas e inclusive enviar todo el edificio en modo de emergencia…afortunadamente…lo hemos mantenido en el d-shop por bastante tiempo y eso otro de nuestros demos con factor “wow” cada vez que alguien viene a visitarnos…




Tener ambos disponibles…poco a poco se fué enciendo la llama de la innovación y la creatividad…por que no construir una Casa Virtual que pueda ser utilizada con el Oculus Rift y que es controlada por Alexa?


No es un trabajo sencillo…pero de hecho que amamos los retos…y gracias a nuestra experiencia previa con Unity3D and Alexa working together ya sabiamos por donde empezar…



La arquitectura es bastante simple… El servidor Heroku no es más que un servidor de Eco, así que va a repetir todo lo que le pasemos como una respuesta JSON. Nuestra aplicación en Unity constantemente revisa el servidor Heroku para determinar si algún mensaje al que deba responder. Por supuesto, para que este trabaje como se quiere, necesitamos crear un skill en Amazon Alexa solo para poder actualizar el servidor. Así que, cuando decimos “open door”, entonces Alexa va a enviar un comando al servidor Heroku y este en respuesta va a generar un mensaje “open door” en JSON. Nuestra aplicación en Unity va a leer la respuesta generada por el servidor Heroku, y actuar correctamente abriendo la puerta…por supuesto, no queremos que esto siga pasándo una vez que ya se ejecutó el comando…así que luego de que Unity executa la acción va a enviar un mensaje nulo al servidor Heroku, así que la próxima vez el mensaje JSON va a ser también nulo y Unity simplemente va a esperar por el próximo comando válido.

Si quieren echar un pequeño vistazo a como se ve la Casa Virtual…aquí hay un par de imagenes…pero no se olviden de ver el video, J Así tendrán la experiencia completa -;)





Ahora…este proyecto comenzó como un “Project in a Box” (para uso interno solamente…disculpen por eso) lo cual significa…que todo el código fuente y la explicación de como construirlo desde cero debería ser proporcionada…pero…pero por razones obvias…eso no pasó L Así que en vez de eso…lo convertimos en un “Product in a Box” lo cual significa que (Disculpen nuevamente…solo empleados) pueden descargar la aplicación compilada y simplemente modificar el archivo de configuración para tenerlo funcionando por su cuenta J El código fuente no está siendo proporcionado pero obviamente con un simpático email pueden conseguirlo -;)

Consigánlo aquí

Ahora…que tengo toda su atención…por favor vean el video J Es un bonito viaje que parte la Casa IoT a la Casa Virtual pasándo por el Edificio Inteligente…



Ahora…se preguntarán por los modelos 3D Models utilizados para la Casa Virtual…como pueden ver en una de las images, la mayoría de ellos fueron descargados pero algunos de ellos fueron diseñados por nosotros J utilizándo Blender…Como el Amazon Echo, el Robot impreso en 3D y los llaveros con nombre, el Amazon Echo y obviamente la casa en sí J Para algunas otras cosas como las mesas y las plantas…esos fueron importados a Blender y “pintados a mano” puesto que la texturas no estaban disponibles.

Ahora…algo que pensamos que es realmente importante…es listar todos los problemas y lecciones que aprendimos mientras desarrollamos esta aplicación…

Problemas y Lecciones Aprendidas:

  • Como este es un Product in a Box y no un Project in a Box, no vamos a incluir el código fuente de la aplicación, pero lo que vamos a hacer en vez de eso es contarles los problemas y lecciones que aprendimos del desarrollo de este proyecto.
  • Unity utiliza el .NET Framework 3.5, el cual ya está deprecado por el .NET 4.0 así que muchas cosas no van a funcionar simplemente porque no han sido implementadas…y por qué es esto? Bueno…Unity utiliza Mono (que es .NET para Linux) y yo pienso que lo utilizán para mantener uniformidad entre todas las plataformas. Mientras Mono continue en .NET 3.5, lo más seguro es que Unity tampoco va a actualizase.
  • Cuando cargamos escenas, la iluminación puede quedar hecha un desastre…así que empiezas en el nivel uno…te mueves al nivel dos y de pronto parece que fuera de noche…la solución es simple…escogemos “Window à Lighting à Lightmaps”, desmarcamos el “Auto” checkbox y presionamos “Build” para hacer un bake a luz nuevamente.
  • Los Coroutines son simplemente asombrosos. Normalmente, no puedes hacer que tu aplicación duerma o espere…pero utilizándo Coroutines de hecho que puedes…Coroutines son como threads o hilos.
  • Cuando utlizamos luces, debemos asegurarnos de apagarlas cuando el personaje no está en el cuarto, porque esto nos va a ahorrar un poco de procesamiento gráfico y porque aunque sea virtualmente…tenemos que preocuparnos por el medio ambiente…
  • Unity no tiene una función o propiedad de wrap (salto de línea automático) para textos en 3D…lo cual es problemático especialmente si queremos hacer un muro de Twitter…así que la única opción es programar uno desde cero…aunque no es tan complicado…simplemente tomamos el texto, lo dividimos por espacio en un array…concatenamos cada palabra revisándo primero si la longitud de la cadena es menor que nuestro tope  (que puede ser el máximo número de caracteres que entran en nuestro muro), si la cadena es mayor que el tope, simplemente agregamos un retorno de carro (“\n”) antes de hacer la concatenación.
  • A medida que la aplicación crece puedes tener la necesidad de duplicar assets, lo cual es perfectamente normal y no agrega mucho poder extra de procesamiento (Especialmente si crean un Prefab y utilizan ese prefab), pero no se olviden de asignar un nombre único, de otra manera van a tener un dolor de cabeza si la aplicación necesita interactuar con esos assets.
  • Algunas veces vamos a tener que descargar modelos 3D de internet…otras veces vamos a crearlos utilizándo Blender…pero no hay que olvidar que muchas veces una simple esfera, cubo o algún otro objeto primitivo de Unity puede funcionar perfectamente utilizándo simplemente una imagen como textura.
  • Cuando creamos nuestro skill con Alexa…debemos asegurárnos de no cometer ningún error ortográfico…de otra manera se van a golpear la cabeza pensándo porque Alexa no está haciendo lo que le hemos pedido que haga…
  • Cuando probamos nuestra aplicación Debug.log() y Print() se volverán tus mejores amigos…nada mejor que un valor impreso o un mensaje para darse cuenta que es lo está funcionando mal.
  • Cuando movemos un objeto, debemos asegurárnos de guardar la posición original  y luego agregar el nuevo valor al valor que tenemos guardado. De lo contrario, algo puede provocar que los valores resulten errados…teniendo guardados los valores originales, podemos evitar tener que calcular los valores originales nuevamente y simplemente retornar al valor original para que todo encaje un su lugar.
  • Cuando utilizámos Texo en 3D nos daremos cuenta de que aún cuando coloquemos un objeto en frente del textp…este se mantendrá siempre visible…lo cual obviamente no es muy bonito…así que temos dos opciones…crear un shader para ocultarlo…o la manera más simple…hacer el objeto que está en frente...transparente. Esto no es algo perfecto para cualquier solución pero por lo menos funcionará.
  • El mayor problema al que nos podemos enfrentar cuando hacemos que Unity y Alexa conversen, es que cuando le digamos a Alexa que prenda las luces, ella responderá “La luz está prendida”…pero si le decimos lo mismo una segunda vez su respuesta debería ser “La luz ya está prendida” …para hacer esto…deberíamos utilizar una Base de Datos o algo parecido para almacenar la información…y cuando cerramos la aplicación, deberíamos limpiar los estados…si bien esto se puede hacer…es bastante trabajo, y que pasa si la aplicación colapsa de improviso? Deberíamos resetear los estados manualmente? No es lo ideal…
  • Esto nos lleva al punto de utilizar el elevador…podemos abrir la puerta o enviarlo a cualquiera de los pisos…en línes generales…eso es bastante sencillo…cada piso es una escena, así que necesitamos estar en el primer piso para poder enviar el elevador al segundo o tercer piso…pero…que pasa si estamos afuera del elevador? Estamos en el primer piso…pedimos el tercer piso…y luego abrimos la puerta…a medida que el personaje se mueve junto con el elevador…cuando abrimos la puerta todo se va a ver mal…solución? Simplemente utilizamos un cubo que no tenga un mesh renderer, así que es invisible…asignámos un collider con “is trigger” habilitado…y validamos que el personaje está interactuándo con el cubo para poder hacer que el elevador se mueva…de esa manera, así aunque pidamos el tercer piso y Alexa confirme que el elevador se está moviendo hacia arriba…nada va a pasar…cuando abrimos la puerta…podemos asumir que el elevador subió o bajó hasta tu piso para que podamos entrar…solo una ilusión…pero funciona…
  • Alexa no tiene una opción para retrazar el re-prompt (cuando pregunta si queremos hacer algo más), así que cuando exploramos la Casa Virtual ella nos preguntará “What else can I do for you?” y si no le respondemos nada, el skill simplemente se irá a dormir…así que tendremos que despertarla nuevamente…eso es algo un poco triste debido a la naturaleza de la aplicación…pero no hay nada que podamos hacer a menos que Amazon proponga una solución para que el re-prompt espere más tiempo…
  • Como todo el Alexa-Unity3D depende de Heroku…podemos esperar algunos retrazos o comandos dados por Alexa que no van a ser replicados necesariamente en el mundo virtual…puede ser un problema con la conexión a Internet o simplemente un problema con Heroku …

Como mencioné al inicio…el entorno se ve afectado por el clima…si está soleado…veremos un cielo claro y soleado…si está lloviendo veremos cielo oscuro y húmedo…y esto involucra utilizar un Skybox…aunque no un Skybox común y corriendo…y a propósito de esto, que es un Skybox? Bueno…simplemente…es un cubo que cubre todo el entorno y tiene diferentes images para simular el cielo…el problema es que un Skybox regular solo nos permite asignar 6 lados…lo cual por supuesto no es óptimo…necesitamos un Skybox de 12 lados…para que así podamos asignar images de cielo soleado e imágenes de cielo oscuro…de esa manera cuando revisamos el clime podemos modificar la luminosidad y esto va a afectar al Skybox puesto que va a utilizar uno u otro brindándonos el efecto de que está reflejando el clima que hace afuera…
Greetings,

Blag.
Development Culture.

Friday, December 02, 2016

Unity3D y Alexa trabajándo juntos

Este post fué posteado originalmente en Unity3D and Alexa working together.


Desde hace tiempo ...Tuve la idea de hacer que Unity3D y Alexa trabajen juntos...sin embargo...otros proyectos me mantuvieron ocupado y me quitaron tiempo para poder trabajar en esto...así que...hace unos días...una conversación con un amigo me hizo recordar que tenía muchas ganas de hacer esto...así que lo hice :)

Al inicio...no estaba exactamente seguro...pero lentamente las ideas se comenzaron a formar en mi mente...que pasaría si Unity leyera un webservice que es actualizado por Alexa? Cuando el comando correcto es interpretado, entonces Unity creará el objeto y problema resuelto...parece sencillo, no? Bueno...en realidad lo es...


Primero lo primero...tenemos que crear un pequeñp servidor web en NodeJS con Heroku...luego...necesitamos instalar Heroku Toolbelt...

Ahora...creamos una carpeta llamada node_alexa y dentro creamos los siguientes archivos...

package.json
{
  "dependencies": {
    "express": "4.13.3"
  },
  "engines": {
    "node": "0.12.7"
  }
}

procfile
web: node index.js
index.js
var express = require('express')
    ,app = express()
    ,last_value;

app.set('port', (process.env.PORT || 5000));

app.get('/', function (req, res) {
  if(req.query.command == ""){
 res.send("{ \"command\":\"" + last_value + "\"}");
  }else{
 if(req.query.command == "empty"){
  last_value = "";
  res.send("{}");
 }else{
  res.send("{ \"command\":\"" + req.query.command + "\"}");
  last_value = req.query.command;
 }
  }
})

app.listen(app.get('port'), function () {
  console.log("Node app is running on port', app.get('port')");
})

Una vez que tenemos esto...nos logeamos en Heroku Toolbelt y escribimos lo siguiente...

Heroku Toolbelt
cd node_alexa
git init .
git add .
git commit -m "Init"
heroku apps:create "yourappname"
git push heroku master
heroku ps:scale = web 0
heroku ps:scale = web 1

El WebService está listo para rockear -:) Deberías poder encontrárlo si vas a "http://yourappname.herokuapp.com/"

Ahora...este simple webservice potenciado por NodeJS nos servirá como un pequeño servidor de Eco...lo cual significa...cualquier cosa que escribamos nos devolverá una respuesta json...por supuesto...si escribimos "empty" entonces la respuesta será un json vacío...así que la idea principal es que podamos guardar el último valor ingresado...si pasamos un comando, este va a ser llamado cuando volvamos a llamar el servicio sin pasar ningún tipo de comando...así que si lo llamamos una vez...podemos llamarlo multiples veces sin interferir con su valor...

Lo siguiente en la lista...será crear nuestra aplicación en Unity...

Creamos una nueva aplicación y la llamamos "WebService" o algo parecido...el nombre del proyecto no importa mucho...

En la ventana de Hierarchy seleccionamos "Main Camera" y cambiamos los detalles del "Tranform" de esta forma...


Ahora, creamos un nuevo "3D Object" -> "Cube" y lo llamamos "Platform" con los siguientes detalles en el "Transform"...


Luego de eso, debemos crear cuatro paredes que van a ir alrededor de la plataforma...así que creamos 4 "3D Object" -> "Cube" y los llamamos "Wall 1", "Wall 2", "Wall 3" y "Wall 4"...





Cuando todo está listo, nuestro workspace debería verse así...


Vamos al tab project y creamos una carpeta llamada "plugins" y después creamos un nuevo archivo C# llamado "SimpleJSON"...dentro copiamos el código fuente que esta aquí...esto nos va a pemitir utlizar SimpleJSON para interpretar el JSON...

Ahora...creamos otra carpeta llamada "Script" y dentro creamos un nuevo archivo C# llamado "MetaCoding"...o cualquier otra cosa...



MetaCoding.cs
using UnityEngine;
using System.Collections;
using System.Net;
using System.IO;
using SimpleJSON;

public class MetaCoding : MonoBehaviour {

    int counter = 1;

    IEnumerator DownloadWebService()
    {
        while (true) { 
            WWW w = new WWW("http://yourapp.herokuapp.com/?command");
            yield return w;

            print("Waiting for webservice\n");

            yield return new WaitForSeconds(1f);

            print("Received webservice\n");
        
            ExtractCommand(w.text);

            print("Extracted information");

            WWW y = new WWW("http://yourapp.herokuapp.com/?command=empty");
            yield return y;

            print("Cleaned webservice");

            yield return new WaitForSeconds(5);
        }
    }

    void ExtractCommand(string json)
    {
        var jsonstring = JSON.Parse(json);
        string command = jsonstring["command"];
        print(command);
        if (command == null) { return;  }
        string[] commands_array = command.Split(" "[0]);
        if(commands_array.Length < 3)
        {
            return;
        }
        if (commands_array[0] == "create")
        {
            CreateObject(commands_array[1], commands_array[2]);
        }
    }

    void CreateObject(string color, string shape)
    {

        string name = "NewObject_" + counter;
        counter += 1;
        GameObject NewObject = new GameObject(name);

        switch (shape)
        {
            case "cube":
                NewObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
                break;
            case "sphere":
                NewObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                break;
            case "cylinder":
                NewObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
                break;
            case "capsule":
                NewObject = GameObject.CreatePrimitive(PrimitiveType.Capsule);
                break;
        }
        NewObject.transform.position = new Vector3(0, 5, 0);
        NewObject.AddComponent();
        switch (color)
        {
            case "red":
                NewObject.GetComponent().material.color = Color.red;
                break;
            case "yellow":
                NewObject.GetComponent().material.color = Color.yellow;
                break;
            case "green":
                NewObject.GetComponent().material.color = Color.green;
                break;
            case "blue":
                NewObject.GetComponent().material.color = Color.blue;
                break;
            case "black":
                NewObject.GetComponent().material.color = Color.black;
                break;
            case "white":
                NewObject.GetComponent().material.color = Color.white;
                break;
        }
    }

        // Use this for initialization
    void Start () {
        print("Started webservice import...\n");

        StartCoroutine(DownloadWebService());
    }
 
 // Update is called once per frame
 void Update () {
 
 }
}

Una vez que tenemos el código...simplemente debemos adjuntarlo al Main Camera...


El concepto básico para este script es bastante simple...Estamos creándo "DownloadWebService" como un método IEnumerator para poder llamarlo como un Coroutine...y eso nos permite poder poner la función a dormir puesto que necesitamos darle un poco de tiempo entre llamadas a la función...

Este método va a llamar a nuestro WebService en Heroku buscándo un comando "create"...una vez que lo tiene...va a interpretar la respuesta JSON y dividirla en 3...para que podamos tener..."create", "blue" y "sphere"...esto va a llmar a CreateObject que luego va a crear la esfera azul...luego de que ha hecho eso...el coroutine va a continuar y simplemente va a enviar un nuevo comando a nuestro WebService para limpiar el resultado...para hacer que esto funcione de manera correcta...queremos darle un tiempo de 5 segundos después de haber el limpiado el webservice antes de poder llamar a otros comando "create"...

Y esta llamada va a ser hecha por nuestro skill en Alexa...así que basicamente cuando decimos "create blue sphere" en Alexa...ella va a enviar el comando al WebService...va a actualizar el mensaje y nuestra aplicación en Unity va a tomar el mensaje...hacer su trabajo...y luego limpiar el Webservice...luego esperar a que Alexa le provea el siguiente comando...

Así que...para ir finalizándo...necesitamos crear nuestro skill en Alexa...

Primera, vamos a crear una función Lambda...así que nos logeamos aquí...

Por supuesto...yo ya tengo todo configurado...así que vamos a crear una función dummy solamente para mostrárles los pasos...

Debemos hacer click en "Create Lambda Function" y veremos la siguiente pantalla...


Por supuesto hay bastantes...así que escribimos "Color" en el filtro...


Escogemos "alexa-skills-kit-color-expert"


Dejamos todo como está y presionamos "Next"


Escogemos un nombre y descripción...


Escogemos un rol existente si es que tenemos alguno creado anteriormente...de otra forma creamos un nuevo lambda_basic_execution...luego aumentamos el Timeout a 10 segundos y dejamos todo lo demás como está...presionamos "Next"...una ventana de confirmación va a aparecer...así que simplemente presionamos "Create function"...

Vamos a ser presentados con una ventana en donde podemos subir nuestro código fuente (lo cual vamos a hacer un poco más adelánte) y un número ARN...el cual necesitamos para un paso más adelánte...


Esta sección explica como crear el skill en Alexa...así que favor sigan mis pasos...y logeense aquí...


Escogemos "Alexa Skills Kit"...y creamos un nuevo skill...



Escogemos un nombre para nuestro skill y lo más importante...escogemos un "Invocation Name"...que es lo que le vamos a decir a Alexa para que abra nuestra aplicación..algo como..."Alexa, open Sandbox"...presionamos click...

En el tab de Interaction Model tenemos dos ventanas...llenamos esto en "Intent Schema"...

Intent Schema
{
  "intents": [
    {
      "intent": "GetUnityIntent",
      "slots": [
        {
          "name": "color",
          "type": "LITERAL"         
        },
        {
          "name": "shape",
          "type": "LITERAL"
        }
      ]
    },
    {
      "intent": "HelpIntent",
      "slots": []
    }
  ]
}

Estos son basicamente parámetros que podemos usar cuando estamos preguntándole algo a Alexa...

Y debemos llenar esto en "Sample Utterances"...

Sample Utterances
GetUnityIntent create {red|color} {sphere|shape}
GetUnityIntent create {yellow|color} {sphere|shape}
GetUnityIntent create {green|color} {sphere|shape}
GetUnityIntent create {blue|color} {sphere|shape}
GetUnityIntent create {black|color} {sphere|shape}
GetUnityIntent create {white|color} {sphere|shape}

GetUnityIntent create {red|color} {cube|shape}
GetUnityIntent create {yellow|color} {cube|shape}
GetUnityIntent create {green|color} {cube|shape}
GetUnityIntent create {blue|color} {cube|shape}
GetUnityIntent create {black|color} {cube|shape}
GetUnityIntent create {white|color} {cube|shape}

GetUnityIntent create {red|color} {cylinder|shape}
GetUnityIntent create {yellow|color} {cylinder|shape}
GetUnityIntent create {green|color} {cylinder|shape}
GetUnityIntent create {blue|color} {cylinder|shape}
GetUnityIntent create {black|color} {cylinder|shape}
GetUnityIntent create {white|color} {cylinder|shape}

GetUnityIntent create {red|color} {capsule|shape}
GetUnityIntent create {yellow|color} {capsule|shape}
GetUnityIntent create {green|color} {capsule|shape}
GetUnityIntent create {blue|color} {capsule|shape}
GetUnityIntent create {black|color} {capsule|shape}
GetUnityIntent create {white|color} {capsule|shape}

GetUnityIntent {thank you|color}

Estos son todos los comandos que Alexa puede entender...y sí...podriamos haber utilizado "Custom Slot Types" para hacer el código más corto...pero...he tenido problemas donde no funciona muy bien cuando hay mas de un slot...simplemente presionamos next...


Aquí, escogemos AWS Lambda ARN...y escogemos North America o Europe dependiendo de nuestra locación física...luego en la caja de texto...simplemente copiamos y pegamos el número ARN que recibimos de nuestra función Lambda...

Esto nos va a enviar al tab de "Test"...pero en realidad no queremos eso e inclusive no podemos usarlo...así que debemos regresar al tab "Skill Information" y encontraremos que un nuevo campo ha aparecido...

Este campo será "Application Id"...copiamos este número y continuamos hacia la parte final...

Creamos una carpeta llamada "Unity" y dentro una carpeta llamada "src"...dentro de esa carpeta copiamos el archivo "AlexaSkills.js"

Vamos a utilizar el módulo "request" de NodeJS...así que lo instalamos localmente en la carpeta Unity...

sudo npm install --prefix=~/Unity/src request 

Esto va a crear una carpeta llamada node_module conteniendo al módulo request...

Luego, creamos un nuevo archivo llamado "index.js"


index.js
var request = require("request")
  , AlexaSkill = require('./AlexaSkill')
    , APP_ID     = 'yourappid';

var error = function (err, response, body) {
    console.log('ERROR [%s]', err);
};

var getJsonFromUnity = function(color, shape, callback){

var command = "create " + color + " " + shape;

if(color == "thank you"){
 callback("thank you");
}
else{
var options = { method: 'GET',
  url: 'http://yourapp.herokuapp.com/',
  qs: { command: command },
  headers: 
   { 'postman-token': '230914f7-c478-4f13-32fd-e6593d8db4d1',
     'cache-control': 'no-cache' } };

var error_log = "";

request(options, function (error, response, body) {
 if (!error) {
  error_log = color + " " + shape;
 }else{
  error_log = "There was a mistake";
 }
  callback(error_log);
    });
}
}

var handleUnityRequest = function(intent, session, response){
  getJsonFromUnity(intent.slots.color.value,intent.slots.shape.value, function(data){
 if(data != "thank you"){
 var text = 'The ' + data + ' has been created';
 var reprompt = 'Which shape would you like?';
    response.ask(text, reprompt);
 }else{
  response.tell("You're welcome");
 }
  });
};

var Unity = function(){
  AlexaSkill.call(this, APP_ID);
};

Unity.prototype = Object.create(AlexaSkill.prototype);
Unity.prototype.constructor = Unity;

Unity.prototype.eventHandlers.onSessionStarted = function(sessionStartedRequest, session){
  console.log("onSessionStarted requestId: " + sessionStartedRequest.requestId
      + ", sessionId: " + session.sessionId);
};

Unity.prototype.eventHandlers.onLaunch = function(launchRequest, session, response){
  // This is when they launch the skill but don't specify what they want.

  var output = 'Welcome to Unity. Create any color shape by saying create and providing a color and a shape';

  var reprompt = 'Which shape would you like?';

  response.ask(output, reprompt);

  console.log("onLaunch requestId: " + launchRequest.requestId
      + ", sessionId: " + session.sessionId);
};

Unity.prototype.intentHandlers = {
  GetUnityIntent: function(intent, session, response){
    handleUnityRequest(intent, session, response);
  },

  HelpIntent: function(intent, session, response){
    var speechOutput = 'Create a new colored shape. Which shape would you like?';
    response.ask(speechOutput);
  }
};

exports.handler = function(event, context) {
    var skill = new Unity();
    skill.execute(event, context);
};

El código es bastante simple...principalmente porque la mayoría es un template...simplemente lo copia...cambias un par de cosas y estás listo para continuar...

Basicamente cuando decimos "Alexa, open Unity"...ella va a escuchar nuestros pedidos...así que podemos decir "create green cube"...y va a llamar a nuesto WebService en Heroku y luego esperar por otro comando...si no le hablas nuevamente...te va a invitar a decir algo...si le dices "Thank you" ella va a desactivarse a si misma de una manera muy educada...

Y eso es basicamente todo...una vez que Alexa envia el comando al WebServer...nuestra aplicación en Unity va a leerla y actuar como corresponde...creándo cualquier forma y color que le hemos pedido...interesánte, huh?

Pero por supuesto...no me creen, no? No puede ser tan simple...bueno...si y no...es simple...pero saqué todos los puntos complicados para poder proveerles una serie de instruciones claras y consisas...

Así que...así es como se ve cuando ejecutamos la aplicación en Unity...



Y aquí está el video en acción...


Espero que les guste...y mantenganse alertas...porque para mi esto solo fué una prueba de concepto...mi próximo proyecto va a llevar esto al siguiente nivel...

Saludos,

Blag.
Development Culture.

Monday, September 28, 2015

Amazon Alexa y Philips Hue

Este post fué posteado originalmente en Amazon Alexa and Philips Hue.



Si eres un empleado de SAP, por favor siguenos en Jam.


Desde que llegó el Amazon Alexa al d-shop aquí en SAP Labs Silicon Valley...estaba buscándo la forma de hacerla interactuar con diferentes cosas...así que hace un par de días mi buen amigo y colega Aaron Williams me preguntó sobre el Philips Hue...

Al inicio...no estaba seguro de que fuera a funcionar, porque como todo el mundo sabe...el Hue necesita estar en la misma red para poder trabajar...y Amazon Lambda está en un servidor quien sabe donde...así que comenzé a investigar...

Cuando utilizamos el app de Amazon Alexa podemos enlazar el Hue para permitirle a Alexa prender o apagar las luces...pero nada más...

Debemos ir a Settings --> Connected Home --> Discover Devices (Te va a pedir que presiones el botón en el Hue Bridge).

Por supuesto...esto no era suficiente para mi...así que seguí pensándo en esto y me di cuenta de que "Hey!...el app de Philips Hue funciona cuando quieres cambiar el color de las luces...y lo hace de forma remota...así que debe de haber una manera de hacerlo"...

Afortunadamente...encontré este blog...Philips Hue Remote API Explained...donde el autor basicamente explica como puedes "engañar" al Hue para hacerle pensar que te estás conectándo desde tu IPhone...entonces...estás haciendo una conexión remota...un genio si me lo preguntan -;)

Para que esto funciones debemos crear una cuenta en meethue...si solo nos logeamos con nuestra cuenta de Google no va a funcionar...por lo menos no me funcionó a mí...


Por cierto...si encuentras algo como esto a final "%3D" simplemente cambialo por "="

Teniendo ambos el BridgeId y el AccessToken deberíamos estar listos para continuar -;)

Debemos crear una Función y un Skill...por favor vean este blog Amazon Alexa y SAP HANA para poder crearlas...

La función se va a llamar getLights y el skill Lights. El nombre de invocación será "lights".

Aquí esta el Interaction Model que vamos a utilizar...

Intent Schema
{
  "intents": [
    {
      "intent": "GetLightsIntent",
      "slots": [
        {
          "name": "color",
          "type": "LITERAL"
        }
      ]
    },
    {
      "intent": "HelpIntent",
      "slots": []
    }
  ]
}

Sample Utterances
GetLightsIntent color {red|color}
GetLightsIntent color {blue|color}
GetLightsIntent color {green|color}
GetLightsIntent color {pink|color}
GetLightsIntent color {purple|color}
GetLightsIntent color {aqua|color}
GetLightsIntent color {white|color}
GetLightsIntent color {yellow|color}
GetLightsIntent color {black|color}

HelpIntent help
HelpIntent help me
HelpIntent what can I ask you
HelpIntent get help
HelpIntent to help
HelpIntent to help me
HelpIntent what commands can I ask
HelpIntent what commands can I say
HelpIntent what can I do
HelpIntent what can I use this for
HelpIntent what questions can I ask
HelpIntent what can you do
HelpIntent what do you do
HelpIntent how do I use you
HelpIntent how can I use you
HelpIntent what can you tell me
HelpIntent how do i change colors

Ahora...lo más importante es el código -:P

Vamos a crear una carpeta y la llamaremos “Lights” o algo así…luego creamos una carpeta llamada “src”. Copiamos el código que esta aquí...y creamos un archivo llamado “AlexaSkills.js”

Vamos a necesitar instalar el paquete request para nuestra función, así que hagamos esto en el terminal

sudo npm install --prefix=~/Alexa/Lights/src request

Esto va a crear una carpeta llamada “node_modules” con el paquete es nuestra carpeta del proyecto…luego creamos un archivo llamado “index.js” y copiamos y pegamos el siguiente código…

index.js
var request = require("request")
  , AlexaSkill = require('./AlexaSkill')
  , APP_ID     = 'YourAPPId'
  , TOKEN = 'YourAccessToken';

var error = function (err, response, body) {
    console.log('ERROR [%s]', err);
};

var getJsonFromLights = function(color,callback){
switch(color){
 case 'red':
  x = 0.674; y = 0.322; break;
 case 'green':
  x = 0.408; y = 0.517; break;
 case 'blue':
  x = 0.168; y = 0.041; break;
 case 'white':
  x = 0.3227; y = 0.329; break;
 case 'yellow':
  x = 0.4317; y = 0.4996; break;
 case 'purple':
  x = 0.2725; y = 0.1096; break;
 case 'orange':
  x = 0.5562; y = 0.4084; break;
 case 'pink':
  x = 0.3944; y = 0.3093; break;
 case 'black':
  x = 0.168; y = 0.041; break;    
 case 'aqua':
  x = 0.2858; y = 0.2747; break;
 default:
  x = 0.3227; y = 0.329; break;
}

var options = { method: 'POST',
                url: 'https://www.meethue.com/api/sendmessage',
                qs: { token: TOKEN},
                headers: { 'content-type': 'application/x-www-form-urlencoded' },
                body: 'clipmessage={ bridgeId: "YourBridgeId", 
                                     clipCommand:  {url:"/api/0/lights/1/state", 
                                     method: "PUT", body: {xy:[' + x + ',' + y + ']}}}' };

var error_log = "";

request(options, function (error, response, body) {
 if (!error) {
  error_log = color;
 }else{
  error_log = "There was a mistake";
 }
 callback(error_log);
    });
}

var handleLightsRequest = function(intent, session, response){
  getJsonFromLights(intent.slots.color.value, function(data){
 var text = 'The color has been changed to ' + data;
    var cardText = 'The color has been changed to ' + intent.slots.color.value;

    var heading = 'The color has been changed to ' + intent.slots.color.value;
    response.tellWithCard(text, heading, cardText);
  });
};

var Lights = function(){
  AlexaSkill.call(this, APP_ID);
};

Lights.prototype = Object.create(AlexaSkill.prototype);
Lights.prototype.constructor = Lights;

Lights.prototype.eventHandlers.onSessionStarted = function(sessionStartedRequest, session){
  console.log("onSessionStarted requestId: " + sessionStartedRequest.requestId
      + ", sessionId: " + session.sessionId);
};

Lights.prototype.eventHandlers.onLaunch = function(launchRequest, session, response){
  // This is when they launch the skill but don't specify what they want.

  var output = 'Welcome to Hue Lights. Please say a color';

  var reprompt = 'Which color would you like?';

  response.ask(output, reprompt);

  console.log("onLaunch requestId: " + launchRequest.requestId
      + ", sessionId: " + session.sessionId);
};

Lights.prototype.intentHandlers = {
  GetLightsIntent: function(intent, session, response){
    handleLightsRequest(intent, session, response);
  },

  HelpIntent: function(intent, session, response){
    var speechOutput = 'Change your Hue Light to any color. Which color would you like?';
    response.ask(speechOutput);
  }
};

exports.handler = function(event, context) {
    var skill = new Lights();
    skill.execute(event, context);
};

Cuando está listo...simplemente comprimimos la carpeta node_module,  y los archivos AlexaSkills.js y index.js en un archivo .zip llamado Lights.zip y lo subimos a Amazon Lambda...

Para llamar a este skill en Alexa...podemos hacer lo siguiente...
  • Alexa, turn on lamp 1 (Tomándo en cuenta que la luz se puede llamar de otra manera)
  • Alexa, ask lights for color blue
  • Alexa, turn off lamp 1
Pueden ver los colores disponibles en el código...pero por siacaso deberían ser...
  • Red
  • Green
  • Blue
  • Aqua
  • Pink
  • White
  • Black
  • Yellow
  • Purple
  • Orange

Y por supuesto...aquí está el video  -:D


Saludos,

Blag.
Development Culture.

Wednesday, September 09, 2015

Amazon Alexa y SAP HANA

Este post fué posteado originalmente en Amazon Alexa and SAP HANA.



Si eres empleado de SAP, por favor siguenos en Jam.


Amazon Alexa (Antes conocido como Amazon Echo) es una impresionante tecnología que te permite dar comándos de voz y recibir asombrosas respuestas. Sí…es como Siri pero de Amazon. Además, no vive en tu celular, puesto que Alexa es un cilindro negro cargado con 7 micrófonos y parlamentes muy ruidosos.




Acabamos de recibir uno en el d-shop hace casi una semana…así que por supuesto…necesitaba hackearlo y hacer que funciones con SAP HANA -;)

Primero, necesitamos crear un Calculation View y llamarlo “FLIGHTS_BY_CARRIER”. Estará compuesto por dos tablas, SCARR y SFLIGHT.

Primero, debemos crear un objeto Join y asociar la tabla por MANDT y CARRID. De ahí, seleccionar los siguientes campos como output MANDT, CARRID, CARRNAME, PRICE yCURRENCY.

Luego crear un objeto Aggregation seleccionándo los campos CARRNAME, PRICE (Como columnas agregadas) y CURRENCY. Debemos filtrar el campo CURRENCY por ‘USD’.

Luego, debemos crear un objeto Projection y seleccionar solo PRICE y CARRNAME.

En el objeto Semantics asegúrense de marcar  “CROSS CLIENT” como cliente por defecto.

Ahora, cambiemos a la vista SAP HANA Development y creemos un nuevo repositorio. Lo llamaremos“Flights”.

Creamos un proyecto “XS Engine” y también lo llamamos “Flights”. Lo enlazamos con el repositorio “Flights”.

Creamos un archivo vacio llamado “.xsapp”.

Creamos un archivo llamado “.xsaccess” con el siguente código.

.xsaccess
{
          "exposed" : true,
          "authentication" : [ { "method" : "Basic" } ]
}

Finalmente creamos un archivo llamado “flights.xsodata” con el siguiente código

Flights.xodata
service {
          "Blag/FLIGHTS_BY_CARRIER.calculationview" as "FLIGHTS" keys 
                                                        generate local "Id";
}

Activamos el proyecto y lanzamos nuestro browser...deberíamos ver algo como esto…


La parte de SAP HANA está lista…así que ahora podemos movernos a la parte de Amazon Alexa…

Debo agradecer a este blog Alexa Skills Tutorial: The Definitive Guide to Coding for the Amazon Echo puesto que realmente me ayudo para poder escribir mi propio blog -;)

Para poder programar para Amazon Alexa, podemos usar un nuevo y asombroso servicio de Amazon llamado Amazon Lambda…que basicamente te permite definir un servicio NodeJS que se va a ejecutar solemente cuando lo necesitemos…y también te permite realizar las primeras millón de llamadas gratis por mes…

Primero…logeate en tu cuenta de Amazon Web Services y entra en Amazon Lambda. Asegurate de que estás en la región de  North Virginia…de otra manera no va a funcionar…

Una vez allí…crea una nueva función lambda function y selecciona alexa-skills-kit-color-expert


Luego, selecciona el “Alexa Skills Kit” Event Source Type…


De ahí, necesitamos configurar nuestra función…lo cual significa, debemos darle un nombre…


Deja la parte del código de lado por un momento y escoje un rol…


Escoge Basic execution role y una ventana se abrir automáticamente…


Presiona el botón “Allow it” que está al final. Continúa y finalmente presiona el botón “Create Function”. Un número ARN va a ser generado para tu función, toma nota puesto que va a ser necesario en un próximo paso.

Ahora, debemos ir a http://developer.amazon.com y logearnos.

Escoge Apps & Services --> Alexa --> Alexa Skills Set.


Presiona el botón “Add New Skill” e ingresa un Nombre, Nombre de Invocación (Como vamos a llamarlo desde Alexa), versión y el Endpoint (En este caso, el número ARN de nuestra función Lambda)…


Cuando presionamos "Next", un Application Id number será generado. Toma nota de este número puesto que lo necesitaremos para la parte del código…

La sección de Interaction Model es muy importante puesto que aquí vamos a definir el “Intent Schema” y el “Sample Utterances”…el primero definirá los parámetros que vamos a mandar a Alexa y el segundo es como vamos a llamar a nuestra aplicación.

Intent Schema
{
  "intents": [
    {
      "intent": "GetSAPHANAIntent",
      "slots": [
        {
          "name": "airline",
          "type": "LITERAL"
        }
      ]
    },
    {
      "intent": "HelpIntent",
      "slots": []
    }
  ]
}

Nuestra variable se va a llamar “airline” y va a ser un LITERAL…otros tipos son NUMBER, DATE, TIME y DURATION. El "intent" es el método que vamos a llamar en nuestra aplicación…


Sample Utterances
GetSAPHANAIntent get total amount for airline {American Airlines|airline}
GetSAPHANAIntent get total amount for airline {United Airlines|airline}
GetSAPHANAIntent get total amount for airline {Delta Airlines|airline}
GetSAPHANAIntent get total amount for airline {Lufthansa Airlines|airline}
GetSAPHANAIntent get amount for {Delta Airlines|airline}
GetSAPHANAIntent get amount for {Lufthansa|airline}
GetSAPHANAIntent get amount for {United Airlines|airline}
GetSAPHANAIntent get amount for {American Airlines|airline}
GetSAPHANAIntent get {United Airlines|airline}
GetSAPHANAIntent get {Lufthansa|airline}
GetSAPHANAIntent get {Delta Airlines|airline}
GetSAPHANAIntent get {American Airlines|airline}

HelpIntent help
HelpIntent help me
HelpIntent what can I ask you
HelpIntent get help
HelpIntent to help
HelpIntent to help me
HelpIntent what commands can I ask
HelpIntent what commands can I say
HelpIntent what can I do
HelpIntent what can I use this for
HelpIntent what questions can I ask
HelpIntent what can you do
HelpIntent what do you do
HelpIntent how do I use you
HelpIntent how can I use you
HelpIntent what can you tell me
HelpIntent how do i get an amount

La sección Test es donde podemos probar nuestra aplicación enviándole un utterance como“get American Airlines”…debemos crear el código de nuestra función antes de poder hacer las pruebas…así que no se preocupen mucho por esto por ahora…


No hagan caso de la sección Publishing Information a menos que de verdad quieran publicar su aplicación…

Debemos crear una carpeta y llamarla “Alexa_HANA” o algo parecido…luego debemos crear una carpeta llamada “src”. Copia el código de aquí y crea un archivo llamado “AlexaSkills.js”

Vamos a necesitar instalar el paquete node-rest-client en nuestra función, así que debemos hacer lo siguiente en el terminal

sudo npm install --prefix=~/Alexa_HANA/src node-rest-client

Esto va a crear un folder llamado “node_modules” con el paquete en nuestra carpeta de proyecto…luego debemos crear un archivo llamado “index.js” y copiar y pegar el siguiente código…

index.js
var Client = require('node-rest-client').Client
  , AlexaSkill = require('./AlexaSkill')
  , APP_ID     = 'amzn1.echo-sdk-ams.app.XXX';

var error = function (err, response, body) {
    console.log('ERROR [%s]', err);
};

function toTitleCase(str)
{
    return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

var getJsonFromHANA = function(airline, callback){
 var sAirline = toTitleCase(airline);
 var options_auth={user:"SYSTEM",password:"YourPassword"};
 client = new Client(options_auth);
 client.get("http://YourServer:8000/Flights/flights.xsodata/FLIGHTS?$format=json&$filter=CARRNAME eq \'" + 
                    sAirline + "\'", function(data, response){
  if (data.d.results[0] != undefined){
   var Amount = data.d.results[0].PRICE; 
  }else{
   var Amount = "Sorry I coudln't find that airline"; 
  }
  callback(Amount);
 }).on('error',function(err){
            callback("Sorry there was a connection error");
       });
}

var handleHANARequest = function(intent, session, response){
  getJsonFromHANA(intent.slots.airline.value, function(data){
 var text = data;
    var cardText = 'Total sales are: ' + text;

    var heading = 'Total sales for: ' + intent.slots.airline.value;
    response.tellWithCard(text, heading, cardText);
  });
};

var HANA = function(){
  AlexaSkill.call(this, APP_ID);
};

HANA.prototype = Object.create(AlexaSkill.prototype);
HANA.prototype.constructor = HANA;

HANA.prototype.eventHandlers.onSessionStarted = function(sessionStartedRequest, session){
  console.log("onSessionStarted requestId: " + sessionStartedRequest.requestId
      + ", sessionId: " + session.sessionId);
};

HANA.prototype.eventHandlers.onLaunch = function(launchRequest, session, response){
  // This is when they launch the skill but don't specify what they want.
  var output = 'Welcome to S A P HANA. ' +
    'Say an Airline Name.';

  var reprompt = 'Which Airline would you like?';

  response.ask(output, reprompt);

  console.log("onLaunch requestId: " + launchRequest.requestId
      + ", sessionId: " + session.sessionId);
};

HANA.prototype.intentHandlers = {
  GetSAPHANAIntent: function(intent, session, response){
    handleHANARequest(intent, session, response);
  },

  HelpIntent: function(intent, session, response){
    var speechOutput = 'Get the total amount for any airline. ' +
      'Which Airline would you like?';
    response.ask(speechOutput);
  }
};

exports.handler = function(event, context) {
    var skill = new HANA();
    skill.execute(event, context);
};

Una vez que hemos terminado, debemos comprimir los archivo en un .zip…así que vamos a la carpeta “src” y debemos crear un archivo .zip con la carpeta “node_modules”, el archivo AlexaSkills.js y el archvo index.js…podemos llamar al archivo .zip “SAPHANA.zip”.

Ahora, regresamos a la función de Amazon Lambda y escojemos la pestaña Code. Luego escojemos “Upload a .ZIP file” y presionamos Upload. Escogemos el archivo SAPHANA.zip y luego presionamos el boton "Save".



Con eso…deberíamos estar listo para seguir adelánte -;)

Ahora podemos utilizar la sección Test de nuestro Alexa Skill…



Una vez que sabemos que está funcionando…podemos probar nuestra nueva aplicación en Amazon Alexa diciéndo…

“Alexa ask get hana”

Y luego “get American Airlines” o “get total amount for Delta Airlines”

Aquí hay un video para demostrárles como funciona -:)


Como puesden ver…es en realidad bastante sencillo pero hay algo que deben tener en cuenta…Alexa no va a poder ser capaz de entender todo lo que le digamos…así que debemos ser cuidadosos con nuestros utterances…también…en SAP HANA tenemos “American Airlines” pero Alexa va a entenderlo como “american airlines”…es por eso que tuve que capitalizar la primera letra puesto que de lo contrario, ambos son diferente nombres de línea aerea -;)

Saludos,

Blag.
Development Culture.