obnizとAlexaの連携 (Skill編)-Alexaに部屋の温度を教えてもらう-

今回やること

前回の、obnizとAlexaの連携(IFTTT編)では、IFTTTを使用したobnizとAlexaの簡単な連携を紹介しました。 今回は、Skillを作成し、より柔軟な機能拡張を実現します。

温度センサーLM35DZを用いて、Alexaに部屋の温度を教えてもらうSkillを作ります。

※ Lambda関数を使用します。

Alexaに部屋の温度を教えてもらう

Skill(スキル)を使えば、特定の質問に対する回答を探したり、デバイスを制御するなどAlexaの機能をカスタマイズすることができます。

IFTTT編のときは、obnizからのセンサ値によってAlexaの話す言葉を変えることはできませんでしたが、Skillによって、センサ値に応じた回答をしてくれるようになります。

完成したSkillの全体の流れとしては、以下の通りです。

  1. Alexaに「アレクサ、温度計を開いて」と話しかける
  2. Alexaが「この部屋の温度を教えて、と話しかけると温度を返します」と答える
  3. Alexaに「この部屋の温度を教えて」と話しかける
  4. Alexaが部屋の温度を教えてくれる

用意するもの

  • obniz Board
  • Alexa搭載のスマートスピーカー(Amazon Echo)
  • 電源(モバイルバッテリーなど)
  • 温度センサー(この記事内ではLM35DZを使用)
  • amazon alexaのアカウント
  • AWSのアカウント

仕組み概要

この流れを実現するためには、obnizから温度センサーの値を読む ①Lambda関数の作成 と、そのLambda関数を呼び出すための ②Alexa Skillの作成 が必要です。

Alexaに話しかけてSkillを開くと、作成したLambda関数が実行される仕組みです。Lambda関数のコードの中に、Skill中に話しかけた言葉に対する応答や温度センサーの値読み出しを盛り込みます。

組み立て方

下の表や図のようにobniz Boardに温度センサーを配線します。このとき温度センサーを挿す向きに注意してください。 平らな面 が上です。

obniz 温度センサー LM35DZ
9 GND
10 OUTPUT
11 Vcc

プログラム

Skillを開いた時に実行されるLambda関数用のプログラムをかきます。

完成したプログラムを参考にしてください。

let obniz = new Obniz('OBNIZ_ID_HERE');

 の OBNIZ_ID_HERE をお手持ちのobnizのIDにかきかえてください。また、 ask-sdk-coreask-sdk-model 、そして obniz のパッケージをインストールしておいてください。

いくつかある ●●●Handler のかっこの中で、話しかけた言葉やエラーに対する動きを定義しています。

例えば、 LaunchRequestHandler はSkillが呼び出されたときに、 GetTemperatureIntentHandler はこの後作るSkillで登録した言葉を話しかけたときに実行されます。特に、後者のハンドラーはこちらで定義したもので、ハンドラーの名前は任意です。

 

let temper = await obniz.wired('LM35DZ', {gnd:9, output:10, vcc:11});

この部分でobnizの9〜11番を温度センサーのGND・OUTPUT・Vccに割り当てています。タイムアウトの関係で、「この部屋の温度を教えて」と話しかけたときではなく、Skillを呼んだ時点で温度の測定をしてしまっています。

Lambda関数を作る

Lambda関数用のプログラムを書いたところで、Lambda関数を作ります。AWS公式からコンソールにサインインし、Lambdaを開いてください。

右上の 関数を作成 をクリックします。

設定画面に移るので、オプションから 一から作成 を選択し、関数名を getRoomTemperature 、ランタイムを Node.js 10.x にして 関数の作成 をクリックします。

次に、コードをアップロードします。先ほどかいたプログラムに index.js と名前をつけて保存し、 node_modules フォルダと共にzipファイルにしてください。(ファイル名は何でも良いです。)index.jsとnode_modulesの2つが入ったフォルダごとではなく、index.jsとnode_modulesフォルダ自体の圧縮ですので注意しましょう。

