步骤8: 在CloudWatch中测试您的技能以及观察日志 (VSK Fire TV)
现在该测试您的技能并观察Alexa向您的Lambda函数发送的指令了。您将通过CloudWatch查看交互,它将列出Alexa发送至您的Lambda的指令。要查看Lambda发送到应用的响应,可以在Android Studio中打开Logcat。
- 通过ADB连接到Fire TV
- 说出测试表述
- 在CloudWatch中查看日志
- 表述的上下文以及隐式与显式确定目标
- 了解Alexa、Lambda和Fire TV应用之间的交互模型
- 针对“Find Iron Man 2”在CloudWatch中的完整交互
- SearchAndPlay指令的工作流程
- 示例应用的指令
- 故障排除
- 后续步骤
通过ADB连接到Fire TV
此时您已准备好在Fire TV上运行您的应用,以确保一切正常并获得授权。
- 按照通过ADB连接到Fire TV中的说明将您的计算机连接至Fire TV。
-
打开Android Studio,选择app(应用)配置,然后选择要运行应用的Fire TV设备(例如“Amazon AFTMM”)。(“AFTMM”是指Fire TV构建型号。)
-
通过单击Run App(运行应用)按钮来运行您的应用。
如果您尚未选择Fire TV设备,系统将提示您选择设备。选择Fire TV设备,然后单击OK(确定)。
在您的应用构建和运行时,单击Android Studio底部的Logcat选项卡以监控日志消息。如果您的应用运行,那么就可以确定您已经正确配置了安全配置文件。示例应用的主屏幕如下所示:
如果您在此步骤中遇到问题,请尝试用以下方法进行故障排除:
- 如果您收到错误消息(例如,某些条目名称发生冲突),请转到Build(构建)> Rebuild Project(重建项目)以查看它是否清除了任何过时的作品,然后再次运行该应用。
- 如果您遇到安装冲突,请确保您的Fire TV中没有任何其他具有相同程序包名称的应用。根据需要卸载任何应用(Settings(设置)> Applications(应用)> Manage Installed Applications(管理已安装应用))。
- 如果应用加载了空白屏幕或存在其他问题,请打开Logcat并就 “Error”(错误)进行筛选。 如果您看到错误消息 “Invalid API key”(API密钥无效),请检查并确保使用之前配置的自定义firetv签名密钥对应用进行签名。
说出测试表述
当应用在Fire TV的前台运行时,对您的Echo说出“Alexa, watch Superman”(Alexa,观看Superman)(或者任何其他电影或电视节目标题,如果您使用的是具有ontv
(IMDb) 目录的示例应用)。
当您第一次问这个问题时,Alexa会回答:“<Video Skill> is now Alexa-enabled…”(<视频技能>现在启用了Alexa…) Alexa告诉您,它已经自动将您的视频技能与Fire TV应用配对。然后Alexa说出:“Getting Superman from <Video Skill>”(从<视频技能>中获取Superman)。 然后开始播放示例视频。
在示例应用中,所有表述都映射到同一示例视频。
请注意,Alexa从您的视频技能中检索列表,而不是“从Fire TV”(这是之前在步骤1中您将Echo链接到Fire TV时的响应)。
在CloudWatch中查看日志
您可以使用CloudWatch来观察从视频技能中接收到的事件以及通过您的Lambda编写的日志。这将让您更好地了解各个组件之间的情况。要在CloudWatch中查看日志,可执行以下操作:
- 在AWS中,单击Services(服务)并转到CloudWatch(搜索它)。
- 在左侧边栏中,转到Logs(日志)> Log groups(日志组)。
-
单击您的Lambda函数。
如果您没有看到Lambda函数,请确保您位于正确的地区,因为仅与您所在地区相关的Lambda函数会出现。
- 将显示该函数的日志列表,其中最新的日志组位于顶部。(在您开始对Echo发出命令之前,日志不会显示在此处。)
如果在Lambda日志中,为了使日志更易于扫描,可以选择Text(文本)单选按钮。
请注意,您也可以通过转到Lambda函数来访问CloudWatch日志。单击Monitoring(监视)选项卡,然后单击View logs in CloudWatch(在CloudWatch中查看日志)按钮,如以下屏幕截图所示。
下面的CloudWatch中的完整交互提供了对日志的更详细解释。
表述的上下文以及隐式与显式确定目标
在进入Cloudwatch中的各种测试案例和逐步演练之前,请注意可以指定表述的不同上下文。您可以在两个主要的上下文中说出表述:
- 隐式定向: 未指定内容提供方或应用名称的语音命令,例如 “Watch Batman”(观看蝙蝠侠)。
- 显式定向: 指定内容提供方或应用名称的语音命令,例如"Watch Superman on Streamz"(在Streamz上观看Superman)。
当您测试各种表述时,请记住,只有在将您的技能推送至动态应用测试后,才能进行显式目标确定。当您通过Android Studio侧载应用时,Alexa无法处理您技能的显式目标确定。因此,您需要打开您的应用,使其位于前台。
如果您的应用在前台(而不是后台),并且您说出“Watch Superman”(观看Superman),Alexa会在前台目录的应用(而不是所有目录)中寻找匹配项。当您的应用在前台时说出一个命令相当于显式确定目标,但即使您从Android Studio本地侧载应用,也支持这点。
了解Alexa、Lambda和Fire TV应用之间的交互模型
现在您已经准备好了所有部件,接下来就可以详细描述所有这些组件如何协同工作。当您对支持Alexa的设备说出措词时,会解析这些措词的含义(通过云中的Alexa自然语言服务)并转换为指令。
指令是Alexa通过Video Skill API发送到Lambda进行处理的信息块(采用JSON格式)。API参考部分列出了大约六个用于Fire TV的指令。(请注意,VSK Fire TV的API与VSK Echo Show使用的API不同。)
您可以通过CloudWatch查看Alexa发送至Lambda函数的指令。CloudWatch是一项AWS服务,以日志、指标和事件的形式收集Lambda(和其他服务)的监控和操作数据。每次您的Lambda接收到来自Alexa的指令时,您的Lambda都可以记录指令和其他事件,并且您可以在CloudWatch中查看这些日志。
配置视频技能的核心任务是理解Alexa发送至Lambda函数的指令,然后在应用中实现预期响应。
您的Lambda还应向Alexa发送一个简单的确认响应,以表明您对指令的处理是成功的。然而,尽管您的Lambda函数会将响应发送回Alexa,但您的Lambda无法提供任何针对用户的自定义语音反馈。Alexa专门处理这种通信。您唯一可以执行的操作是执行API参考中指定的操作。
您的Lambda通过Amazon Device Messaging (ADM) 将信息传达至您的Fire TV应用。在示例应用的Lambda中,Lambda只需将其从Alexa收到的同一指令推送至您的应用。(您可以自定义Lambda,以便根据需要向应用发送信息,例如在处理或查询其他数据源之后。) 示例应用包含逻辑,该逻辑可以查看来自ADM的消息,找到与其目录匹配的任何媒体标识符并播放它们。您可以通过Logcat窗格查看传入的消息 - 为您搜索的标题进行筛选。
针对“Find Iron Man 2”在CloudWatch中的完整交互
在跳到下面的测试表述之前,让我们针对表述“Find Iron Man 2”(查找Iron Man 2),详细浏览CloudWatch的示例日志。 如果使用关键字“find”(查找),将提示Alexa发送SearchAndDisplayResults
指令。(使用关键字“watch” [观看] 或“play” [播放] 会提示Alexa发送SearchAndPlay
指令。) 如果您使用的是示例应用,您的CloudWatch日志应该和以下所示类似(至少对于示例Lambda是如此)。有关每条日志消息含义的解释,请参阅内嵌注释。请注意,为了便于阅读,已缩短了请求ID和时间戳。
START RequestId: b9e15474 Version: $LATEST
请求开始。
2020-03-18T04:28:49 b9e15474 INFO Lambda was invoked by Alexa
日志以Alexa请求事件开始。当调用Lambda函数时(在本例中,由充当“触发器”的视频技能调用),AWS Lambda通过调用handler
函数开始执行代码。运行时将三个参数传递至handler
方法:
exports.handler = (event, context, callback) =>
每个参数如下:
event
: 第一个参数event
包含来自引用者的信息。在本例中,引用者是Alexa指令。Alexa在调用Invoke
时将此指令作为JSON格式的字符串传递。context
: 第二个参数是context
对象,它包含有关引用、函数和执行环境的信息。callback
: 第三个参数callback
是一个函数,您可以在非异步函数中调用它来发送响应。
您可以在Node.js中的AWS Lambda函数处理程序中了解关于Lambda和处理程序的更多信息。
2020-03-18T04:28:49 b9e15474 INFO Context: …
{
"callbackWaitsForEmptyEventLoop": true,
"functionVersion": "$LATEST",
"functionName": "zombie_streamz_lambda",
"memoryLimitInMB": "128",
"logGroupName": "/aws/lambda/zombie_streamz_lambda",
"logStreamName": "2020/03/10/[$LATEST]34de0361da2c4450a97f2ba2c674fcb0",
"invokedFunctionArn": "arn:aws:lambda:us-east-1:458179560631:function:zombie_streamz_lambda",
"awsRequestId": "980c8eef"
}
为了查看context
,示例Lambda会记录以下信息:
console.log("Context: " + JSON.stringify(context, null, 2));
context
包含有关引用Lambda的事件的信息。上下文信息的用处不大,主要作用在于示出了引用的Lambda ARN和引用它的触发器(您的视频技能,或invokedFunctionArn
)。您可能想直接将此注释掉。在示例应用中取消对上下文的注释,以便您可以查看传递给它的信息。视频技能是您连接到该Lambda函数的智能家居触发器。
2020-03-18T04:28:49 b9e15474 INFO Directive received from Alexa: SearchAndDisplayResults
{
"directive": {
"payload": {
"entities": [
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6",
"tms": "MV002527780000"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.80a7eda2-3e16-55de-b56b-3136f5dc0e74",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.80a7eda2-3e16-55de-b56b-3136f5dc0e74",
"tms": "MV006154660000"
}
}
],
"searchText": {
"transcribed": "iron man two"
}
},
"header": {
"payloadVersion": "3",
"messageId": "f5cbe32c-72fc-4816-892d-92f695c125a4",
"namespace": "Alexa.RemoteVideoPlayer",
"name": "SearchAndDisplayResults",
"correlationToken": "7e8bdcef-1aff-452e-b405-5a877d712755"
},
"endpoint": {
"endpointId": "432521e3d404c9e3##amzn1-ask-skill-c7dc9021-ba50-4aa9-bbad-7b9bcd2e94c3##development##com-tomjoht-vskfiretv",
"cookie": {
"deviceType": "AKPGW064GI9HE",
"VSKClientVersion": "1.4.6",
"appPackageName": "com.tomjoht.vskfiretv",
"deviceId": "G070VM1694861D9N",
"appName": "VSKFireTV",
"applicationInstanceId": "amzn1.adm-registration.v3.Y29tLmFtYXpvbi5EZXZpY2VNZXNzYWdpbmcuUmVnaXN0cmF0aW9uSWRFbmNyeXB0aW9uS2V5ITEheFgvdzhUODl1SGc0WU96N0svSlZCVWFXVnBqUkNhdnJuWHlXWFBucUtvMHVLd0g2M1lkVEhZcjNRZ3J2K3A5QVAzdzhYbEkzZGYrY0t2Q0NQSWVQSEtEMDEvbGRhYUgrZVY4b1JRTXFMQndpYUZXajRXZlN3MEwzQ2ZaWkh2d0tUd3c1cFlYKys5WFdxZkVtRkg4RTQzcXhDMmxJYkZmd3dJeVVObStnWU00VU9GVmVwQTBVYityTFRRZHlYanNGUy9RTEhDbDhMUm1URmovZldsazF6WGRnZUV1V1pGWjkrcXYzR0lRcFV1MlpSYS9yZFA0aDEwbU5EcG1ndDE4eVJPRUg1bUtIQWVpUkZ3UlNwSlljcG9XSE96R3ZtMWJvZys2U1QyWUxvM3l0Nk4xNWI0QmFvMjBLcjNuZEl5UHpvNFBSUUs5aHZ1VUdTZ1psdUlFeVVsdzBZajFnb1ZZKzBLL3NGN2pJQnJjPSFmdk1qcTFuM0tOcVBOVzdyOGhybkp3PT0"
},
"scope": {
"token": null,
"type": "BearerToken"
}
}
}
}
在该场景中,您说出以下命令: “Find Iron Man 2”(查找Iron Man 2),由此得到SearchAndDisplayResults
。Alexa在云中解析了该表述,并生成了SearchAndPlay
指令,其中包含作为value
的搜索词的payload
。通过Video Skill API,Alexa向您的Lambda函数发出指令。
Alexa生成的SearchAndDisplayResults
指令指定了应用应该播放的内容。指定的内容为“Iron Man 2”。 Alexa在目录中查找与用户请求匹配的标题,并在externalIds
数组中列出匹配的条目。Alexa将所有匹配项发送至您的Lambda,并希望您确定应该在应用中播放哪一项。
请注意,对于该“Iron Man 2”示例,这部电影属于系列片,因此有效负载包含"type": "Franchise"
。有关详细信息,请参阅按系列片观看。当用户提出按系列片播放的请求时,您将收到用户在指令中请求的系列片(在本例中为Jurassic Park),但该指令将不包含目录ID。对于这些指令,预计您将在目录中搜索该内容,然后播放搜索结果中排名最靠前的结果。
2020-03-18T04:28:49 b9e15474 INFO Search started
示例Lambda函数包含一些搜索逻辑,用于解析传入请求中的值并在数据库中查找匹配项。这里的实现仅作为示例,展示如何使用目录中的有效负载在另一个数据源中创建搜索。示例应用实际上并没有找到并播放该内容,因为它可以播放的媒体数量有限。
在本例中,连接到Lambda的“数据库”位于video_catalog.json
文件中,该文件包含如下条目:
[
{
"id": 1,
"movie_id": "FG1",
"title": "Forrest Gump",
"genre": "drama",
"description": "A drama starring Tom Hanks",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
{
"id": 2,
"movie_id": "IM1",
"title": "钢铁侠1",
"genre": "action",
"description": "Tony Stark and his magic war suit",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
{
"id": 3,
"movie_id": "IM2",
"title": "钢铁侠2",
"genre": "action",
"description": "Tony Stark and his magic war suit do more cool stuff",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
}
]
示例Lambda利用了一个名为Fuse.js的JS库,该库提供了一个“轻量级模糊搜索库”来将搜索词与潜在匹配列表进行匹配。fuse
采用两种参数:数据源和搜索选项。在Lambda中,此项如下所示:
var fuse = new Fuse(VIDEOS, options)
VIDEOS
是数据源,定义为const VIDEOS = require("video_catalog.json");
。options
定义不同字段的权重以执行匹配。
Lambda从Alexa指令中获取关键字 (let searchTerm = directive.payload.entities[0].value;
),并将这些词传递到fuse
中以执行搜索:
fuse.search(searchTerm)
理想情况下,这些搜索词会被传递到您的应用,以播放正确的媒体。
您不需要实现Fuse即可在应用中执行搜索。您可以实现自己的(有可能是更稳健的)逻辑来执行内容搜索。
2020-03-18T04:28:49 b9e15474 INFO Search Term: 钢铁侠2
该搜索词是通过directive.payload.entities[0].value;
从传入的Alexa指令中拉取的。
2020-03-18T04:28:49 b9e15474 INFO Search Results:
{
"item": {
"id": 3,
"movie_id": "IM2",
"title": "钢铁侠2",
"genre": "action",
"description": "Tony Stark and his magic war suit do more cool stuff",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.0005
}
,
{
"item": {
"id": 2,
"movie_id": "IM1",
"title": "钢铁侠1",
"genre": "action",
"description": "Tony Stark and his magic war suit",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.05
}
,
{
"item": {
"id": 201,
"movie_id": "BA1_1",
"episode": 1,
"genre": "comedy",
"title": "Black Adder Season 1 Ep 1",
"description": "Rowan Atkinson is pretty much the funniest man alive in this debut for the Black Adder series",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.45
}
,
{
"item": {
"id": 202,
"movie_id": "BA1_2",
"episode": 2,
"genre": "comedy",
"title": "Black Adder Season 1 Ep 2",
"description": "Rowan Atkinson continues his role as the esteemed and ill-fated Black Adder",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.45
}
]
searchTerm
被传递到fuse
并存储在searchResults
中,searchResults通过以下方式记录至控制台:
var searchResults = fuse.search(searchTerm)
JSON.stringify
函数。否则您将改为看到所记录的是[object Object]
。2020-03-18T04:28:49 b9e15474 INFO ADM Registration ID
ADM注册ID被记录到控制台中。
2020-03-18T04:28:49 b9e15474 INFO Got Access Token Type:bearer Expires In:3600…
ADM使用您的凭据获取访问令牌。如果它成功,Lambda函数会调用sendMessageToDevice()
函数。如果未成功,则不授予任何访问令牌。
2020-03-18T04:28:49 b9e15474 INFO Calling device…
ADM调用sendMessageToDevice()
函数将指令发送到您的应用,通过API键来标识该指令。ADM使用请求模块发送指令。您可以在请求访问令牌中阅读有关ADM文档头部的更多信息。
2020-03-18T04:28:49 b9e15474 INFO Sending message to app:
Lambda通过ADM将消息发送至Fire TV应用。JSON被转义。
"{\"directive\":{\"payload\":{\"entities\":[{\"type\":\"Video\",\"uri\":\"entity://provider/program/amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6\",\"value\":\"Iron Man 2\",\"externalIds\":{\"ENTITY_ID\":\"amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6\",\"tms\":\"MV002527780000\"}},{\"type\":\"Video\",\"uri\":\"entity://provider/program/amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903\",\"value\":\"Iron Man 2\",\"externalIds\":{\"ENTITY_ID\":\"amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903\"}},{\"type\":\"Video\",\"uri\":\"entity://provider/program/amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238\",\"value\":\"Iron Man 2\",\"externalIds\":{\"ENTITY_ID\":\"amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238\"}},{\"type\":\"Video\",\"uri\":\"entity://provider/program/amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef\",\"value\":\"Iron Man 2\",\"externalIds\":{\"ENTITY_ID\":\"amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef\"}},{\"type\":\"Video\",\"uri\":\"entity://provider/program/amzn1.p11cat.merged-video.80a7eda2-3e16-55de-b56b-3136f5dc0e74\",\"value\":\"Iron Man 2\",\"externalIds\":{\"ENTITY_ID\":\"amzn1.p11cat.merged-video.80a7eda2-3e16-55de-b56b-3136f5dc0e74\",\"tms\":\"MV006154660000\"}}],\"searchText\":{\"transcribed\":\"iron man two\"},\"searchResults\":[{\"item\":{\"id\":3,\"movie_id\":\"IM2\",\"title\":\"Iron Man 2\",\"genre\":\"action\",\"description\":\"Tony Stark and his magic war suit do more cool stuff\",\"video_file\":\"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4\"},\"score\":0.0005},{\"item\":{\"id\":2,\"movie_id\":\"IM1\",\"title\":\"Iron Man 1\",\"genre\":\"action\",\"description\":\"Tony Stark and his magic war suit\",\"video_file\":\"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4\"},\"score\":0.05},{\"item\":{\"id\":201,\"movie_id\":\"BA1_1\",\"episode\":1,\"genre\":\"comedy\",\"title\":\"Black Adder Season 1 Ep 1\",\"description\":\"Rowan Atkinson is pretty much the funniest man alive in this debut for the Black Adder series\",\"video_file\":\"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4\"},\"score\":0.45},{\"item\":{\"id\":202,\"movie_id\":\"BA1_2\",\"episode\":2,\"genre\":\"comedy\",\"title\":\"Black Adder Season 1 Ep 2\",\"description\":\"Rowan Atkinson continues his role as the esteemed and ill-fated Black Adder\",\"video_file\":\"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4\"},\"score\":0.45}]},\"header\":{\"payloadVersion\":\"3\",\"messageId\":\"f5cbe32c-72fc-4816-892d-92f695c125a4\",\"namespace\":\"Alexa.RemoteVideoPlayer\",\"name\":\"SearchAndDisplayResults\",\"correlationToken\":\"7e8bdcef-1aff-452e-b405-5a877d712755\"},\"endpoint\":{\"endpointId\":\"432521e3d404c9e3##amzn1-ask-skill-c7dc9021-ba50-4aa9-bbad-7b9bcd2e94c3##development##com-tomjoht-vskfiretv\",\"cookie\":{\"deviceType\":\"AKPGW064GI9HE\",\"VSKClientVe
如果您对这个JSON进行解压缩和优化,它会类似以下所示:
{
"directive": {
"payload": {
"entities": [
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6",
"tms": "MV002527780000"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.80a7eda2-3e16-55de-b56b-3136f5dc0e74",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.80a7eda2-3e16-55de-b56b-3136f5dc0e74",
"tms": "MV006154660000"
}
}
],
"searchText": {
"transcribed": "iron man two"
},
"searchResults": [
{
"item": {
"id": 3,
"movie_id": "IM2",
"title": "钢铁侠2",
"genre": "action",
"description": "Tony Stark and his magic war suit do more cool stuff",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.0005
},
{
"item": {
"id": 2,
"movie_id": "IM1",
"title": "钢铁侠1",
"genre": "action",
"description": "Tony Stark and his magic war suit",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.05
},
{
"item": {
"id": 201,
"movie_id": "BA1_1",
"episode": 1,
"genre": "comedy",
"title": "Black Adder Season 1 Ep 1",
"description": "Rowan Atkinson is pretty much the funniest man alive in this debut for the Black Adder series",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.45
},
{
"item": {
"id": 202,
"movie_id": "BA1_2",
"episode": 2,
"genre": "comedy",
"title": "Black Adder Season 1 Ep 2",
"description": "Rowan Atkinson continues his role as the esteemed and ill-fated Black Adder",
"video_file": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/1080/Big_Buck_Bunny_1080_10s_10MB.mp4"
},
"score": 0.45
}
]
},
"header": {
"payloadVersion": "3",
"messageId": "f5cbe32c-72fc-4816-892d-92f695c125a4",
"namespace": "Alexa.RemoteVideoPlayer",
"name": "SearchAndDisplayResults",
"correlationToken": "7e8bdcef-1aff-452e-b405-5a877d712755"
},
"endpoint": {
"endpointId": "432521e3d404c9e3##amzn1-ask-skill-c7dc9021-ba50-4aa9-bbad-7b9bcd2e94c3##development##com-tomjoht-vskfiretv",
"cookie": {
"deviceType": "AKPGW064GI9HE",
"VSKClientVersion": "1.4.6",
"appPackageName": "com.tomjoht.vskfiretv",
"deviceId": "G070VM1694861D9N",
"appName": "VSKFireTV",
"applicationInstanceId": "amzn1.adm-registration.v3.Y29tLmFtYXpvbi5EZXZpY2VNZXNzYWdpbmcuUmVnaXN0cmF0aW9uSWRFbmNyeXB0aW9uS2V5ITEheFgvdzhUODl1SGc0WU96N0svSlZCVWFXVnBqUkNhdnJuWHlXWFBucUtvMHVLd0g2M1lkVEhZcjNRZ3J2K3A5QVAzdzhYbEkzZGYrY0t2Q0NQSWVQSEtEMDEvbGRhYUgrZVY4b1JRTXFMQndpYUZXajRXZlN3MEwzQ2ZaWkh2d0tUd3c1cFlYKys5WFdxZkVtRkg4RTQzcXhDMmxJYkZmd3dJeVVObStnWU00VU9GVmVwQTBVYityTFRRZHlYanNGUy9RTEhDbDhMUm1URmovZldsazF6WGRnZUV1V1pGWjkrcXYzR0lRcFV1MlpSYS9yZFA0aDEwbU5EcG1ndDE4eVJPRUg1bUtIQWVpUkZ3UlNwSlljcG9XSE96R3ZtMWJvZys2U1QyWUxvM3l0Nk4xNWI0QmFvMjBLcjNuZEl5UHpvNFBSUUs5aHZ1VUdTZ1psdUlFeVVsdzBZajFnb1ZZKzBLL3NGN2pJQnJjPSFmdk1qcTFuM0tOcVBOVzdyOGhybkp3PT0"
},
"scope": {
"token": null,
"type": "BearerToken"
}
}
}
}
在Android Studio中打开Logcat并筛选您的搜索请求(可以键入部分匹配项,如“iron”,也可以键入完整的关键词)。您将能够看到您的应用收到的此指令:
有关应用如何处理传入指令的详细信息,请参阅VSKFireTVMessageHandler.java
类。例如,第113至131行包含以下用于播放视频的逻辑:
if (directive != null) {
String directiveName = directive.getDirective().getHeader().getName();
if ("SearchAndPlay".equals(directiveName)) {
Movie firstMovie = MovieList.getList().get(0);
String movieName = firstMovie.getTitle();
Log.d(TAG, "Playing MOVIE " + movieName);
//为了便于演示,抓取了电影列表中的第一项,没有和电影ID对应
Movie someMovie = MovieList.getList().get(0);
Intent playIntent = new Intent();
String packageName = AlexaClientManager.getSharedInstance().getApplicationContext().getPackageName();
playIntent.setClassName(packageName, packageName + ".PlaybackActivity");
playIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//PlaybackActivity要求当前选择一部电影,如果没有选择,请立即设置
playIntent.putExtra(DetailsActivity.MOVIE, someMovie);
AlexaClientManager.getSharedInstance().getApplicationContext().startActivity(playIntent);
} else if ("SearchAndDisplayResults".equals(directiveName)) {
String searchTerm = directive.getDirective().getPayload().getEntities().get(0).getValue().toLowerCase();
Log.d(TAG, "Searching for: " + searchTerm);
String searchPayload = "";
...
}
... }
正如您所见,在这个示例应用中,响应仅从MovieList
中获取第一个电影标题,如Movie firstMovie = MovieList.getList().get(0)
中所示。您可以在应用中查看MovieList
类,以了解有关逻辑的更多详细信息。示例应用的数据量有限,因此这里仅对一些响应进行了编码。在您的实现中,通常您会将搜索与一个全面的云数据库连接。
2020-03-18T04:28:49 b9e15474 INFO Response from the app:
{
"registrationID": "amzn1.adm-registration.v3.Y29tLmFtYXpvbi...WVFnbXppczc0K0dWb2pnRklBQjNvdEhJV0F1cjNZTHZrakkvS09JPSE2czc5UVFhYklEN01BT1R5dVJNZDlnPT0"
}
应用使用注册ID进行响应,以确认通信。(为了便于阅读,此处截断了长字符串。) 您的应用不需要在此处发送注册ID;它可以发送您需要的任何内容。
2020-03-18T04:28:49 b9e15474 INFO Sending SearchAndDisplayResults response back to Alexa:
{
"event": {
"header": {
"messageId": "not-required",
"correlationToken": "not-required",
"name": "Response",
"namespace": "Alexa",
"payloadVersion": "3"
},
"endpoint": {
"scope": {
"type": "DirectedUserId",
"directedUserId": "not-required"
},
"endpointId": "not-required"
},
"payload": {}
}
}
您的Lambda函数会向Alexa发回一个“success”(成功)响应,表明Lambda已收到指令。您可以在SearchAndDisplayResults
API的响应示例中阅读有关此预期响应的更多信息。
END RequestId: b9e15474
该指令具有一个如这里所示的请求ID。交互完成。
REPORT RequestId: b9e15474 Duration: 882.12 ms计费持续时间: 900 ms内存大小: 128MB内存最大使用量: 105 MB
指令花费了这里所示的时间和内存来进行处理。这是您运行此进程的计费持续时间。大多数具有视频技能的Fire TV应用使用的内存很少超过免费套餐已经提供的内存: “每月100万个请求,每月400,000GB的计算时间。” 有关更多详细信息,请参阅AWS Lambda定价。
SearchAndPlay指令的工作流程
上面详细的Cloudwatch演练使用了SearchAndDisplayResults
指令。如果您说出“Play Iron Man 2”(播放Iron Man 2),这样会发送SearchAndPlay
指令。
工作流程将相同,但是“从Alexa收到的指令: SearchAndPlay”部分将显示以下内容:
{
"directive": {
"payload": {
"entities": [
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.ae637959-7f3c-5c5e-8baf-ce730ed9beb6",
"tms": "MV002527780000"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.b4e77dca-954a-555d-ba2b-da5e0ca9e903"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.0e3a1fb7-b973-52c0-98ef-cc4765fb9238"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.a13bd1b9-b2f6-5c15-b5a5-387f24836ba3",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.a13bd1b9-b2f6-5c15-b5a5-387f24836ba3"
}
},
{
"type": "Video",
"uri": "entity://provider/program/amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef",
"value": "钢铁侠2",
"externalIds": {
"ENTITY_ID": "amzn1.p11cat.merged-video.6beb3014-436d-5138-b0c0-bcf819cfadef"
}
}
],
"searchText": {
"transcribed": "iron man two"
}
},
"header": {
"payloadVersion": "3",
"messageId": "53b4f99d-b8e4-44aa-b32f-095589f13987",
"namespace": "Alexa.RemoteVideoPlayer",
"name": "SearchAndPlay",
"correlationToken": "e114fc4d-098a-47ad-8c54-94fbd693871e"
},
"endpoint": {
"endpointId": "432521e3d404c9e3##amzn1-ask-skill-c7dc9021-ba50-4aa9-bbad-7b9bcd2e94c3##development##com-tomjoht-vskfiretv",
"cookie": {
"deviceType": "AKPGW064GI9HE",
"VSKClientVersion": "1.4.6",
"appPackageName": "com.tomjoht.vskfiretv",
"deviceId": "G070VM1694861D9N",
"appName": "VSKFireTV",
"applicationInstanceId": "amzn1.adm-registration.v3.Y29tLmFtYXpvbi5EZXZpY2VNZXNzYWdpbmcuUmVnaXN0cmF0aW9uSWRFbmNyeXB0aW9uS2V5ITEheFgvdzhUODl1SGc0WU96N0svSlZCVWFXVnBqUkNhdnJuWHlXWFBucUtvMHVLd0g2M1lkVEhZcjNRZ3J2K3A5QVAzdzhYbEkzZGYrY0t2Q0NQSWVQSEtEMDEvbGRhYUgrZVY4b1JRTXFMQndpYUZXajRXZlN3MEwzQ2ZaWkh2d0tUd3c1cFlYKys5WFdxZkVtRkg4RTQzcXhDMmxJYkZmd3dJeVVObStnWU00VU9GVmVwQTBVYityTFRRZHlYanNGUy9RTEhDbDhMUm1URmovZldsazF6WGRnZUV1V1pGWjkrcXYzR0lRcFV1MlpSYS9yZFA0aDEwbU5EcG1ndDE4eVJPRUg1bUtIQWVpUkZ3UlNwSlljcG9XSE96R3ZtMWJvZys2U1QyWUxvM3l0Nk4xNWI0QmFvMjBLcjNuZEl5UHpvNFBSUUs5aHZ1VUdTZ1psdUlFeVVsdzBZajFnb1ZZKzBLL3NGN2pJQnJjPSFmdk1qcTFuM0tOcVBOVzdyOGhybkp3PT0"
},
"scope": {
"token": null,
"type": "BearerToken"
}
}
}
}
示例应用的指令
现在您已经了解如何读取CloudWatch日志(对于示例Lambda记录的信息),现在是时候运行一些测试了。在该部分中,您将把四种不同的表述保存到Alexa中,并观察应用的行为以及CloudWatch中记录的内容。因为您的应用还没有显式确定目标,所以在说出每个表述之前,您需要启动示例应用,将其移动到前台(确保它在Fire TV上主动显示)。
示例应用能够处理四个指令。请记住,在这个示例Lambda代码中,Lambda只传递它从Alexa接收的同一指令。然后,该应用查询负载并在其目录中查找媒体。
您可以用任何对您的应用有意义的进程来对您的Lambda编码。例如,作为一种替代工作流程,您的Lambda可以自行执行一些处理,以定位正确的媒体标识符,然后根据需要将其发送到您的应用。
SearchAndDisplayResults指令
示例表述: 启动示例应用。然后说出“Alexa,查找Iron Man 2”。
响应: “从 [视频技能名称] 获取《钢铁侠2》”。应用进入搜索屏幕,搜索文本“iron man 2”。
Alexa发送至您的Lambda的指令: SearchAndDisplayResults
。
SearchAndPlay指令
示例表述: 启动示例应用。然后说出“Alexa, watch Iron Man 2”(Alexa,观看Iron Man 2)。
响应: “Getting Iron Man 2 from [video skill name]”(从 [视频技能名称] 获取Iron Man 2)。 然后播放来自应用的测试视频。
Alexa发送至您的Lambda的指令: SearchAndPlay
PlaybackController - 暂停指令
示例表述: 启动示例应用(如果尚未在前台)并播放其中一个视频。然后说出“Alexa,暂停”。
响应: Alexa没有回应什么,但视频暂停了。
Alexa发送至您的Lambda的指令: PlaybackController: Pause
PlaybackController - 播放指令
示例表述: 在上一个场景中暂停示例视频后,说出:“Alexa, play”(Alexa,播放)。
响应: Alexa没有回应什么,但视频恢复播放。
Alexa发送至您的Lambda的指令: PlaybackController: Play
故障排除
如果遇到问题,请参阅下面的故障排除提示。
- “Something went wrong”(出现了错误)
- 您的Lambda语法可能无效。
- Cloudwatch中未显示任何日志
- 确保您选择了正确的AWS地区。
- 抱歉,我不明白这个…
- Alexa不能理解命令。
[object Object]
出现在Cloudwatch日志中。- 您没有使用
JSON.Stringify
函数来呈现JSON。
后续步骤
现在,您已经了解Alexa指令和Lambda函数的工作原理,现在就可以为自己的内容和服务定制Lambda了。继续执行步骤9: 解释指令并对指令做出反应。
(如果遇到任何问题而无法继续,请参阅云端集成故障排除。)
Last updated: 2021年10月13日