开发者控制台

步骤8: 在CloudWatch中测试您的技能以及观察日志 (VSK Fire TV)

步骤8: 在CloudWatch中测试您的技能以及观察日志 (VSK Fire TV)

现在该测试您的技能并观察Alexa向您的Lambda函数发送的指令了。您将通过CloudWatch查看交互,它将列出Alexa发送至您的Lambda的指令。要查看Lambda发送到应用的响应,可以在Android Studio中打开Logcat。

通过ADB连接到Fire TV

此时您已准备好在Fire TV上运行您的应用,以确保一切正常并获得授权。

  1. 按照通过ADB连接到Fire TV中的说明将您的计算机连接至Fire TV。
  2. 打开Android Studio,选择app(应用)配置,然后选择要运行应用的Fire TV设备(例如“Amazon AFTMM”)。(“AFTMM”是指Fire TV构建型号。)

    Android Studio中的配置选择
    Android Studio中的配置选择
  3. 通过单击Run App(运行应用)按钮运行“app”(应用)来运行您的应用。

    如果您尚未选择Fire TV设备,系统将提示您选择设备。选择Fire TV设备,然后单击OK(确定)。

    选择目标Fire TV设备
    选择目标Fire TV设备

    在您的应用构建和运行时,单击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中查看日志,可执行以下操作:

  1. 在AWS中,单击Services(服务)并转到CloudWatch(搜索它)。
  2. 在左侧边栏中,转到Logs(日志)> Log groups(日志组)。
  3. 单击您的Lambda函数。

    如果您没有看到Lambda函数,请确保您位于正确的地区,因为仅与您所在地区相关的Lambda函数会出现。

  4. 将显示该函数的日志列表,其中最新的日志组位于顶部。(在您开始对Echo发出命令之前,日志不会显示在此处。)

如果在Lambda日志中,为了使日志更易于扫描,可以选择Text(文本)单选按钮。

请注意,您也可以通过转到Lambda函数来访问CloudWatch日志。单击Monitoring(监视)选项卡,然后单击View logs in CloudWatch(在CloudWatch中查看日志)按钮,如以下屏幕截图所示。

Lambda函数屏幕中的CloudWatch日志链接
Lambda函数屏幕中的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)

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”,也可以键入完整的关键词)。您将能够看到您的应用收到的此指令:

检查应用中的ADM消息处理程序,查看是否已收到指令。
检查您在应用中的ADM消息处理程序,查看是否已收到指令。筛选您搜索的媒体标题。

有关应用如何处理传入指令的详细信息,请参阅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日