24/01/2021

Skill de Alexa II: Conectar nuestra skill con un servicio externo

En el post anterior de esta serie veíamos cómo crear una skill de Alexa sencilla, consistente en invocar nuestra skill y pedirle una cita célebre y que Alexa nos diera una respuesta que en esa primera versión de la skill era estática, una cita que nosotros mismos habíamos incluido en la lógica y que por tanto siempre era la misma. Ese post sirvió para mostrar los pasos para crear la skill desde cero así como qué componentes y elementos debemos manejar dentro del sistema de desarrollo de skills para Alexa por lo que es recomendable echar un vistazo a ese primer post antes de continuar con éste si es que no se tienen los conocimientos básicos del desarrollo de skills.

Así pues en este post, como siguiente paso, vamos a hacer que nuestra skill, en lugar de devolver una respuesta estática, se conecte a un servicio externo de citas célebres mediante una llamada HTTP para obtener esa cita famosa que nos pide el usuario en nuestro ejemplo. De esta forma la respuesta de Alexa será siempre distinta.

Conectando nuestra skill con un servicio externo mediante una llamada HTTP GET

Para conectar nuestra skill con APIs o servicios externos mediante llamadas HTTP necesitamos definir esa comunicación en la parte backend de nuestra skill, es decir, una vez dentro del Developer Console tenemos que acudir a la pestaña Code en la que están definidas nuestras funciones lambda que determinan la lógica y capacidades de nuestra skill. Para nuestro ejemplo vamos a recurrir a una API externa facilitada a través del servicio quotable que nos proporcionará diversos servicios para obtener citas célebres, entre ellos uno de citas célebres aleatorias que se adapta exactamente a nuestras necesidades.

Como ya adelantamos en el planteamiento de la skill vamos a usar, tanto para el asistente de voz como para el resto de servicios externos, el inglés como idioma de desarrollo para facilitar la integración y conseguir un resultado coherente.

1. Conociendo el servicio externo

En nuestro caso, como ya hemos dicho, la API de quotable nos proporciona un endpoint https://api.quotable.io/random mediante el cual podemos obtener citas famosas o célebres de forma aleatoria. Según vemos en la documentación de la API los detalles del servicio son:

  • Service: Get random quote Returns a single random quote from the database

  • Path: GET /random

  • Query parameters:

    • maxLength: Int The maximum Length in characters ( can be combined with minLength )
    • minLength: Int The minimum Length in characters ( can be combined with maxLength )
    • tags: String Filter random quote by tag(s). Takes a list of one or more tag names, separated by a comma (meaning AND) or a pipe (meaning OR). A comma separated list will match quotes that have all of the given tags. While a pipe (|) separated list will match quotes that have either of the provided tags.
  • Response:

      {
      _id: string
      // The quotation text
      content: string
      // The full name of the author
      author: string
      // The length of quote (number of characters)
      length: number
      // An array of tag names for this quote
      tags: [string]
      }
    

En nuestro ejemplo nos vamos a centrar en el servicio sencillo, sin usar query parameters, por tanto haremos una llamada /random que nos va a devolver entre otras cosas un author y un content que es la información que nos puede interesar devolver al usuario.

2. Llamando al servicio externo desde nuestra skill

Si recordamos los pasos de creación de nuestra skill elegimos un backend tipo Node.js, por lo que necesitaremos escribir la llamada HTTP en Javascript para que nuestro backend realice la llamada y procese la respuesta. Para ello vamos a crear un nuevo archivo dentro del proyecto de código de la skill, en nuestro caso lo llamamos getQuote.js pero se le puede dar otro nombre, lo único importante es que tenga extensión .js.

En ese archivo añadimos el código para realizar la llamada al servicio externo:

var https = require('https');

module.exports = function getQuote() {
  return new Promise(((resolve, reject) => {

    var options = {
        host: 'api.quotable.io',
        port: 443,
        path: '/random',
        method: 'GET'
    };

    const request = https.request(options, (response) => {

      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });

    request.end();

  }));

}

Como podemos observar estamos importando el módulo de Node.js llamado https para realizar llamadas http mediante la línea var https = require('https'). Luego creamos una función, que exportamos para que esté accesible desde otros puntos del backend, que se encarga de configurar la llamada al servicio externo anteriormente descrito, configurando el host api.quotable.io y el path /random, especificando que nuestra llamada va a ser de tipo GET. Una vez incluido el código en el nuevo fichero que hemos creado es momento de hacer uso de él desde nuestro index.js, en concreto dentro del Intent que se encarga de devolver la llamada.

