ガジェットからカスタムイベントを受信する
カスタムスキルを有効にして、ガジェットからの情報を処理して対応できるようにするには、ガジェットからスキルにカスタムイベントが送信されるよう設定します。カスタムイベントには、ガジェット固有の任意のペイロードが含まれます。
カスタムイベントをサポートするには、まずカスタムインターフェースを定義し、次にガジェットのファームウェアで、定義したカスタムイベントをエンコードするコードを追加します。スキルコードはカスタムインターフェースコントローラーを使用することで、これらのカスタムイベントを受信できます。このインターフェースは、Alexa Skills Kit開発者コンソールまたはAlexa Skills Kitコマンドラインインターフェース(ASK CLI)を使用してスキルをセットアップする際に、スキルに追加します。
このトピックでは、カスタムイベントを受信するようスキルを設定する方法について説明します。カスタムイベントは、このトピックでは単に「イベント」と呼びます。インターフェースを定義する方法や、そのインターフェースをサポートするためにガジェットで行うべき処理については、カスタムインターフェースの詳細を参照してください。また、スキルからガジェットにカスタムディレクティブを送信することもできます。詳しくは、スキルからガジェットにカスタムディレクティブを送信するを参照してください。
サンプルコードについては、GitHubのAlexa Gadgets Raspberry Piサンプルを参照してください。
概要
ガジェットはAlexaクラウドに直接接続されないため、Echoデバイスを使用してスキルとの通信を処理します。次の図は、ガジェットがEchoデバイスを通じてスキルにイベントを送信する流れを示しています。