圧縮できたら、コードエントリタイプを .zipファイルをアップロード にし、 アップロード ボタンからアップロードしてください。

ファイルサイズが大きくてアップロードできないときは、コードエントリタイプを Amazon S3からのファイルのアップロード にし、Amazon S3にzipファイルをアップロードしてから、ファイルのURLを貼り付けてください。

コードをアップロードできたら、スクロールして基本設定の欄から タイムアウト を伸ばします。obnizで温度計の値を読み出すのに時間がかかると考えたので、ここでは 20秒 と設定しました。

完了したら右上の 保存 ボタンを押すのを忘れないようにしましょう。

ここで一旦Alexa Skill作りに移ります。この後もまたLambdaの画面に戻りますので、開いたままにしておいてください。

 Alexa Skillを作る

Lambda関数を呼び出すためのAlexa Skillを作ります。何度か先ほどまでのLambdaの画面と行き来することになるので注意してください。

alexa developper consoleにログインし、 Skillの作成 をクリックします。

すると以下の画面に移るので、Skill名を RoomTemperature 、Skillに追加するモデルを カスタム にします。

続いてスクロールし、ユーザー定義のプロビジョニング を選択して、 Skillの作成 をクリックします。

これでSkillが新しく作られたので、まず呼び出し名を設定します。ここでは 温度計 と設定しました。

呼び出し名を入力したら、必ず モデルを保存 し、ビルド項目の画面に戻りましょう。呼び出し名のところにチェックが入っているので、次に インテント・サンプル・スロット をクリックします。(インテントとは、話しかけた言葉に対して行われるアクションのことです。)

カスタムインテントを追加します。Skillが呼び出されたときに実行されるものや、ヘルプに対するものはもう既にビルドインテントとして用意されています。

入力ボックス内が映り切っていませんが、 GetTemperatureIntent と入力しています。

カスタムインテントを作成 をクリックすると、サンプル発話を入力するように求められます。ここに、Skillを呼び出した後、何と話しかけたら作成したインテントを実行するのかを、複数パターン入力します。

再び モデルを保存 を押してビルド項目に戻り、次の項目の モデルをビルド をクリックします。ビルドが始まるので、以下の画像のようにビルドが完了するまで少し待ちます。

ビルドが完了したら、最後の項目であるエンドポイントを設定するのですが、その前に一旦先ほど開いたままにしておいたLambdaの画面に戻ります。

Lambdaの画面に戻る(1)

一つ前の章で作ったLambda関数を開くと、以下のような画面になっていると思います。ここで、 右上のARNという文字の横にある 「ここをコピー」 という部分をコピーします。

コピーできたら、再びalexa developper consoleに戻ります。

alexa developper consoleに戻る

alexa developper consoleに戻ったら、最後のビルド項目である エンドポイント をクリックします。

すると以下の画面になるので、①先ほどLambdaの画面に戻ってコピーしたものを、 デフォルトの地域 の部分に貼り付けます。次に、② スキルID の部分をコピーしておきます。

コピーができたら忘れずに エンドポイントを保存 をクリックしてください。この後、再びLambdaの画面に戻ります。

Lambdaの画面に戻る(2)

再びLambdaの画面に戻り、作ったSkillとLambda関数を連携させます。 トリガーを追加 をクリックしてください。

検索ウィンドウが出るので Alexa Skill Kit を選択してください。すると以下のような画面になるので、 スキルIDの検証 を 有効 にし、 スキルID の部分に、先ほどコピーしたスキルIDを貼り付けます。

最後に 追加 をクリックすれば、全て完成です。早速テストしてみましょう。

テストする

実際にAlexaに話しかけることによってきちんと動くかどうか確かめることもできますが、alexa developper consoleでは、PCへのキーボード入力や音声入力によって動作検証できるテスト機能が用意されています。実際に使ってみましょう。

実際にテストをする前に、温度センサーを挿したobnizに電源を供給するのを忘れないうようにしてください。