Para ello volvemos al fichero index.js y modificamos el código para realizar la llamada que obtiene la cita desde el servicio externo:

  1. Añadimos la referencia al nuevo fichero que contiene la lógica que realiza la llamada y la asignamos a una variable que hemos llamado getQuote de cara a mantener el mismo criterio de nomenclatura:
const getQuote = require('./getQuote.js');
  1. Modificamos el bloque de código del Intent QuoteIntentHandler para sustituir la variable estática que devolvíamos hasta ahora con la llamada externa:
const QuoteIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'QuoteIntent';
    },
    async handle(handlerInput) {

        const response = await getQuote();

        return handlerInput.responseBuilder
            .speak(response.author + " Said: " + response.content)
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};

Como podemos ver simplemente usamos la función getQuote y para la respuesta de nuestra skill, mediante la función speak, construimos el mensaje extrayendo del objeto response que hemos recibido los atributos author, que contiene el nombre del autor, y content que contiene la cita en sí, tal como vimos en la documentación del servicio /random.

Finalmente hacemos Deploy de nuestra nueva funcionalidad y probamos el resultado en la pestaña Test:

Como vemos la respuesta de Alexa, además de ser dinámica basándose en el servicio de citas aleatorias, sigue la estructura de respuesta que hemos definido, mostrando author y content.

Enviando una petición a un servicio externo mediante HTTP POST

Hasta ahora hemos visto cómo hacer peticiones para obtener datos de un servicio externo mediante llamadas GET, que simplemente obtienen datos. Si lo que necesitamos es realizar una llamada POST (o PUT para más detalles sobre los distintos tipos de verbos HTTP consulta esto o esto), que son llamadas que se caracterízan no solo por recibir una respuesta sino por enviar datos en el body de la llamada, lo único que tendremos que hacer será, aparte de cambiar la configuración de la llamada, añadir el contenido de ese body.

A continuación muestro un ejemplo simple de cómo podríamos hacerlo. Imaginemos que vamos a llamar a un servicio al que le enviamos un nombre y un dni:

var https = require('https');

module.exports = function httpPost(name, dni) {
  return new Promise(((resolve, reject) => {

        var bodyContent = JSON.stringify({
            'name': name,
            'dni': dni
        });

    var options = {
        host: '[SOME-HOST]',
        port: 443,
        path: '/[SOME-PATH]',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': bodyContent.length
        }
    };

    const request = https.request(options, (response) => {

      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });

    request.write(bodyContent);
    request.end();

  }));

}

Tal como muestra el ejemplo definimos una variable bodyContent a la que asociamos un JSON con los datos de nombre y dni que recibe la función, en [HOST] pondríamos el host que contiene el servicio y en [PATH] el recurso de la API del servicio que va a recibir nuestra petición, igual que hemos hecho para el ejemplo con la llamada GET. Finalmente la línea request.write(bodyContent) se encargará de añadir nuestro body a la llamada.

En este caso, como hemos creado una función parametrizada la llamada que haremos desde el index.js deberá aportar esos parámetros:

    const response = await httpPost('John Doe', '29292929X');

Conclusión

En este post hemos visto cómo conectar nuestra skill de Alexa con un servicio externo tanto con llamadas GET como POST para aumentar la funcionalidad de nuestra skill y enriquecer las respuestas que Alexa da al usuario. En el siguiente post de la serie veremos cómo ampliar la funcionalidad de nuestra skill permitiendo que ésta sea capaz de memorizar datos que le aportamos por voz mediante el uso de Slots y cómo gestionarlos en el backend de nuestra skill.

Recursos utilizados

16/01/2021

Skill de Alexa I: crear una skill en unos pocos pasos

Desde que en 2014 Amazon lanzó al mercado sus dispositivos Echo con el ya famoso asistente de voz Alexa como anfitrión la empresa habilitó e incentivó la creación de skills para extender las capacidades de dicho asistente, que están al alcance de todo aquel que esté interesado en entrar en el mundo de los asistentes virtuales y para lo que no hacen falta demasiados conocimientos técnicos como iremos viendo a lo largo de este post.

¿Qué es una skill de ALexa?

Una skill es eso, una habilidad, una funcionalidad extra que se le puede añadir al asistente y que debe instalarse e invocarse como si fuera una aplicación de las que usamos a diario en otros dispositivos, pero obviamente con una capacidad de interacción entre el usuario el sistema mucho más limitada al estar basada en la voz.

Entendiendo el modelo

El sistema que plantea Amazon para su asistente de voz está basado en dos partes:

  • reconocimiento de voz: sería considerada la parte fontend del sistema llevada a cabo en local por el dispositivo receptor.
  • procesado de la petición: sería la parte backend ya que depende de un pocesamiento de la petición que se realizaría en remoto mediante las funciones lambda de los Amazon Web Services

