TemplateRuntime接口对具有屏显与图形介面的TVS设备提供了视觉元数据。这些元数据中的展示卡片 (display cards) 是用来描述或增强用户的语音交互体验的。这些元数据会以结构化的JSON形式提供,并且其结构应该以如下所述的各个模版形式相同。
数据流
下图以比较高的层次说明了精灵服务是如何向接入精灵的设备下发视觉元数据的信息流:
- 用户问:「谁是马云?」其音频语音会被设备捕捉并传送给TVS。
- TVS返回两个指令:
- 一个告知客户端应该播放TTS的
Speak
指令。
- 一个用来要求客户端用来展示视觉元数据的
TemplateRuntime
。在此例中,就是马云的信息。
- 精灵服务的TTS开始播放。
- 客户端立刻渲染
RenderTemplate
指令中的数据 (如果可以的话,与 Speak
在不同线程中执行)。
- 客户端上报
SpeechStarted
事件来告知TVS已经开始播放TTS。
- 当TTS播报完毕时,向TVS上报
SpeechFinished
事件。
渲染视觉元数据
除了在交互模型中所提供的规范外,你的产品在渲染视觉元数据时也必须遵守以下准则:
- 与发送请求的线程中读取回复并解析指令:
- 必须立刻在一个新的线程中执行不带有
dialogRequestId
的指令。
- 必须立刻在一个新的线程中执行
RenderTemplate
指令。
- 将带有
dialogRequestId
的指令放置到指令队列之中。
- 队列中的指令应该由另一个线程取出并依顺序处理。
Play
指令及与其关连的 RenderPlayerInfo
指令的处理必须同步。与 RenderTemplate
不同,RenderTemplate
指令不需要总是立刻进行渲染。例如,在发送一个 PlaybackNearlyFinished
事件之后,如果你收到了新的 Play
指令和 RenderPlayerInfo
指令,则两者都必须加入队列中,且需要在当前音频播放完毕之后处理。这也代表展示卡片的实现必须要对播控状态有所感知,例如正在播放、停止或暂停等等。
RenderTemplate指令
以下是目前支持的templates:
类型 |
描述 |
用例 |
BodyTemplate1 |
仅支持纯文本的template。支持显示的内容有标题、子标题、文本内容与技能图示。 |
没有图片的百科条目, ASK simple cards |
BodyTemplate2 |
用以支持内容文本与单张图片的template。 |
有图片的百科条目 |
ListTemplate1 |
用以显示列表与日历条目的template。 |
购物清单、待办事项、日历 |
WeatherTemplate |
设计来用以显示天气数据的template。 |
设计来用以显示天气数据的template |
LocalSearchListTemplate1 |
用以显示地址搜索列表template |
地址信息 |
BodyTemplate1
BodyTemplate1
用于渲染纯文本的可视数据。下面是一个百科的示例:
Sample Message
{
"directive": {
"header": {
"namespace": "TemplateRuntime",
"name": "RenderTemplate",
"messageId": "{{STRING}}",
"dialogRequestId": "{{STRING}}"
},
"payload": {
"token": "{{STRING}}",
"type": "BodyTemplate1",
"title": {
"mainTitle": "{{STRING}}",
"subTitle": "{{STRING}}"
},
"skillIcon": {{IMAGE STRUCTURE}},
"textField": "{{STRING}}"
}
}
}
Header参数
参数 |
描述 |
型态 |
messageId |
用以代表一个特定message的唯一ID。 |
字串 |
dialogRequestId |
用以将指令与一个特定 Recognize 事件关连的唯一ID。 |
字串 |
Payload参数
参数 |
描述 |
型态 |
token |
|
字串 |
type |
BodyTemplate1 |
字串 |
title |
包含标题信息的键/值对,例如标题和副标题。实际的键/值对因模板而异。 |
对象 |
title.mainTitle |
|
字串 |
title.subTitle |
不一定有值 |
字串 |
skillIcon |
|
image structure |
textField |
对于此卡片的文本内容 |
字串 |
BodyTemplate2
BodyTemplate2
用于渲染主体文本和单个图像。
Sample Message
{
"directive": {
"header": {
"namespace": "TemplateRuntime",
"name": "RenderTemplate",
"messageId": "{{STRING}}",
"dialogRequestId": "{{STRING}}"
},
"payload": {
"token": "{{STRING}}",
"type": "BodyTemplate2",
"title": {
"mainTitle": "{{STRING}}",
"subTitle": "{{STRING}}"
},
"skillIcon": {{IMAGE STRUCTURE}},
"textField": "{{STRING}}",
"image": {{IMAGE_STRUCTURE}}
}
}
}
Header参数
参数 |
描述 |
型态 |
messageId |
用以代表一个特定message的唯一ID。 |
字串 |
dialogRequestId |
用以将指令与一个特定 Recognize 事件关连的唯一ID。 |
字串 |
Payload参数
参数 |
描述 |
型态 |
token |
|
字串 |
type |
BodyTemplate2 |
字串 |
title |
|
对象 |
title.mainTitle |
|
字串 |
title.subTitle |
|
字串 |
skillIcon |
|
image structure |
xtField |
对于此卡片的文本内容 |
字串 |
image |
向右对齐的内容图片 |
image structure |
ListTemplate1
ListTemplate1
用于渲染列表(待办事项列表、购物列表)和日历条目。
Sample Message
{
"directive": {
"header": {
"namespace": "TemplateRuntime",
"name": "RenderTemplate",
"messageId": "{{STRING}}",
"dialogRequestId": "{{STRING}}"
},
"payload": {
"token": "{{STRING}}",
"type": "ListTemplate1",
"title": {
"mainTitle": "{{STRING}}",
"subTitle": "{{STRING}}"
},
"skillIcon": {{IMAGE_STRUCTURE}},
"listItems": [
{
"leftTextField": "{{STRING}}",
"rightTextField": "{{STRING}}"
},
{
"leftTextField": "{{STRING}}",
"rightTextField": "{{STRING}}"
},
{
...
}
]
}
}
}
Header参数
参数 |
描述 |
型态 |
messageId |
用以代表一个特定message的唯一ID。 |
字串 |
dialogRequestId |
用以将指令与一个特定 Recognize 事件关连的唯一ID。 |
字串 |
Payload参数
参数 |
描述 |
型态 |
token |
|
字串 |
type |
ListTemplate1 |
字串 |
title |
|
对象 |
title.mainTitle |
|
字串 |
title.subTitle |
|
字串 |
skillIcon |
|
image structure |
listItems |
对于此卡片的文本内容 |
list |
listItems.leftTextField |
左对齐文本栏位内容 |
字串 |
listItems.rightTextField |
右对齐文本栏位内容 |
字串 |
WeatherTemplate
WeatherTemplate
是一个渲染天气预报的模板.
Sample Message
{
"directive": {
"header": {
"namespace": "TemplateRuntime",
"name": "RenderTemplate",
"messageId": "{{STRING}}",
"dialogRequestId": "{{STRING}}"
},
"payload": {
"token": "{{STRING}}",
"type": "WeatherTemplate",
"title": {
"mainTitle": "{{STRING}}",
"subTitle": "{{STRING}}"
},
"skillIcon": {{IMAGE_STRUCTURE}},
"currentWeather": "{{STRING}}",
"description": "{{STRING}}",
"currentWeatherIcon": {{IMAGE_STRUCTURE}},
"highTemperature": {
"value": "{{STRING}}",
"arrow": {{IMAGE_STRUCTURE}}
},
"lowTemperature": {
"value": "{{STRING}}",
"arrow": {{IMAGE_STRUCTURE}}
},
"weatherForecast": [
{
"image": {{IMAGE_STRUCTURE}},
"day": "{{STRING}}",
"date": "{{STRING}}",
"highTemperature": "{{STRING}}",
"lowTemperature": "{{STRING}}"
},
{
"image": {{IMAGE_STRUCTURE}},
"day": "{{STRING}}",
"date": "{{STRING}}",
"highTemperature": "{{STRING}}",
"lowTemperature": "{{STRING}}"
},
{
...
}
]
}
}
}
Header参数
参数 |
描述 |
型态 |
messageId |
用以代表一个特定message的唯一ID。 |
字串 |
dialogRequestId |
用以将指令与一个特定 Recognize 事件关连的唯一ID。 |
字串 |
Payload参数
参数 |
描述 |
型态 |
token |
|
字串 |
type |
WeatherTemplate |
字串 |
title |
|
对象 |
title.mainTitle |
|
字串 |
title.subTitle |
|
字串 |
skillIcon |
|
image structure |
currentWeather |
|
字串 |
description |
|
字串 |
currentWeatherIcon |
|
image structure |
highTemperature |
|
对象 |
highTemperature.value |
|
字串 |
highTemperature.arrow |
|
image structure |
lowTemperature |
|
对象 |
lowTemperature.value |
|
字串 |
lowTemperature.arrow |
|
image structure |
weatherForecast |
|
list |
weatherForecast.image |
|
image structure |
weatherForecast.day |
|
字串 |
weatherForecast.date |
|
字串 |
weatherForecast.highTemperature |
|
字串 |
weatherForecast.lowTemperature |
|
字串 |
LocalSearchListTemplate1
ExtCommonExceptionTemplate
用于查询周边地址的信息的渲染
Sample Message
{
"directive": {
"header": {
"namespace": "TemplateRuntime",
"name": "RenderTemplate",
"messageId": "{{STRING}}",
"dialogRequestId": "{{STRING}}"
},
"payload": {
"token": "{{STRING}}",
"type": "LocalSearchListTemplate1",
"listItems": [{
"rightPrimaryTextField": "{{STRING}}",
"leftTextField": "{{STRING}}",
"rightSecondaryTextField": "{{STRING}}",
"onClickAction": [{
"directive": {
"header": {
"namespace": "Navigation",
"name": "SetDestination"
},
"payload": {
"metadata": {
"rating": "{{STRING}}",
"cost": "{{STRING}}",
"phoneNumber": "{{STRING}}"
},
"destination": {
"name": "{{STRING}}",
"coordinate": {
"longitudeInDegrees": {{DOUBLE}},
"latitudeInDegrees": {{DOUBLE}}
},
"singleLineDisplayAddress": "{{STRING}}",
"province": "{{STRING}}",
"city": "{{STRING}}",
"county": "{{STRING}}"
},
}
}
}]
}],
"title": {
"mainTitle": "{{STRING}}",
"subTitle": "{{STRING}}"
}
}
}
}
Header参数
参数 |
描述 |
型态 |
messageId |
用以代表一个特定message的唯一ID。 |
字串 |
dialogRequestId |
用以将指令与一个特定 Recognize 事件关连的唯一ID。 |
字串 |
Payload参数
参数 |
描述 |
型态 |
token |
|
字串 |
type |
LocalSearchListTemplate1 |
字串 |
title |
|
对象 |
title.mainTitle |
|
字串 |
title.subTitle |
|
字串 |
listItems |
搜索信息结果 |
list |
listItems.rightPrimaryTextField |
店铺名 |
字串 |
listItems.leftTextField |
距离 |
字串 |
listItems.rightSecondaryTextField |
店铺详细地址 |
字串 |
listItems.onClickAction |
可选择的商店信息(字段参数再onClickAction表中) |
list |
onClickAction参数
参数 |
描述 |
型态 |
directive |
|
对象 |
directive.header |
|
对象 |
directive.header.namespace |
Navigation |
字串 |
directive.header.name |
SetDestination |
字串 |
directive.payload |
|
对象 |
directive.payload.metadata |
店铺的信息 |
对象 |
directive.payload.metadata.rating |
店铺的评分 |
字串 |
directive.payload.metadata.cost |
店铺的人均消费 |
字串 |
directive.payload.metadata.phoneNumber |
店铺的电话号码 |
字串 |
directive.payload.destination |
|
对象 |
directive.payload.destination.name |
店铺名 |
字串 |
directive.payload.destination.coordinate |
坐标 |
对象 |
directive.payload.destination.coordinate.longitudeInDegrees |
经度 |
double |
directive.payload.destination.coordinate.latitudeInDegrees |
纬度 |
double |
directive.payload.destination.singleLineDisplayAddress |
详细地址 |
字串 |
directive.payload.destination.province |
省份 |
字串 |
directive.payload.destination.city |
城市 |
字串 |
directive.payload.destination.county |
区/县 |
字串 |
RenderPlayerInfo指令
AVS官方文档链结: https://developer.amazon.com/de/docs/alexa-voice-service/templateruntime.html#renderplayerinfo
RenderPlayerInfo
指令要求客户端展示关于某个media item的相关视觉元数据,例如一首歌或一个播放清单。除了发送Play指令外,TVS还将发送带有特定于音频内容提供者的可视元数据的RenderPlayerInfo
指令,您的客户端会将其绑定到模板并为最终用户呈现。
Sample Message
{
"directive": {
"header": {
"namespace": "TemplateRuntime",
"name": "RenderPlayerInfo",
"messageId": "{{STRING}}",
"dialogRequestId": "{{STRING}}"
},
"payload": {
"audioItemId": "{{STRING}}",
"content": {
"title": "{{STRING}}",
"titleSubtext1": "{{STRING}}",
"titleSubtext2": "{{STRING}}",
"header": "{{STRING}}",
"mediaLengthInMilliseconds": {{LONG}},
"art": {
"sources":[
{
"url": "{{STRING}}"
}
]
},
"provider": {
"name": "{{STRING}}",
"logo": {{IMAGE_STRUCTURE}}
}
}
"controls": [
// This array includes all controls that must be
// rendered on-screen.
{
"type": "{{STRING}}",
"name": "{{STRING}}",
"enabled": {{BOOLEAN}},
"selected": {{BOOLEAN}}
},
{
"type": "{{STRING}}",
"name": "{{STRING}}",
"enabled": {{BOOLEAN}},
"selected": {{BOOLEAN}}
},
{
...
}
]
}
}
}
Header参数
参数 |
描述 |
型态 |
messageId |
用以代表一个特定message的唯一ID。 |
字串 |
dialogRequestId |
用以将指令与一个特定 Recognize 事件关连的唯一ID。 |
字串 |
Payload参数
参数 |
描述 |
型态 |
audioItemId |
用以识别audioItem的ID。此参数应该用以关连所提供的视觉元数据与 play 指令。 |
字串 |
content |
Contains key/value pairs for title information, such as title and subtitle. Actual key/value pairs vary by template. |
对象 |
content.title |
标题 |
字串 |
content.titleSubtext1 |
第一文字栏位 |
字串 |
content.titleSubtext2 |
第二文字栏位 |
字串 |
content.header |
头标第一文字栏位 |
字串 |
content.mediaLengthInMilliseconds |
以毫秒为单位的流长度 |
长整数 |
content.art |
针对该音频的美术内容 |
image structure |
content.art.sources |
针对该音频的美术内容 |
image structure |
content.art |
针对该音频的美术内容 |
image structure |
content.provider |
包含关于内容提供者的信息 |
对象 |
content.provider.name |
内容提供者名称(不一定有值) |
字串 |
content.provider.logo |
内容提供者的logo(不一定有值) |
image structure |
controls |
必须要显示在屏幕上的按钮列表,所有在这个数组中出现的控制项都必须渲染在画面上。 |
list |
controls.type |
用以识别控制项类型。允许值:BUTTON |
字串 |
controls.name |
控制项名称。允许值:PLAY_PAUSE, NEXT , PREVIOUS |
字串 |
controls.enabled |
告知客户端此控制项是否可点击,当可点击时,其值必须为true |
boolean |
controls.selected |
指明一个控制项是否应该渲染为被选择态。例如:如果用户喜爱了一首歌,当这首歌播放时用以表示是否喜爱的控制项的这个值必须是true。 |
boolean |
Control参数
参数 |
描述 |
型态 |
controls |
必须要显示在屏幕上的按钮列表,所有在这个数组中出现的控制项都必须渲染在画面上。 |
list |
controls.type |
用以识别控制项类型。允许值:BUTTON |
字串 |
controls.name |
控制项名称。例如PLAY_PAUSE, NEXT , PREVIOUS 等,不同的渲染指令支持的name不同 |
字串 |
controls.enabled |
告知客户端此控制项是否可点击,当可点击时,其值必须为true |
boolean |
controls.selected |
指明一个控制项是否应该渲染为被选择态。例如:如果用户喜爱了一首歌,当这首歌播放时用以表示是否喜爱的控制项的这个值必须是true。 |
boolean |
控制项与事件的对映
当用户与屏显上的控制项进行交互时,与控制项对应的事件必须使用 PlaybackController
接口发送给精灵服务。下表是控制项与 PlaybackController
接口中对应的事件:
控制项类型 |
控制项名称 |
事件 |
补充 |
BUTTON |
PLAY_PAUSE |
PlayCommandIssued |
n/a |
BUTTON |
NEXT |
NextCommandIssued |
n/a |
BUTTON |
PREVIOUS |
PreviousCommandIssued |
n/a |
Image Structure
{
"sources": [
{
"url": "{{STRING}}",
"darkBackgroundUrl": "{{STRING}}"
},
{
...
},
{
...
}
]
}
参数 |
描述 |
选择 |
型态 |
sources |
同一图片的来源列表。它可能包含url 。重要的是要注意这是一个列表,可能有一个或多个来源。 |
可选 |
list |
sources[i].url |
图片URL。此字段总是存在。 |
必选 |
字串 |
sources[i].darkBackgroundUrl |
针对设备夜间模式的图片URL。这些图像通常较适用于深色背景。为可选参数(天气模板使用)。 |
可选 |
字串 |
通用按钮(扩展)
除了RenderPlayerInfo里面的按钮控件之外,TVS提供通用的按钮控件,TemplateRuntime接口返回的渲染指令可能包含一个或多个控件,客户端需要渲染这些控件,完成必要的前端交互功能,并需上报控件事件给GeneralController接口。
示例代码:
"controls": [
// This array includes all controls that must be
// rendered on-screen.
{
"type": "{{STRING}}",
"name": "{{STRING}}",
"enabled": {{BOOLEAN}},
"selected": {{BOOLEAN}}
},
{
"type": "{{STRING}}",
"name": "{{STRING}}",
"enabled": {{BOOLEAN}},
"selected": {{BOOLEAN}}
},
{
...
}
]
控制项与事件的对映
当用户与屏显上的控制项进行交互时,与控制项对应的事件必须使用 GeneralController
接口发送给精灵服务。下表是控制项与 GeneralController
接口中对应的事件:
控制项类型 |
控制项名称 |
事件 |
补充 |
BUTTON |
TAKEAWAY_BUY |
ExtButtonCommandIssued |
n/a |
BUTTON |
TAKEAWAY_PAY |
ExtButtonCommandIssued |
n/a |
FAQ
关于此文档暂时还没有FAQ