はじめに
Alexaスキルを音声以外の方法で起動させたかったのが発端。
2019年10月末、ようやく定型アクションでスキルが起動できるようになった。さらにEcho Flex(※今は取扱い停止。) も発売され、モーションセンサー(別売)を使うことで定型アクションのトリガーとして使えるようになった。であればスマートホームスキルのセンサー用のスキルを作れば定型アクションのトリガーとして使えるのではないか?と考えセンサー用のスマートホームスキルを作ろうと思ったのがキッカケ。
※第四世代のEcho Dot も人感センサーに似た、在室感知という機能が追加されていて、こちらも定型アクションのトリガーとして使用可能となっている。反応はセンサーの方が良いように思う。
前提
- カスタムスキルを作ったことがある
- Alexaスキルモデル:スマートホーム(センサー用)
- OAuthプロバイダ:Login with Amazon(LWA)
スキル作成の流れ
スマートホームスキルを作成
Alexa Developer Consoleで、センサー用のスマートホームスキルを新規作成する。
1. Alexa Developer Consoleにログインする
2. スキルを作成する
スキルの作成
ボタンをクリックし、必要な情報を入力後スキル作成
ボタンをクリックする。
センサー用スマートホームスキルは、デフォルトの言語は英語(米国)を選択する。
No. | 項目 | 値 |
---|---|---|
1 | スキル名 | 任意(制限事項はマニュアル参照) |
2 | デフォルトの言語 | 英語(米国) |
3 | スキルに追加するモデルを選択 | スマートホーム |
※種類が豊富!
3. スキルの情報を入力する
ペイロードのバージョン
でv3(推奨)
を選択したら、いったん保存
ボタンをクリックしてスキルを保存しておく。
LWAのセキュリティプロファイルやLambda関数を作成した後に、スマートホームスキルのエンドポイントとアカウントリンクを設定する。
Lambda関数作成時にスキルIDが必要になるので、スキルID
をメモ帳などに保管しておく。
※Alexa Developer Console開けば確認できますが・・・
No. | 項目 | 値 |
---|---|---|
1 | ペイロードのバージョン | v3(推奨) |
LWAでセキュリティプロファイルを作成
アカウントリンクに必要なクライアントIDとクライアントシークレットを作成する。
1. Amazon開発者ポータルにログインする
画面のデザインが変わってる。2020年4月時点のAmazon開発者ポータル画面。
2. セキュリティプロファイルを作成する
上部のメニューからLogin with Amazon
をクリックしセキュリティプロファイルを新規作成
ボタンをクリックする。
必要な情報を入力後、保存
ボタンをクリックする。
No. | 項目 | 値 |
---|---|---|
1 | セキュリティプロファイル名 | 任意 |
2 | セキュリティプロファイルの説明 | 任意 |
3 | プライバシー規約同意書URL | 任意 ※空欄可 |
4 | 同意のロゴ画像 | 任意 ※選択しなくても良い |
3. クライアントIDとクライアントシークレットの確認
対象セキュリティプロファイルのクライアントIDおよびクライアントシークレットを表示
リンクをクリックすると、クライアントIDおよびクライアントシークレットが表示されるので、これをメモ帳などに保管しておく。
※後で確認することでもできる。
※IDが表示されているが、このセキュリティプロファイルは削除済み。
Lambda関数を作成
Lambda関数を新規作成する。
1. AWSコンソールにログインする
2. Lambdaを選択する
スマートホームスキルはLambdaのリージョンが指定されているので注意。
センサー用のスマートホームスキルは"スキルの言語に英語(米国)を設定する"と記載されているが、Lambdaのリージョンは米国西部(オレゴン)を選択しないと動作しない。最初、スキルの言語が英語(米国)なので、Lambdaは米国東部(バージニア北部)で作成したけど動かなかった。
No. | スキルの言語 | Lambdaリージョン |
---|---|---|
1 | 英語(米国)または英語(カナダ) | 米国東部(バージニア北部) |
2 | 英語(英国)、英語(インド)、ドイツ語、フランス語(フランス) | EU(アイルランド) |
3 | 日本語および英語(オーストラリア) | 米国西部(オレゴン) |
3. Lambda関数を作成する
関数の作成
ボタンをクリックし必要な情報を選択および入力した後、画面右下の関数の作成
ボタンをクリックする。
ロールに関しては、マニュアルにはいろいろ書いてあるけど、今回はサンプルなので"基本的なLambda アクセス権限で新しいロールを作成"で作成することにした。詳細はマニュアル参照。
No. | 項目 | 値 |
---|---|---|
1 | オプション | 一から作成 |
2 | 名前 | Lambda関数の名前を入力します |
3 | ランタイム | Node.js 18.x |
4 | ロール | 基本的な Lambda アクセス権限で新しいロールを作成 |
4. トリガーを追加する
設定タブの+トリガーを追加
ボタンをクリックし、トリガーの設定の一覧の中からAlexa Smart Home
を選択する。
5. トリガーを設定する
トリガーの設定
セクションで、アプリケーションID
欄に、先ほど保管したスキルID
を追加する。
トリガーの有効化
はオンのままにしておく。
追加
ボタンをクリックした後、保存
ボタンをクリックする。
6. コードを作成する
exports.handler = function (request, context) {
if (request.directive.header.namespace === 'Alexa.Discovery' && request.directive.header.name === 'Discover') {
log("DEBUG:", "Alexa.Discovery:", JSON.stringify(request));
handleDiscovery(request, context, "");
}
else if (request.directive.header.namespace === 'Alexa') {
log("DEBUG:", "Alexa:", JSON.stringify(request));
if (request.directive.header.name === 'ReportState' ) {
log("DEBUG:", "Alexa:", JSON.stringify(request));
handleMotionSensor(request, context);
}
}
else if (request.directive.header.namespace === 'Alexa.MotionSensor') {
log("DEBUG:", "Alexa.MotionSensor:", JSON.stringify(request));
if (request.directive.header.name === 'detectionState' ) {
log("DEBUG:", "Alexa.MotionSensor:", JSON.stringify(request));
handleMotionSensor(request, context);
}
}
else if (request.directive.header.namespace === 'Alexa.EndpointHealth') {
log("DEBUG:", "Alexa.EndpointHealth:", JSON.stringify(request));
handleMotionSensor(request, context);
}
else if (request.directive.header.name === 'AcceptGrant') {
log("DEBUG:", "AcceptGrant:", JSON.stringify(request)); // AcceptGrantの内容をlogに出力
var response = {
"event": {
"header": {
"namespace": "Alexa.Authorization",
"name": "AcceptGrant.Response",
"messageId": request.directive.header.messageId,
"payloadVersion": "3"
},
"payload": {}
}
};
context.succeed(response);
}
else {
log("DEBUG:", "Other request:", JSON.stringify(request));
log("DEBUG:", "Other context:", JSON.stringify(context));
}
function handleDiscovery(request, context) {
var payload = {
"endpoints":
[
{
"endpointId": "sensor-001",
"manufacturerName": "TAKARA MOTION SENSOR",
"friendlyName": "TAKARAのセンサー",
"description": "TAKARAのセンサー(スマートホームスキル)",
"displayCategories": ["MOTION_SENSOR"],
"cookie": {
"key1": "このエンドポイントのキーと値のペアです",
"key2": "複数のエントリーがある場合があります",
"key3": "参照目的で使用します",
"key4": "現在のエンドポイントの状態を維持するためには使わないでください"
},
"capabilities": [
{
"type": "AlexaInterface",
"interface": "Alexa",
"version": "3"
},
{
"type": "AlexaInterface",
"interface": "Alexa.MotionSensor",
"version": "3",
"properties": {
"supported": [
{
"name": "detectionState"
}
],
"proactivelyReported": true,
"retrievable": true
}
},
{
"type": "AlexaInterface",
"interface": "Alexa.EndpointHealth",
"version": "3",
"properties": {
"supported": [
{
"name": "connectivity"
}
],
"proactivelyReported": true,
"retrievable": true
}
}
]
}
]
};
var header = request.directive.header;
header.name = "Discover.Response";
log("DEBUG", "Discovery Response:", JSON.stringify({ header: header, payload: payload }));
context.succeed({ event: { header: header, payload: payload } });
}
function log(message, message1, message2) {
console.log(message + message1 + message2);
}
function handleMotionSensor(request, context) {
// 検出中に渡されたデバイスIDを取得します
log("DEBUG", "handleMotionSensor: ", JSON.stringify({ request }));
var requestMethod = request.directive.header.name;
var responseHeader = request.directive.header;
responseHeader.namespace = "Alexa";
responseHeader.name = "Response";
responseHeader.messageId = responseHeader.messageId + "-R";
// リクエスト中のユーザートークンパスを取得します
var requestToken = request.directive.endpoint.scope.token;
var SHResult;
// デバイス制御クラウドを呼び出して制御します
SHResult = "DETECTED";
var contextResult = {
"properties": [
{
"namespace": "Alexa.MotionSensor",
"name": "detectionState",
"value": SHResult,
"timeOfSample": "2017-02-03T16:20:50.52Z",
"uncertaintyInMilliseconds": 0
}
]
};
var response = {
context: contextResult,
event: {
header: responseHeader,
endpoint: {
scope: {
type: "BearerToken",
token: requestToken
},
endpointId: "sensor-001"
},
payload: {}
}
};
log("DEBUG", "Alexa.MotionSensor ", JSON.stringify(response));
context.succeed(response);
}
};
7. LambdaのARNをコピーする
作成したLambda関数のARNをメモ帳などに保管しておく。
Lambda関数のテスト
作成したLambda関数をテストする。
1. テストを作成する
画面右上の方にあるテスト
ボタンをクリックする。
※現在はテスト
の位置が変わっていて、テストタブになっています。
2. テストイベントを作成する
イベントテンプレートは、デフォルトのHello Worldのままにしておく。
{
"directive": {
"header": {
"namespace": "Alexa.Discovery",
"name": "Discover",
"payloadVersion": "3",
"messageId": "1bd5d003-31b9-476f-ad03-71d471922820"
},
"payload": {
"scope": {
"type": "BearerToken",
"token": "access-token-from-skill"
}
}
}
}
イベント名にDiscovery
と入力し、エディターのコンテンツをすべて以下のコードで置き換え変更を保存
ボタンをクリックする。
3. テストを実行する
テスト
ボタンをクリックする。成功すると以下のような結果が表示される。
詳細をクリックし、以下のような実行結果が表示されれば成功。
Response:
{
"event": {
"header": {
"namespace": "Alexa.Discovery",
"name": "Discover.Response",
"payloadVersion": "3",
"messageId": "1bd5d003-31b9-476f-ad03-71d471922820"
},
"payload": {
"endpoints": [
{
"endpointId": "sensor-001",
"manufacturerName": "TAKARA MOTION SENSOR",
"friendlyName": "TAKARAのセンサー",
"description": "TAKARAのセンサー(スマートホームスキル)",
"displayCategories": [
"MOTION_SENSOR"
],
"cookie": {
"key1": "このエンドポイントのキーと値のペアです",
"key2": "複数のエントリーがある場合があります",
"key3": "参照目的で使用します",
"key4": "現在のエンドポイントの状態を維持するためには使わないでください"
},
"capabilities": [
{
"type": "AlexaInterface",
"interface": "Alexa",
"version": "3"
},
{
"type": "AlexaInterface",
"interface": "Alexa.MotionSensor",
"version": "3",
"properties": {
"supported": [
{
"name": "detectionState"
}
],
"proactivelyReported": true,
"retrievable": true
}
},
{
"type": "AlexaInterface",
"interface": "Alexa.EndpointHealth",
"version": "3",
"properties": {
"supported": [
{
"name": "connectivity"
}
],
"proactivelyReported": true,
"retrievable": true
}
}
]
}
]
}
}
}
スマートホームスキルの詳細設定
スマートホームスキルにエンドポイントと、アカウントリンクを設定する。
1. Alexa Developer Consoleに戻る
2. エンドポイントを設定する
スマートホームサービスのエンドポイントのデフォルトのエンドポイントボックスに、作成したLambda関数のARNを入力する
保存
ボタンをクリックする。
3. アカウントリンクを設定する
左ペインからアカウントリンク
をクリックし、必要事項を入力した後保存
ボタンをクリックする。
認証画面のURI
は、認可リクエストを参照。
アクセストークンのURI
は、アクセストークンリクエストを参照。
No. | 項目 | 値 |
---|---|---|
1 | 認証画面のURI | https://www.amazon.com/ap/oa |
2 | アクセストークンのURI | https://api.amazon.com/auth/o2/token |
3 | ユーザーのクライアントID | ※セキュリティプロファイルのクライアントID
|
4 | ユーザーのシークレット | ※セキュリティプロファイルのクライアントシークレット
|
5 | ユーザーの認可スキーム | HTTP Basic認証 (推奨) |
6 | スコープ | profile |
7 | ドメインリスト | |
8 | デフォルトのアクセストークンの有効期限 | |
9 | Alexaのリダイレクト先のURL |
https://pitangui.amazon.com/api/skill/link/MA67IWRRJKXIU https://layla.amazon.com/api/skill/link/MA67IWRRJKXIU https://alexa.amazon.co.jp/api/skill/link/MA67IWRRJKXIU |
スマートホームスキルのテスト
スキルの詳細設定が完了し、Lambda関数のテストが完了したら、スマートホームスキルをテストする。
1. Alexaアプリにログインする
2. 対象のスマートホームスキルを有効にする
有効にする
ボタンをクリックしてスキルを有効にすると、スキルをデバイス制御クラウドにアカウントリンクするページにリダイレクトされる。
3. アカウントリンク
Eメールアドレス
とAmazonのパスワード
を入力し、ログイン
ボタンをクリックする。
"スマートホームが正常にリンクされました。"と表示されれば成功。
4. スマートホームスキルを検出する
Alexaアプリで端末を検出
し、作成したスマートホームスキルがデバイスの一覧に表示されれば成功。
5. 動作テスト
Alexaアプリで定型アクション
を作成する。
実行条件の設定でスマートホーム
を選択し、デバイス一覧で作成したスマートホームスキルを選択し、実行条件を検出
に設定する。
※センサー用のスマートホームスキルにしないとデバイス一覧には表示されない。
外部からイベントゲートウェイにChangeReportを送信して、定型アクションが動作すれば成功。
今回はブラウザからChangeReport
を送信した。
記載している内容は、2020年4月5日時点での情報。変更されている可能性があるので注意。
おわりに
実は、センサーデバイスがなくてもChangeReport
ディレクティブを送信してあげればスマートホームスキルは動いてくれる。つまり、イベントゲートウェイにChangeReport
ディレクティブを送信する仕組みは、デバイスでもアプリでもプログラムでもブラウザでも何でも良い。顔認証、距離、温度、湿度や天気など、いろいろな変化をトリガーにしてAlexaを起動することができるようになる。