Dentro de este sistema el dispositivo físico compatible con Alexa, tipo echo o del tipo que sea, es el dispositivo encargado de captar las instrucciones del usuario (1), aplicar el reconocimiento de voz para "entender" cuál es la intención del usuario y qué proceso debe lanzar (2), ponerse en contacto con la parte backend del sistema para procesar la petición y generar una respuesta (3) que finalmente devolverá al usuario para finalizar el flujo de comunicación.

Así pues el entorno estará formado por una parte hardware conformada por el dispositivo de comunicación y dos partes software (frontend y backend). Entender este punto es importante para poder identificar qué estamos haciendo y por qué cuando desarrollemos más adelante la skill

Entendiendo el flujo de comunicación

El flujo de comunicación por tanto constará de tres fases:

  1. El usuario invoca la skill a través del dispositivo de hardware usando un nombre de invocación que identifica la skill ("Alexa, abre mis tareas").
  2. Una vez invocada la skill Alexa nos pide más información si es necesario ("Soy tu lista de tareas ¿qué quieres hacer?")
  3. El usuario expresa una intención ("Dime qué tareas tengo programadas para hoy")
  4. Mediante la configuración de la parte "fontend" Alexa reconoce qué quiere el usuario (su lista de tareas) e identifica las variables necesarias (solo las tareas de hoy), a continuación envía la petición parametrizada al backend que la procesa (obtiene las tareas para hoy del usuario) y se la devuelve al dispositivo para que emita la respuesta ("Hoy tienes 3 tareas programadas: llamar a X, pedir presupuesto en Y y partido de fútbol en Z").

Dentro de este proceso podemos identificar:

  • Palabra para lanzar el proceso activando la escucha por parte del dispositivo: Alexa en este caso, a este término se le denomina wake word.
  • Nombre para identificar nuestra skill: mis tareas en nuestro ejemplo, a este término se le denomina invocation name.
  • Las distintas declaraciones de intención del usuario: dime qué tareas tengo, a estos elementos se les conoce como intents.
  • Las variables necesarias para procesar la ejecución: dime qué tareas tengo hoy, hoy sería una variable necesaria para filtrar la búsqueda de tareas. A estas variables se las denomina slots. A la hora de identificarlos, como veremos más adelante, será necesario "entrenar" al modelo de identificación de lenguaje aportándole una serie de ejemplos o posibles expresiones.

Teniendo claros estos sencillos conceptos podemos comenzar ya la creación de nuestra skill para Alexa, que como veremos a continuación va plasmando todo lo que hemos estado enumerando en distintos elementos dentro de la plataforma que Amazon nos facilita para el desarrollo de estas skills.

Creando nuestra primera skill para Alexa

0. Si no la tenemos creamos una cuenta de desarrollador

Para tener acceso a la consola de desarrollo de las skills de Alexa necesitaremos darnos de alta primero en el portal de developers de Amazon, que consiste en simplemente crearnos una cuenta de desarrollador para lo cual únicamente necesitaremos un correo electrónico y dar los típicos datos básicos para crear nuestro perfil.

No es necesario dar de alta una tarjeta de crédito para crear la cuenta de desarrollador.

1. Accedemos a nuestro panel de desarrolladores

Una vez hemos hecho login en el portal de developers de Amazon accedemos a nuestro dashboard de desarrollo mediante el link a la Developer Console y una vez ahí accediendo a la sección Alexa Skills Kit. Dentro tendremos acceso, mediante distintas pestañas, al listado de nuestras skills, a nuestros ingresos, información de pagos, hosting y configuración. Por el momento todo esto estará vacío.

2. Creamos una nueva skill

En la pestaña Skills veremos un botón Create Skill que nos dará acceso al asistente para crear nuestra primera skill para Alexa.

Para este ejemplo vamos a crear una skill sencilla que nos devuelva una cita célebre, en este primer post la cita será siempre la misma y en el proximo post de la serie veremos cómo conectar la skill con un servicio externo para obtener cada vez una cita aleatoria distinta. Usaremos tanto para el asistente de voz como para el resto de servicios externos futuros el inglés como idioma de desarrollo para facilitar la integración y conseguir un resultado coherente.