alexa developper consoleの上部のバーの テスト をクリックします。すると以下のような画面が開きます。(スキルテストが無効になっている場合は、 開発中 に変更してください。)

「アレクサ、温度計を開いて」と打ってSkillを開くと、うまく動作すれば「この部屋の温度を教えて、と話しかけると温度を返します。」と返事してくれます。

また、Skillを開いた後に「この部屋の温度を教えて」と打つと、温度センサーで計測した部屋の温度を返してくれます。

テストがうまくいったら、実際にAlexaに話しかけてみましょう! より詳しい説明はamazon alexa公式のAlexa Skill Kitによるスキル作成のページや、obnizのobniz with AWS Lambdaのページを参考にしてください。

 

完成したプログラム

const Alexa = require("ask-sdk-core"),
      Obniz = require("obniz"),
      fs = require("fs");

let connected = false;
let temperVal = null;
let skill;

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'この部屋の温度を教えて、と話しかけると温度を返します。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('温度計', speechText)
      .getResponse();
  }
};

let GetTemperatureIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'GetTemperatureIntent';
  },
  handle(handlerInput) {
    let speechText;
    
    if(temperVal === null){
      speechText = '計測に失敗しました';
    }else{
      speechText = '約' + temperVal + '度です';
    }

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('温度計', speechText)
      .getResponse();
  }
};

const HelpIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    const speechText = 'obnizと温度センサLM35DZを必要とします。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('温度計', 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 = 'さようなら';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('温度計', speechText)
      .getResponse();
  }
};

const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
  },
  handle(handlerInput) {
    
    obniz.close();
    
    return handlerInput.responseBuilder.getResponse();
  }
};

const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`処理されたエラー: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('すみません。コマンドを理解できませんでした。もう一度お願いします。')
      .reprompt('すみません。コマンドを理解できませんでした。もう一度お願いします。')
      .getResponse();
  },
};

exports.handler = async function (event, context) {
  console.log(`REQUEST++++${JSON.stringify(event)}`);
  
  let obniz = new Obniz('OBNIZ_ID_HERE');
  
  connected = await obniz.connectWait({timeout:10});
  
  if(connected){
    let temper = await obniz.wired('LM35DZ', {gnd:9, output:10, vcc:11});
    temperVal = Math.round(await temper.getWait());
    console.log("温度:" + temperVal);
  }
  
  await obniz.wait(300);
  
  if (!skill) {
    skill = await Alexa.SkillBuilders.custom()
      .addRequestHandlers(
        LaunchRequestHandler,
        GetTemperatureIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
      )
      .addErrorHandlers(ErrorHandler)
      .create();
  }
  
  const response = await skill.invoke(event, context);
  await console.log(`RESPONSE++++${JSON.stringify(response)}`);
  await obniz.wait(200);
  await obniz.close();

  return response;
};

応用編

前の章ではAlexaに部屋の温度を尋ねることによって、Alexaが温度センサーの値に合わせた返事をしてくれるものを作りましたが、これを様々なものに応用させることができます。

醤油がまだ残っているかをAlexaに教えてもらう

例えば、obnizに挿すセンサーを圧力センサーに変えることで、醤油がまだ残っているかをAlexaに教えてもらうSkillを作ることもできます。出発前に玄関で「醤油まだ残っていたっけ?今日買って帰った方が良いのかな?」と疑問に思ったとき、わざわざ靴を脱いで確認しに行かなくても、アレクサが答えてくれれば楽ですね。

お茶を醤油に見立てて簡易的に作ってみました。(音が出ます)

おわりに

このように、Skillを作成することでobnizからのセンサ値によってアレクサからの応答を変化させることができます。IFTTTを使ったときもより柔軟にobnizとAlexaを連携させることができましたね。

手軽に、または出力側だけにobnizを使うときはIFTTTを、入力側にobnizを使う、またはAlexaに何かを喋らせたいときはSkillを作成すると良いと思います。