カスタムイベントのしくみ
スキルはガジェットからイベントを自動的に受信するわけではありません。イベントの受信を求めていること、受信するイベントのタイプ、イベントが満たす必要がある条件、Alexaからスキルにイベントを転送する期間を、スキルからAlexaに示す必要があります。そのためには、スキルからAlexaに対して、この情報を使用してイベントハンドラーのコンフィギュレーションを行うディレクティブを送信します。
ガジェットのイベントによってスキルの起動がトリガーされることはありません。スキルは既に動作中で、イベントハンドラーを開始するディレクティブを送信済みである必要があります。これは、スキルでガジェットからのイベントを受信する準備ができていることを意味します。
各スキルインスタンスに対して一度に有効化できるイベントハンドラーは1つのみです。新しいイベントハンドラーを開始すると、それが前のイベントハンドラーと置き換わります。通常、イベントハンドラーはスキルの動作に合わせて何度も開始と停止を繰り返します。
前提条件
ガジェットからカスタムイベントを受信するためのスキルコードを追加する前に、以下を実行します。
- Echoデバイスのソフトウェアバージョンが、開始する前にに記載されているバージョン以降であることを確認します。Echoデバイスのソフトウェアバージョンを確認するには、Alexaアプリで設定>デバイスの設定>デバイス>その他>デバイスのソフトウェアバージョンの順に移動します。Echoデバイスのソフトウェアバージョンを最新にするには、以下を実行します。
- 画面の付いていないEchoデバイスの場合 – 「アレクサ、ソフトウェアをアップデートして」と呼びかけます。
- 画面付きEchoデバイスの場合 – Echoデバイスで設定>デバイスオプション>ソフトウェアアップデートの確認の順に選択します。
- インターフェースが定義されており、ガジェットがそのインターフェースのサポートを宣言していることを確認します。これらの要素の詳細については、カスタムインターフェースの詳細を参照してください。
- カスタムスキルを作成し、スキルのサポート対象インターフェースにカスタムインターフェースコントローラーのインターフェースを追加します。詳しくは、ガジェットにスキルを設定するを参照してください。
カスタムイベントのディレクティブ
イベントハンドラーに関連するディレクティブは2つあります。
CustomInterfaceController.StartEventHandler
– スキルにイベントを渡すイベントハンドラーのコンフィギュレーションと開始を行うコマンドをAlexaに送信します。できるだけ早くスキルがイベントを受信できるようにするには、AlexaからのLaunchRequest
への応答として、このディレクティブを送信することをお勧めします。CustomInterfaceController.StopEventHandler
– 現在のイベントハンドラーを停止するコマンドをAlexaに送信します。この結果、スキルへのイベントの送信が停止されます。これはオプションです。このディレクティブを送信しない場合、指定された期間が経過すると、イベントハンドラーは自動的に停止します。
StartEventHandler
ディレクティブ
このディレクティブは、イベントハンドラーのコンフィギュレーションと開始を行います。これにより、スキルがカスタムイベントを受信できるようになります。1つのスキルに対して一度に有効化できるイベントハンドラーは1つのみです。
以下は、StartEventHandler
ディレクティブの例と、SDKを使用してディレクティブを組み立てる方法を示しています。例の後に、各フィールドについて説明します。
{
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>ゲームを始めましょう。</speak>"
},
"shouldEndSession": false,
"directives": [
{
"type": "CustomInterfaceController.StartEventHandler",
"token": "1234abcd-40bb-11e9-9527-6b98b093d166",
"expiration": {
"durationInMilliseconds": 8000,
"expirationPayload": {
"gameOverSpeech": "ゲームオーバーです。 ステータスを聞きたいですか?"
}
},
"eventFilter": {
"filterExpression":{
"and": [
{"==": [{"var": "header.namespace"}, "Custom.Robot"]},
{ "==": [{ "var": "endpoint.endpointId" }, "amzn1.ask.endpoint.ABCDEFGHIJKLMNOPQRSTUVWXYZ"]}
]
},
"filterMatchAction": "SEND_AND_TERMINATE"
}
}
]
}
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
const StartInputIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'StartEventHandlerIntent';
},
handle(handlerInput) {
// イベントハンドラーのコンフィギュレーションを行うディレクティブを含む
// 応答を作成します。ここに示されているディレクティブは、
// イベントを8秒間リッスンし、特定のガジェットから送信される
// Custom.Robot名前空間に属するイベントのみを受信するよう
// イベントハンドラーのコンフィギュレーションを行います。このコンフィギュレーションでは、
// 条件を満たすイベントを受信したらイベントハンドラーを停止
// することも指定します。
const response = handlerInput.responseBuilder
.speak("ゲームを始めましょう。")
.withShouldEndSession(false)
.addDirective({
'type': 'CustomInterfaceController.StartEventHandler',
'token': '1234abcd-40bb-11e9-9527-6b98b093d166',
'expiration': {
'durationInMilliseconds': 8000,
'expirationPayload': {
'gameOverSpeech': 'ゲームオーバーです。 ステータスを聞きたいですか?'
}
},
'eventFilter': {
'filterExpression':{
'and': [
{'==': [{'var': 'header.namespace'}, 'Custom.Robot']},
{'==': [{'var': 'endpoint.endpointId' }, 'amzn1.ask.endpoint.ABCDEFGHIJKLMNOPQRSTUVWXYZ']}
]
},
'filterMatchAction': 'SEND_AND_TERMINATE'
}
})
.getResponse();
// CloudWatchへの応答を記述します。
console.log("===応答=== "+ JSON.stringify(response));
// 応答を返します。
return response;
}
};
このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。
@Override
public Optional<Response> handle(HandlerInput input) {
final String speechText = "ゲームを始めましょう。";
// リクエストIDをトークンとして使用し、セッションアトリビュートでこの値をキャッシュします。
String token = input.getRequest().getRequestId();
input.getAttributesManager().getSessionAttributes().put(StatusGaugeHandler.TOKEN_KEY, token);
// スキルでカスタムイベントを受信できるようにする
// StartEventHandlerディレクティブを作成します。スキルで一度に
// 有効化できるイベントハンドラーは1つのみです。
StartEventHandlerDirective directive = StartEventHandlerDirective.builder()
.withToken(token)
.withEventFilter(EventFilter.builder()
.withFilterExpression(JsonUtil.fromString(FILTER))
.withFilterMatchAction(FilterMatchAction.SEND).build())
.withExpiration(Expiration.builder()
.withDurationInMilliseconds(30000l)
.withExpirationPayload(JsonUtil.fromString(EXPIRATION_PAYLOAD)).build())
.build();
Optional<Response> response = input.getResponseBuilder()
.withSpeech(speechText)
.withSimpleCard(RobotEventHandler.SKILL_TITLE, speechText)
.addDirective(directive).build();
System.out.println("=== 応答を返す === " + JsonUtil.asString(response.get()));
return response;
}
このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。
def build_start_event_handler_directive(token, duration=30000):
"""イベントハンドラーを開始するカスタムディレクティブを作成します。
:param token: イベントハンドラーに関連付けられる一意のID
:param duration: イベントハンドラーの有効期間(ミリ秒)
:return: イベントハンドラーを開始するカスタムディレクティブ
"""
filter_expression = {"==": [{"var": "header.namespace"}, "Custom.Event"]}
payload = {"data": "スキルセッションの有効期限が切れました。さようなら。"}
return StartEventHandlerDirective(
token=token,
event_filter=EventFilter(
filter_expression=filter_expression,
filter_match_action=FilterMatchAction.SEND
),
expiration=Expiration(
duration_in_milliseconds=duration,
expiration_payload=payload))
StartEventHandler
ディレクティブには、次のフィールドが含まれています。
フィールド | 説明 | 型 | 必須 |
---|---|---|---|
|
ディレクティブの種類です。 |
|
〇 |
|
イベントハンドラーによってディスパッチされたすべてのカスタムイベントと関連付けるための一意のIDです。このトークンを保存しておき、新しいイベントハンドラーを開始した後で送られてくるイベントを拒否するフィルターとして使用します。Alexaからスキルへのリクエストの |
UUID16の |
〇 |
|
イベントハンドラーの期限と、イベントハンドラーが期限切れになった場合にのみスキルが受信するJSONペイロード(オプション)を定義するオブジェクトです。 |
オブジェクト |
〇 |
|
接続されたガジェットからのイベントがスキルに渡されるまでの時間(ミリ秒)です。この期限が切れるまで、またはイベントハンドラーが何らかの理由で停止するまで、スキルはイベントを受信し続けます。 |
int64 |
〇 |
|
イベントハンドラーが期限切れになった場合にのみスキルが受信する自由形式のJSONオブジェクトです。 |
オブジェクト |
✕ |
|
jsonlogicイベントフィルター式と、それに対応する一致アクションを定義します。このフィルターは、イベントハンドラーの期間中、すべてのイベントに適用されます。フィルター式によって却下されたイベントは、スキルに送信されません。 |
オブジェクト |
✕ |
|
JSONロジックを表すJSONオブジェクトです。これに照らしてイベントが評価されます。この式を満たすと、対応する 例については、フィルター式の例を参照してください。 |
オブジェクト(jsonlogic) |
〇 |
|
フィルター式が一致すると、アクションを実行します。有効な値は、 |
列挙{ |
〇 |
フィルター式の例
StopEventHandler
ディレクティブ
このディレクティブは、提供されたトークンに関連するイベントハンドラーが実行中である場合に、イベントハンドラーを停止します。イベントハンドラーの期限が切れる前にスキルによってこのディレクティブが送信された場合、スキルにはCustomInterfaceController.Expired
イベントが送信されません。StopEventHandler
ディレクティブの例を次に示します。
{
"shouldEndSession": false,
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>ゲームオーバーです。</speak>"
},
"directives": [
{
"type": "CustomInterfaceController.StopEventHandler",
"token": "1234abcd-40bb-11e9-9527-6b98b093d166"
}
]
}
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
const StopInputIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'StopEventHandlerIntent';
},
handle(handlerInput) {
// イベントハンドラーの開始時に指定したトークンが
// 保存されているセッションアトリビュートを
// 取得します。
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
// 応答の作成を開始します。
const responseBuilder = handlerInput.responseBuilder;
responseBuilder.withShouldEndSession(false)
.speak("ゲームオーバーです。");
// セッションアトリビュートからイベントハンドラートークンを
// 取得できたら、それでイベントハンドラーを停止します。トークンが
// 既に動作を停止しているイベントハンドラーからのものであっても、
// このディレクティブは何の影響も及ぼさないので、
// 問題ありません。
if (sessionAttributes.currentInputHandlerId) {
// ディレクティブを追加します。
responseBuilder.addDirective({
'type': 'CustomInterfaceController.StopEventHandler',
'token': sessionAttributes.currentEventHandlerToken
});
}
// 応答を作成します。
const response = responseBuilder.getResponse();
// CloudWatchへの応答を記述します。
console.log('===応答=== '+ JSON.stringify(response));
// 応答を返します。
return response;
}
};
このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。
@Override
public Optional<Response> handle(HandlerInput input) {
String speechText = "ではまた。";
// トークンIDが一致したらイベントハンドラーを停止します。
Map<String, Object> attributes = input.getAttributesManager().getSessionAttributes();
String cachedToken = (String) attributes.getOrDefault(TOKEN_KEY, "MissingToken");
StopEventHandlerDirective directive =
StopEventHandlerDirective.builder().withToken(cachedToken).build();
return input.getResponseBuilder()
.withSpeech(speechText)
.withSimpleCard(StatusGaugeHandler.SKILL_TITLE, speechText)
.addDirective(directive).build();
}
このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。
@sb.request_handler(can_handle_func=lambda handler_input:
is_intent_name("AMAZON.CancelIntent")(handler_input) or
is_intent_name("AMAZON.StopIntent")(handler_input))
def stop_and_cancel_intent_handler(handler_input):
logger.info("stop_and_cancel_intent_handler:リクエストを処理します")
session_attr = handler_input.attributes_manager.session_attributes
response_builder = handler_input.response_builder
if EVENT_HANDLER_TOKEN_KEY in session_attr and session_attr[EVENT_HANDLER_TOKEN_KEY] is not None:
token = session_attr[EVENT_HANDLER_TOKEN_KEY]
response_builder.add_directive(StopEventHandlerDirective(token))
return response_builder.speak("ではまた。").response
StopEventHandler
ディレクティブには、次のフィールドが含まれています。
フィールド | 説明 | 型 | 必須 |
---|---|---|---|
|
ディレクティブの種類です。 |
|
〇 |
|
停止するイベントハンドラーに関連付けられている一意のIDです。このトークンは、 |
|
〇 |
カスタムイベントのタイプ
StartEventHandler
を使用してイベントハンドラーを開始した後、スキルは2つのタイプのリクエストを受信します。
CustomInterfaceController.EventsReceived
– イベントがイベントハンドラーのフィルター条件を満たしている場合に受信します。CustomInterfaceController.Expired
– イベントハンドラーの期限が切れている場合に受信します。
スキルがイベントを受信する順番は、イベントの生成順ではない場合があります。イベントの順番を把握するため、シーケンスIDを生成して、イベントペイロードに含めることができます。スキル側でシーケンスIDを調べることで、イベントが正しい順番で受信されたかどうかを確認できます。しかし、セッションアトリビュートも使用する場合は特に、検出以外でスキルが順不同のイベントを処理する可能性はほとんどありません。
EventsReceived
リクエスト
スキルは、StartEventHandler
ディレクティブで指定したフィルター条件をイベントが満たしている場合、このタイプのリクエストを受信します。以下は、EventsReceived
リクエストの例です。例の後に、各フィールドについて説明します。
{
"version": "1.0",
"session": {
"application": {},
"user": {}
},
"request": {
"type": "CustomInterfaceController.EventsReceived",
"requestId":"amzn1.echo-api.request.406fbc75-8bf8-4077-a73d-519f53d172a4",
"timestamp":"2018-12-02T01:29:40.027Z",
"token": "1234abcd-40bb-11e9-9527-6b98b093d166",
"events": [
{
"header" : {
"namespace":"Custom.Robot",
"name":"EyeBlink"
},
"endpoint": {
"endpointId": "amzn1.ask.endpoint.ABCD"
},
"payload":{
"ack": "ok"
}
}
]
}
}
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
canHandle(handlerInput) {
let { request } = handlerInput.requestEnvelope;
console.log("CustomEventHandler:処理できるかどうかを確認します" + request.type);
return (request.type !== 'CustomInterfaceController.EventsReceived');
},
handle(handlerInput) {
// ロボットからのステータス確認応答を処理します。
console.log("== カスタムイベントの受信 ==");
let { request } = handlerInput.requestEnvelope;
let payload = request.events[0].payload;
if (payload.ack === "ok") {
return response.speak("ロボットのステータスが正常に更新されました")
.getResponse();
}
return response;
}
このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。
@Override
public Optional<Response> handle(HandlerInput input) {
EventsReceivedRequest event = (EventsReceivedRequest) input.getRequestEnvelope().getRequest();
Map<String, Object> attributes = input.getAttributesManager().getSessionAttributes();
String cachedEndpointId = (String) attributes.getOrDefault(ENDPOINT_KEY, "MissingEndpoint");
String cachedToken = (String) attributes.getOrDefault(TOKEN_KEY, "MissingToken");
if (!cachedToken.equals(event.getToken())) {
// イベントは無視します
System.out.println("==SessionAttributes==" + JsonUtil.asString(attributes));
System.out.println("トークンを持つイベントを無視しています:" + event.getToken());
return Optional.empty();
}
// イベントが1つのみと仮定した場合
Event customEvent = event.getEvents().get(0);
Header header = customEvent.getHeader();
String endpointId = customEvent.getEndpoint().getEndpointId();
String speechText = "予期しないイベントを受信しました。";
// 名前空間は開始イベントハンドラーのフィルターで既にチェックされています
if (EVENT_STATUS_ACK.equals(header.getName())) {
// イベントが同じガジェットからのものであるか検証します
if (cachedEndpointId.equals(endpointId)) {
JsonNode payload = JsonUtil.fromMap(customEvent.getPayload());
if ("ok".equals(payload.get("ack").asText())) {
speechText = "ロボットのステータスが正常に更新されました";
}
}
}
return input.getResponseBuilder().withSpeech(speechText, PlayBehavior.ENQUEUE).build();
}
このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。
@sb.request_handler(can_handle_func=is_request_type("CustomInterfaceController.EventsReceived"))
def gadget_event_handler(handler_input: HandlerInput):
"""メッセージがガジェットによって正常に受信されたことを確認します。
:param handler_input: すべてのリクエストハンドラーに提供されたデータ
:return: メッセージが正常に受信されたかどうかを示す応答
"""
logger.info("gadget_event_handler:ガジェットからのイベントを処理します")
custom_event = handler_input.request_envelope.request # type: EventsReceivedRequest
session_attr = handler_input.attributes_manager.session_attributes
response_builder = handler_input.response_builder
# イベントトークンを検証します
cached_token = session_attr[EVENT_HANDLER_TOKEN_KEY]
event_token = custom_event.token
if cached_token != event_token:
logger.info("イベントトークンが一致しません。このイベントを無視します")
return response_builder.response
# イベント名を検証します(名前空間はイベントハンドラーのフィルターで既にチェックされています)
event = custom_event.events[0]
if EVENT_NAME == event.header.name:
payload = event.payload
# ロボットがイベントで送信したステータスメッセージを確認します
if payload['ack'] == 'ok':
return response_builder\
.speak("ロボットのステータスが正常に更新されました", PlayBehavior.ENQUEUE)\
.response
return response_builder\
.speak("このイベントを処理できません", PlayBehavior.ENQUEUE)\
.response
EventsReceived
リクエストは、次のフィールドを含みます。
フィールド | 説明 | 型 |
---|---|---|
|
イベントタイプ( |
|
|
Alexaからスキルに送られたリクエストのIDです。 |
|
|
Alexaからスキルにリクエストが送られた時刻です。 |
|
|
このイベントをディスパッチしたイベントハンドラーの |
|
|
フィルター条件に一致するイベントのリストです。 |
配列 |
|
イベントのヘッダーを含むオブジェクトです。 |
オブジェクト |
|
ガジェットに定義されたイベントの名前空間(インターフェース名)です。 |
|
|
ガジェットに定義されたイベントの名前です。 |
|
|
イベントを送ったガジェットのエンドポイントIDを含むオブジェクトです。 |
オブジェクト |
|
イベントを送ったガジェットのエンドポイントIDです。 |
|
|
この名前空間とタイプのイベントにある、ガジェット定義フィールドを含む自由形式のJSONオブジェクトです。 |
オブジェクト |
Expired
リクエスト
イベントハンドラーが期限切れになると、スキルはこのタイプのリクエストを受信します。以下は、Expired
リクエストの例です。例の後に、各フィールドについて説明します。
{
"version": "1.0",
"session": {
"application": {},
"user": {}
},
"request": {
"type": "CustomInterfaceController.Expired",
"requestId":"amzn1.echo-api.request.406fbc75-8bf8-4077-a73d-519f53d172a4",
"timestamp":"2018-12-02T01:29:40.027Z",
"expirationPayload": {
<JSON Object>
},
"token": "1234abcd-40bb-11e9-9527-6b98b093d166"
}
}
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
canHandle(handlerInput) {
let { request } = handlerInput.requestEnvelope;
console.log("CustomEventHandler:処理できるかどうかを確認します" + request.type);
return request.type === 'CustomInterfaceController.Expired';
},
handle(handlerInput) {
console.log("== カスタムイベントの有効期限の入力 ==");
let { request } = handlerInput.requestEnvelope;
let data = request.expirationPayload.data;
let response = handlerInput.responseBuilder
.withShouldEndSession(true)
.speak(data)
.getResponse();
response.directives = response.directives || [];
return response;
}
このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。
public class ExpirationEventHandler implements RequestHandler {
@Override
public boolean canHandle(HandlerInput input) {
return input.matches(requestType(ExpiredRequest.class));
}
@Override
public Optional<Response> handle(HandlerInput input) {
ExpiredRequest event = (ExpiredRequest) input.getRequestEnvelope().getRequest();
JsonNode payload = JsonUtil.fromMap(event.getExpirationPayload());
String speechText = payload.get("data").toString();
return input.getResponseBuilder()
.withSpeech(speechText, PlayBehavior.ENQUEUE)
.withShouldEndSession(true)
.build();
}
}
このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。
@sb.request_handler(can_handle_func=is_request_type("CustomInterfaceController.Expired"))
def event_handler_expiration_handler(handler_input):
"""イベントハンドラーを停止し、スキルセッションを終了します。
:param handler_input: すべてのリクエストハンドラーに提供されたデータ
:return: スキルが終了したかどうかを示す応答
"""
logger.info("event_handler_expiration_handler:期限切れイベントを処理します")
request = handler_input.request_envelope.request # type: ExpiredRequest
response_builder = handler_input.response_builder
if request.expiration_payload is None:
speech_text = "期限切れペイロードがありません"
else:
logger.info("期限切れペイロード:" + str(request.expiration_payload))
speech_text = request.expiration_payload['data']
return response_builder.set_should_end_session(True).speak(speech_text).response
Expired
リクエストは、次のフィールドを含みます。
フィールド | 説明 | 型 |
---|---|---|
|
イベントのタイプ( |
|
|
AlexaからのリクエストのIDです。 |
|
|
Alexaからのリクエストの時刻です。 |
|
|
イベントハンドラーが期限切れになった場合にのみスキルが受信する自由形式のJSONオブジェクトです。これは、 |
オブジェクト |
|
このイベントをディスパッチしたイベントハンドラーの |
|
イベントの処理
このセクションでは、スキルで受信するイベントの検証方法を例を挙げて説明します。
エンドポイントIDを検証する
カスタムディレクティブに応答してガジェットからスキルにカスタムイベントが送信される場合、スキルはガジェットから送信されるイベントを検証する必要があります。そのためには、エンドポイントIDを検証します。
まず、Endpoint Enumeration APIを使用して、接続済みガジェット(エンドポイントID)のリストを次のとおり保存する必要があります。
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
let { context } = handlerInput.requestEnvelope;
let { apiEndpoint, apiAccessToken } = context.System;
let response;
try {
console.log("エンドポイントを確認します");
response = await Endpoint.getConnectedEndpoints(apiEndpoint, apiAccessToken);
console.log("v1/endpointsの応答: " + JSON.stringify(response));
if ((response.endpoints || []).length === 0) {
console.log('利用可能な接続済みエンドポイントがありません');
response = handlerInput.responseBuilder
.speak("エンドポイントが見つかりません。ガジェットを接続してからもう一度お試しください。")
.getResponse();
return response;
}
// エンドポイントIDを保存しておき、後でカスタムディレクティブと
// イベントが同じガジェットからのものであるか確認できるようにします。
let endpointId = response.endpoints[0].endpointId;
console.log("エンドポイントを受信しました。エンドポイントIDを保存しています:" + endpointId);
const attributesManager = handlerInput.attributesManager;
let sessionAttributes = attributesManager.getSessionAttributes();
sessionAttributes.endpointId = endpointId;
attributesManager.setSessionAttributes(sessionAttributes);
}
接続済みガジェットのエンドポイントIDを保存したら、カスタムイベントの受信時にエンドポイントIDを検証できます。
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
const CustomEventHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'CustomInterfaceController.EventsReceived';
},
handle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
// スキルでイベントを取得しようとしているガジェットの
// エンドポイントIDが保存されているセッションアトリビュートを取得します。
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
// エンドポイントIDを検証します。
// 理解しやすくするため、ここではイベントが1つのみであると仮定しています。
let endpointOfCurrentRequest = request.events[0].endpoint.endpointId;
return endpointOfCurrentRequest == sessionAttributes.endpointId;
}
};
イベントの名前空間と名前を検証する
スキルでイベントを受信したら、処理を進める前に、イベントの名前空間と名前を確認する必要があります。受信したイベントの名前空間と名前は、カスタムインターフェースで定義したイベントの名前空間と名前に対応しています。以下に、この検証の実装方法の例を示します。
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
const EXPECTED_NAMESPACE = "Custom.Robot";
const EXPECTED_NAME = "Blink";
const CustomEventHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'CustomInterfaceController.EventsReceived';
},
handle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
var infoToSpeak = '';
// イベントの名前空間と名前を見つけます。
// 理解しやすくするため、ここではイベントが1つのみであると仮定しています。
let namespace = request.events[0].header.namespace;
let name = request.events[0].header.name;
console.log('イベントを検証しています。名前空間:' + namespace + '、名前:' + name);
// 名前空間と名前が想定のものかを検証します。
if (namespace != EXPECTED_NAMESPACE || name != EXPECTED_NAME) {
infoToSpeak = 'このイベントを処理できません。';
}
else {
infoToSpeak = '次のイベントを受信しました。名前空間:' + namespace + '、名前:' + name;
}
return handlerInput.responseBuilder
.speak(infoToSpeak)
.withShouldEndSession(false)
.getResponse();
}
}
};
期限切れペイロードを処理する
StartEventHandler
を使用してイベントハンドラーを設定する際、イベントハンドラーの期限が切れている場合(つまり、スキルがExpired
イベントを受信した場合)にAlexaに発話させるプレーンテキストexpiration.expirationPayload
を指定する場合があります。
次の例は、expiration.expirationPayload
で定義したgameOverSpeech
フィールドに保存したテキストをAlexaに発話させるための方法を示しています。
このサンプルコードは、Alexa Skills Kit SDK for Node.js(v2)を使用しています。
const EXPECTED_NAMESPACE = "Custom.Robot";
const EXPECTED_NAME = "Blink";
const ExpiredEventHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'CustomInterfaceController.Expired';
},
handle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
let response = handlerInput.responseBuilder
.speak(request.expirationPayload.gameOverSpeech)
.getResponse();
response.directives = response.directives || [];
return response;
}
};
カスタムイベントとセッションアトリビュート
ガジェットが複数のイベントをすばやく送信すると、スキルが同時に呼び出されることがあります。スキルセッション中のデータの保存にセッションアトリビュートを使用している場合、セッションアトリビュートはアトミックに更新されるとは限らないことに注意してください。セッションアトリビュートが同時に更新されることで、新たに追加されたアトリビュートが、ほかの同時リクエストからの応答によってオーバーライドされる可能性があります。次に例を示します:
スキルに連続的に送信されたイベント:
--> Request for Event1 has sessionAttributes = {}
<-- Response to Event1 has sessionAttributes = {'sawevent1': true}
--> Request for Event2 has sessionAttributes = {'sawevent1': true}
<-- Response to Event2 has sessionAttributes = {'sawevent1': true, 'sawevent2': true}
--> Request for Event3 has sessionAttributes = {'sawevent1': true, 'sawevent2': true}
スキルに同時に送信されたイベント:
--> Request for Event1 has sessionAttributes = {}
--> Request for Event2 has sessionAttributes = {}
<-- Response to Event1 has sessionAttributes = {'sawevent1': true}
<-- Response to Event2 has sessionAttributes = {'sawevent2': true}
--> Request for Event3 has sessionAttributes = {'sawevent2': true}
ガジェットからスキルに複数のイベントが同時に送信されることが予想される場合は、セッションアトリビュートではなく、DynamoDBのようなデータベースを使用してスキルの状態を保存することをお勧めします。