Antes de comenzar a configurar nuestra skill podemos elegir en qué region de la infraestructura de Amazon Web Services queremos que se ubique el procesado en backend de las peticiones de nuestra skill. Para este ejemplo usamos la región EU Ireland. Lo ideal sería usar la región más cercana a la ubicación de los potenciales usuario de la skill.

  • Damos un nombre a nuestra skill, que servirá para identificarla en nuestra lista de skills, solamente, más adelante ya veremos cómo definir el invocation name. En este caso nombramos nuestra skill como RandomQuote.
  • Como ya hemos mencionado usaremos el inglés como idioma por defecto.
  • Usaremos un modelo Custom que como vemos es de las opciones de modelo el que más se ajusta a lo que queremos implementar.
  • El method to host es básicamente el lenguaje de programación que se usará en los procesos AWS Lambda en la parte backend de nuestra skill. En este post usaremos la opción Node.js pero como vemos se puede usar también Python o incluso nuestros propios recursos backend.

Una vez completadas las opciones pulsamos el botón Create Skill que hay arriba a la derecha.

A continuación podemos elegir aplicar una template para partir de una base ya pre-configurada o podríamos también importar una skill que hubiéramos exportado previamente. En nuestro caso dejamos la opción por defecto start from scratch para crear nuestra skill con la base mínima requerida y poder añadir manualmente los elementos que necesitamos. Finalmente pulsamos el botón Continue with template.

El sistema nos pedirá que resolvamos un sencillo captcha y a continuación comenzará el proceso de creación de nuestra template. Pasados unos segundos si todo ha ido bien tendremos acceso a la consola de desarrollo de nuestra primera skill.

3. Identificando las partes

Como ya hemos mencionado una skill de Alexa está formada por una parte frontend encargada de la interacción con el usuario y el reconocimiento del lenguaje y una parte backend encargada del proceso lógico de la skill. En la pantalla podemos situar cada una de estas dos partes, siendo la pestaña Build y sus elementos de la izquierda (Invocation, Interaction Model y Assets) los que representan los recursos frontend, mientras que la pestaña Code es en la que definiremos los procesos backend, en nuestro caso mediante Javascript al haber elegido el method to host tipo Node.js.

4. Definir el Invocation Name

Lo primero que debemos hacer será definir un invocation name para que el dispositivo receptor de nuestra voz sepa identificar cuándo queremos acceder a nuestra skill. Como se trata de un elemento front necesitaremos situarnos en la pestaña Build que es la que engloba dichos recursos y concretamente iremos a la sección Invocation. Por defecto vemos que el Invocation Name está definido como "change me" en una clara invitación a que personalicemos este parámetro. Elegimos para este caso "famous quote", como veremos hay ciertos requisitos que nuestro Invocation Name deberá cumplir, como un rango de longitud y el estar formado por al menos dos palabras.

Los Invocation Name solo es necesario en los casos de custom skill como el nuestro y no son identificadores únicos de una skill, por lo que podremos elegir cualquier valor que cumpla con los requisitos. Los Invocation Name pueden cambiarse en cualquier momento del desarrollo hasta que la skill pasa por el proceso final de certificación y publicación.

Una vez configurado el invocation name lanzamos el proceso Build Model para que los cambios en la skill tengan efecto.

5. Testear el Invocation Name

Antes de continuar vamos a comprobar que el invocation name se ha configurado correctamente. Vamos a la pestaña Test y elegimos Development como opción en el desplegable Skill testing is enabled in.

Si hemos dado permisos al navegador para usar nuestro micro podremos dirigirnos directamente a la versión test de Alexa mediante nuestra voz, si no podemos usar el cuadro de diálog para solicitar que abra nuestra skill escribiendo y enviando el mensaje "open famous quote". Si el invocation name está bien configurado nuestra skill se lanzará y Alexa nos devolverá una respuesta por defecto, en cualquier otro caso nos responderá con una respuesta de error.

6,. Cambiar la respuesta por defecto

Ahora que ya hemos visto el primer elemento frontend de la Developer Console vamos a entrar en contacto con el backend para cambiar esa respuesta por defecto y sustituirla por una personalizada para nuestra skill. En la pestaña Code podremos ver los distintos bloques de código contenidos en el fichero index.js. Debemos centrarnos en el primero de ellos, el que define una constante llamada LaunchRequestHandler, ese bloque de código son las instrucciones que sigue la función Lambda remota de AWS, que se ejecuta una vez invocada la skill mediante el invocation name, en algún servidor de Irlanda en nuestro caso.

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speakOutput = 'Welcome, you can say Hello or Help. Which would you like to try?';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};

Como podemos ver el mensaje speakOutput, insertado tanto en la función de respuesta speak (para que Alexa lo pronuncie), como en la reprompt (para que Alexa quede a la espera de interacción sin abandonar la el contexto de la skill), corresponde a la respuesta que hemos recibido en el test rápido realizado en el punto anterior.

Simplemente cambiamos el mensaje a algo tipo Welcome to famous quote skill. What do you want me to do?

Y finalmente guardamos cambios y desplegamos el nuevo backend mediante los botones Save y Deploy.

Como podemos ver el frontend y el backend de la skill tienen procesos distintos y separados para el Deployment. En la parte front podemos construir el modelo para aplicar los cambios mientras que en el caso del back debemos accionar el despliegue.

Bajo el botón Deploy tendremos siempre la referencia de cuándo se ha desplegado la última vez.

Si ahora repetimos el test que hemos hecho antes deberemos recibir como respuesta el nuevo mensaje personalizado en lugar del mensaje por defecto.

7. Configurar un nuevo Intent

A continuación vamos a cubrir el último elemento de este post de introducción a la creación de skills para Alexa. Se trata de los Intent que como ya hemos mencionado son elementos que sirven para que Alexa entienda qué intención tiene el usuario, qué es lo que quiere hacer. En el caso de nuestra skill, aunque un tanto obvio, el usuario tras invocar la skill querrá que esta le diga la cita célebre o famosa, por lo que expresará esa intención mediante algún mensaje tipo "quiero que me digas una cita célebre" o "dime una cita famosa". Para capturar, y que Alexa "entienda" dicha petición necesitamos definir un Intent específico.

Para crear un nuevo Intent, al tratarse de un elemento de la parte frontend debemos volver a la pestaña Build y seleccionar el apartado Interaction Model > Intents en el panel de la izquierda. Veremos el listado de los intents que trae por defecto una skill con template del tipo start from scratch como es la nuestra y tendremos un botón Add Intent para crear nuevos intents personalizados.

A continuación veremos que podemos elegir entre una lista de intents predeterminados o crear uno personalizado. Elegimos Create custom intent y le damos un nombre, QuoteIntent por ejemplo, para definir que la petición del usuario está relacionada con la obtención de una nueva cita. Finalmente presionamos Create custom intent.

Ahora habremos sido redirigidos a la página de configuración del nuevo intent en la que lo primero que tendremos que hacer será "entrenar" a nuestra skill para que sepa ante qué tipo de mensajes del usuario queremos que lance el proceso relacionado con este nuevo intent. Para ello tendremos que añadir una serie de entradas en la sección Sample Utterances (muestra de declaraciones) que servirán para "mapear" los posibles mensajes del usuario. En nuestro caso, y en inglés, añadiremos las distintas variantes esperables para la obtención de una cita célebre:

  • "give me a famous quote"
  • "tell me a famous quote"
  • "say a quote"
  • "tell me a quote"
  • "give me a random quote"

Y así sucesivamente, cuantas más utterances aportemos más "inteligente" será Alexa a la hora de "comprender" al usuario.

Finalmente, y como siempre tras un cambio en la parte "frontend" de la skill lanzaremos la construcción del nuevo modelo mediante el botón Build Model.

8. Configurar una respuesta para el nuevo Intent

Finalmente como último paso necesitamos que Alexa tenga una respuesta asociada a este nuevo intent y que se ejecutará en el backend de la skill por lo que necesitamos volver a la pestaña Code.

Una vez allí necesitaremos crear un nuevo bloque de código para generar la función que se ejecutará cuando Alexa envíe la invocación del intent. A continuación del LaunchRequestHandler añadiremos el siguiente bloque de código:

const QuoteIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'QuoteIntent';
    },
    handle(handlerInput) {
        const speakOutput = 'Knowledge is of no value unless you put it into practice.';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};

Mediante este código estamos creando una función lambda que en caso de que el request proveniente de la skill sea identificado como el QuoteIntent lanzará la lógica del handler, que en este caso devuelve simplemente una cita cualquiera y estática, siempre va a devolver la misma. Como vemos al función reprompt en el responseBuilder está comentada por lo que con esta ejecución, una vez devuelta la respuesta al usuario, se dará por cerrada la "sesión" de nuestra skill y en caso de querer volver a obtener la cita tendremos que volver a invocar la skill mediante su invocation name.

Antes de desplegar el nuevo backend tenemos que registrar el nuevo intent handler en la lista de exports al final del fichero index.js

Finalmente guardamos y desplegamos la nueva funcionalidad del backend como hemos hecho antes mediante los botones Save y Deploy.

Si ahora volvemos a testear nuestra skill veremos como, una vez invocada, podemos hacer uso del nuevo intent para que Alexa nos devuelva la cita célebre que le hemos definido.

Destacar como el mensaje que le hemos enviado no coincide exactamente con ninguna de las utterances que definimos para el intent pero el proceso de "Build Model" se encarga de procesar las "pistas" que le hemos dado para "entrenar" al modelo basado en eso pero dotándolo de mayor flexibilidad.

Conclusión

En este post hemos visto una sencilla introducción a la creación de skills para Alexa, hemos visto el modelo básico de funcionamiento, las fases del flujo de comunicación y hemos repasado los distintos elementos implicados en el funcionamiento de una skill básica.

En el siguiente post de la serie veremos cómo conectar la skill que hemos creado para que en lugar de devolver siempre la misa cita sea capaz de conectarse a una API externa, obtener citas aleatorias y hacérselas llegar al usuario.

Recursos utilizados:

10/01/2021

Enlazar una bombilla inteligente TRADFRI de IKEA con Alexa

IKEA se unió a la tendencia de domotizar nuestros hogares algo más tarde de lo que podría esperarse de una empresa basada en eso, en el hogar, pero finalmente, y casi coincidiendo con la popularización de los asistentes de voz, lanzó al mercado su ecosistema de productos domóticos TRÅDFRI cuyos puntos fuertes fueron unos precios contenidos y, especialmente, la compatibilidad con otras plataformas.

En este post vamos a hablar de cómo enlazar las bombillas TRÅDFRI de IKEA a un ecosistema Alexa basado en un echo plus de segunda generación, que es un dispositivo que incluye un hub domótico que nos permite gestionar los distintos dispositivos compatibles.

> No todos los dispositivos echo de Amazon incluyen este hub, así que no todos pueden ser usados como concentradores domóticos.

Para enlazar una nueva bombilla a nuestro ecosistema domótico vamos a necesitar simplemente la app Amazon Alexa y completar unos sencillos pasos.

Amazon Alexa en iOS

Amazon Alexa en Android

Coloca la bombilla en su lámpara.

Lo primero obviamente es tener la bombilla colocada en su lámpara de manera que ésta tenga conexión con la red eléctrica y le permita así emparejarse con el nuestro sistema domótico.

Abre la app Amazon Alexa

La app de Amazon para Alexa nos permite controlar todo lo relacionado con el asistente de voz, entre esas funcionalidades podemos encontrar la opción de añadir nuevos dispositivos domóticos. Para ello, una vez abierta la app, iremos a la sección Dispositivos y presionaremos la opción de añadir un nuevo dispositivo que se encuentra con el símbolo + en la zona superior derecha.



A continuación en el cuadro de diálogo seleccionaremos la opción Añadir Dispositivo.



El siguiente paso consistirá en indicar qué tipo de dispositivo queremos enlazar, en nuestro caso una bombilla.



Ahora tendremos que indicar la marca del dispositivo, en nuestro caso IKEA.



> En caso de que este proceso no funcionara podemos probar también eligiendo como marca la opción "Otra" que hay como última de las opciones de la lista de marcas.


En la siguiente pantalla nos da unas breves indicaciones de cómo debemos proceder:

- Poner la bombilla en modo emparejamiento mediante el encendido y apagado repetido de la lámpara en la que está colocada la bombilla. 

- Presionar el botón Descubir Dispositivos que aparece en la zona inferior.

Como vemos las instrucciones no aportan detalles concretos de cuántas veces hay que repetir la operación de encendido y apagado por lo que, al menos en mi caso, lo que ha funcionado ha sido hacer lo siguiente:

  1. Damos al botón Descubrir Dispositivos.
  2. Cuando nos aparezca la pantalla de búsqueda, en la que vemos que Alexa está activamente buscando nuevos dispositivos encendemos y apagamos la bombilla 7 veces de manera rápida, dejando la séptima vez la bombilla encendida.

Si todo va bien, en algún punto del proceso tras los 7 encendidos, la bombilla bajará automáticamente de intensidad la luz y volverá a la intensidad normal para indicar que se ha realizado el emparejamiento (en otras bombillas puede que el indicador sea un parpadeo). Una vez finalizado el proceso de búsqueda la app confirmará en pantalla que ha encontrado un nuevo dispositivo y nos dará la opción de añadirlo a un grupo y darle nombre para identificarlo dentro de nuestra red.

Si algo no ha ido bien veremos una pantalla con opciones en la que podremos repetir el proceso (es bastante habitual que el proceso de descubrimiento de dispositivos falle alguna vez).



Y eso es todo, siguiendo estos sencillos pasos ya tendremos nuestra nueva bombilla lista para ser controlada por voz o mediante rutinas a través de Alexa.

04/01/2021

Reduciendo el peso de un PDF con macOS

Más de una vez me he encontrado en la necesidad de subir PDFs a mis servicios en la nube o de enviarlos por email y éstos no siempre vienen comprimidos de la mejor forma y ocupan bastante más espacio del estrictamente necesário.

Si eres usuario de Mac macOS nos ofrece la opción de reducir considerablemente el peso de un PDF sin necesidad de descargar una app específica ya que nos ofrece la opción de reducir el peso del archivo directamente desde la misma app nativa *Preview* desde la que podemos visualizarlo.

Simplemente tendremos que elegir *File > Export...* y en las opciones de *Quarz Filter* escoger *Reduce File Size*.




Eso sí, tendremos que tener en cuenta que la compresión va a afectar a la calidad de las imágenes incluidas en el PDF, por lo tanto si la calidad de éstas tiene un peso importante en el documento debemos tenerlo en cuenta para buscar una alternativa que nos permita más opciones de control sobre la compresión.

Comparación de la definición de una imágen antes y después de la reducción de tamaño del archivo

Ahora bien, si la calidad de las imágenes no es vital podemos reducir el peso del archivo considerablemente consiguiendo ahorrar mucho espacio sin perder nada de la información que el PDF original contenía.



05/02/2017

Telegram no aparece entre las Aplicaciones de Antergos

Tras 24h. en Antergos me dispongo a instalar Telegram, una de esas apps "básicas". Parece que el paquete a instalar es el que aparece en AUR como telegram-desktop pero da "error de compilación" (después de estar unos 15 minutos descargando e instalando "cosas" o.O).

Compruebo en foros que no soy el único con el mismo problema, así que descargo la app directamente del sitio oficial de Telegram. Tras la descarga obtengo esto (un ejecutable "Telegram" y un ejecutable para hacer update)


Pero, aunque lanzando el ejecutable "Telegram" la app se ejecuta correctamente, no aparece entre las apps disponibles de Antergos y, aunque la ancle como app favorita al dock, al reiniciar ha desaparecido y tengo que volver a buscar el ejecutable si quiero lanzar la applicación.

De igual forma la app no aparece en la lista de "Startup applications" del Tweak Tool de Gnome así que no puedo automatizar que se lance en el arranque del sistema.

Después de jugar sin éxito con "systemctl" de Arch y los "services" asociados, lo planteo de otra forma.

1- Copio el ejecutable "Telegram" a la carpeta "usr/bin/", donde están el resto de apps del sistema (más por orden y coherencia que por otra cosa).

2- Descargo un logo para la app de Telegram. y lo guardo en "/usr/share/icons/"

3- En "/usr/share/applications" creo un archivo "telegram.desktop" y le añado el siguiente contenido

[Desktop Entry]
Name=Telegram
Comment=Telegram      
Exec=/usr/bin/Telegram  
Icon=/usr/share/icons/Telegram_logo.png
Terminal=false
Type=Application
Keywords=Telegram


Donde "Exec" lo apunto al ejecutable que he copiado en la carpeta "usr/bin" e "Icon" lo apunto al logo que me he descargado para la app.

4- Tras guardar los cambios en el archivo "telegram.dektop" la applicación ya aparece en el hub de apps de Antergos.


Una vez reconocida como app ya podemos ejecutarla y anclarla como favorita en el dock. Y...

5- Con "Tweak Tool" podemos seleccionarla para que se lance al arrancar el sistema.


Listo, ya tenemos Telegram disponible y accesible como cualquier otra applicación más.

21/08/2013

Lanzar una aplicación Java desde icono en Ubuntu Linux

Una vez hemos conseguido que nuestra aplicación Java corra en nuestro equipo con Linux (Ubuntu 12.04 LTS), tal como se explica en la entrada anterior del blog, el paso final lógico será el ahorrarnos tener que lanzarla desde el Terminal pudiendo hacerlo desde un icono como el resto de aplicaciones del sistema, icono que además podemos personalizar elaborándolo nosotros. Vamos a ver cómo debemos proceder para ello.

Primero diseñamos un icono, yo lo he hecho desde Gimp pero puede usarse cualquier software, o bien usar una imagen que ya tengamos lista. A modo de ejemplo he puesto simplemente un fondo de color y las iniciales de la aplicación. Un diseño bastante mejorable pero insisto en que es solo un ejemplo rápido.

Creamos el icono


Una vez lo tengamos listo lo guardamos en formato .png ó .xpm o en cualquier formato de icono que sea compatible. Yo lo he hecho en PNG.

guardamos el icono en formato png


Con el icono y el archivo ejecutable .jar localizados abrimos el terminal y escribimos:

sudo gedit /usr/share/applications/nombre-de-nuestra-aplicación.desktop

Con este comando lo que hacemos es, con acceso root, ir a la carpeta de aplicaciones del sistema y crear un archivo .desktop para que Linux sepa que hay una nueva aplicación gráficamente accesible.
Se nos abrirá un archivo de texto vacío .desktop en el que escribiremos lo siguiente:

[Desktop Entry]
Name=nombre de nuestra aplicación
Comment=una descripción corta que puede ser el mismo nombre de la aplicación
Exec=java -jar ruta-del-archivo-ejecutable.jar
Icon=ruta-del-icono.png
Terminal=false
Type=Application


creamos el archivo .desktop


Una vez escrito guardamos el documento. Cerramos los programas y reiniciamos el sistema. Vamos al launcher de aplicaciones y buscamos nuestra aplicación y si todo ha ido bien ahí debe aparecer:

Launcher de aplicaciones de Ubuntu con el icono de nuestra aplicación


Simplemente haciendo click en el icono la aplicación se ejecutará.

aplicación Java corriendo en Ubuntu


Si además queremos fijar el icono en el launcher únicamente tenemos que, mientras se ejecuta la aplicación, dar al botón derecho del ratón en el icono de la aplicación en el launcher y elegir “Lock to Launcher”

Fijar icono de nuestra aplicación al launcher

19/08/2013

Java Básico: Convertir en ejecutable una aplicación Java con NetBeans en Ubuntu Linux

Una vez que hemos terminado el código de nuestro proyecto en NetBeans lo lógico es que queramos que esa aplicación funcione “fuera” del IDE y pueda ser usada en cualquier otro equipo. Para ello debemos compilar el proyecto y generar un archivo .jar que será el que ejecutaremos para hacer funcionar nuestra aplicación.

Voy a mostrar paso por paso cómo he conseguido hacerlo en la distribución de Linux Ubuntu 12.04 LTS:

En nuestro proyecto en NetBeans damos al icono de la escoba y el martillo, si nos fijamos, en el panel de la izquierda, pestaña “files” habrá aparecido una nueva carpeta en el proyecto denominada “dist”. En su interior NetBeans habrá creado el archivo .jar de nuestro proyecto:

Compilando con NetBeans
Compilamos el proyecto mediante el icono del martillo y la escoba ("Clean and Build Project")

Accediendo al .jar en NetBeans
En el panel de la izquierda, en la pestaña Files, aparece la nueva carpeta "dist" con el archivo ejecutable .jar dento


Ahora ese archivo .jar, que podemos cambiar de ubicación si queremos, deberemos ejecutarlo desde el Terminal mediante el comando:

java -jar “ruta-completa-del-archivo.jar"

Para obtener esa ruta completa lo que yo hago es arrastrar el icono del archivo .jar a la ventana del Terminal y automáticamente se crea la ruta completa a ese archivo.

Creando la ruta de un archivo en el Terminal mediante drag and drop


Creando la ruta completa de un archivo en el Terminal mediante drag and drop


Una vez hecho esto lo único que hay que hacer el completar el comando con “java -jar “ delante de la ruta.

comando para ejecutar un .jar en Ubuntu Linux


Dando Enter, y si todo está correcto, la aplicación deberá ejecutarse.

Posibles problemas en la ejecución: UnsupportedClassVersionError

Debemos prestar atención especialmente al JDK que hemos usado para la compilación del proyecto y a la máquina virtual que usa nuestro equipo para ejecutar las aplicaciones Java. Si estamos portando la aplicación desarrollada en nuestro equipo personal a otro distinto debemos comprobar previamente que tiene instalada una máquina virtual de Java y si no la tiene descargarla e instalársela, y que las versiones de compilación y ejecución coincidan.

En mi caso mi NetBeans compilaba con un JRE superior a la versión que luego usaba el sistema, lo cual me daba un error de tipo:

Exception in thread "main" java.lang.UnsupportedClassVersionError: ruta/del/paquete/de/mi/aplicación : Unsupported major.minor version 51.0

Para solucionarlo hay que cambiar la versión de la máquina virtual de Java que usa nuestro sistema, lo cual haremos mediante el comando:

sudo update-alternatives --config java

Nos aparecerá un listado con las versiones de las máquinas virtuales que están instaladas en nuestro sistema y elegiremos la que nos convenga.

Elección de máquina virtual JVM JRE en Ubuntu


En mi caso, al usar el JDK7 debía elegir la java-7-openjdk, que correspondía al número 2. Por tanto 2+Enter y solucionado.


Otra opción podría ser aplicar la táctica inversa, es decir, variar la versión de la máquina virtual de Java que usa NetBeans previamente a la compilación del .jar para que coincida con la del sistema.

Referencias