ごきげんよう
この記事は
「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」
の13日目の記事です。
ついにきましたね!
Echo Show!
というわけで、さっそく試してみるわけです。
#でかい
比較のために500mlの空き缶を添えてますがでかいです。
#動画とかすごい
映画を観ると特に感じますが、ブラウン管かよってレベルの大きさであるだけ音がすごい良いです。
これはもう今後Echo ShowでPrime VideoやYoutubeを観ます。
#スキルたちがより見やすくなる
#ヒロインの告白をAPL対応にしてみる
結果、テストバージョンでこうなってます。
環境構築から実装まで、偉大なる @zono_0 さんが既にわかりやすい記事を掲載しています。
いつもありがとうございます。
Alexa Presentation Language(APL)スキル 開発チュートリアル
上記、記事ではask cliが前提ですが、そうでなくてもAPLはもちろん使えます。
同じところは省いて、そうでないところを今回は補完していきます。
##1.Amazon Developer Portal
僕は今回開いた際にに右下にデバイス検出エラーがでました。
が、今回は気にしないで大丈夫です。
JSONのフォーマットが欲しいので。
ちなみに、ここで対象のデバイスの大きさを以下のように選べます。
##2.Lambdaでインラインエディタでも組める
ask cliは僕も使います。
というより、慣れるとそちらの方が開発は大変良いです。
コード管理とかもできますし。
かといって、ask cliの導入に躓く方もいらっしゃるとも思いますので、インラインでがんばってみました。
@zono_0 さんのように、APLのJSONは分割した方が良いと思います。
##3.ソースコード
とりあえず起動して動いた段階のソースコードです。
'use strict';
const Alexa = require('ask-sdk');
//起動時
const kotoha_smartmacchiato = '<audio src=\"https://s3XXXXXXXXXXXXXX.mp3\" />';
const kotoha_voice = '<audio src=\"https://s3-XXXXXXXXXXXXXX.mp3\" />';
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
async handle(handlerInput) {
// ディスプレイ有り(APL対応)の場合
if (supportsApl(handlerInput)) {
// APL対応(documentに設定したテンプレートレイアウトを利用し、datasourcesの内容をディスプレイに表示します。)
handlerInput.responseBuilder
.addDirective({
type : 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: require('./homepage.json'),
datasources: require('./data.json')
});
}
const speechText = kotoha_smartmacchiato + kotoha_voice;
return handlerInput.responseBuilder
.speak('<speak>' + speechText + '</speak>')
.reprompt('<speak>' + speechText + '</speak>')
.withShouldEndSession(false)
.getResponse();
}
};
/**
* ディスプレイサポート(APL対応)判定値
* @author zono_0 いつもありがとうございます!
*/
const supportsApl = (handlerInput) => {
const hasDisplay =
handlerInput.requestEnvelope.context &&
handlerInput.requestEnvelope.context.System &&
handlerInput.requestEnvelope.context.System.device &&
handlerInput.requestEnvelope.context.System.device.supportedInterfaces &&
handlerInput.requestEnvelope.context.System.device.supportedInterfaces['Alexa.Presentation.APL'];
return hasDisplay;
};
/**
* Echo Spotで使っていたディスプレイかどうかを判定するfunction
Echo Showでも引き続き使えますが、今回はAPLを使います
*/
function supportsDisplay(handlerInput) {
var hasDisplay =
handlerInput.requestEnvelope.context &&
handlerInput.requestEnvelope.context.System &&
handlerInput.requestEnvelope.context.System.device &&
handlerInput.requestEnvelope.context.System.device.supportedInterfaces &&
handlerInput.requestEnvelope.context.System.device.supportedInterfaces.Display;
console.log("Supported Interfaces are" + JSON.stringify(handlerInput.requestEnvelope.context.System.device.supportedInterfaces));
return hasDisplay;
}
//動かす
exports.handler = Alexa.SkillBuilders.standard()
.addRequestHandlers(LaunchRequestHandler)
.lambda();
{
"type": "APL",
"version": "1.0",
"theme": "dark",
"import": [
{
"name": "alexa-layouts",
"version": "1.0.0"
}
],
"resources": [
{
"description": "Stock color for the light theme",
"colors": {
"colorTextPrimary": "#151920"
}
},
{
"description": "Stock color for the dark theme",
"when": "${viewport.theme == 'dark'}",
"colors": {
"colorTextPrimary": "#f0f1ef"
}
},
{
"description": "Standard font sizes",
"dimensions": {
"textSizeBody": 48,
"textSizePrimary": 27,
"textSizeSecondary": 23,
"textSizeSecondaryHint": 25
}
},
{
"description": "Common spacing values",
"dimensions": {
"spacingThin": 6,
"spacingSmall": 12,
"spacingMedium": 24,
"spacingLarge": 48,
"spacingExtraLarge": 72
}
},
{
"description": "Common margins and padding",
"dimensions": {
"marginTop": 40,
"marginLeft": 60,
"marginRight": 60,
"marginBottom": 40
}
}
],
"styles": {
"textStyleBase": {
"description": "Base font description; set color and core font family",
"values": [
{
"color": "@colorTextPrimary",
"fontFamily": "Amazon Ember"
}
]
},
"textStyleBase0": {
"description": "Thin version of basic font",
"extend": "textStyleBase",
"values": {
"fontWeight": "100"
}
},
"textStyleBase1": {
"description": "Light version of basic font",
"extend": "textStyleBase",
"values": {
"fontWeight": "300"
}
},
"mixinBody": {
"values": {
"fontSize": "@textSizeBody"
}
},
"mixinPrimary": {
"values": {
"fontSize": "@textSizePrimary"
}
},
"mixinSecondary": {
"values": {
"fontSize": "@textSizeSecondary"
}
},
"textStylePrimary": {
"extend": [
"textStyleBase1",
"mixinPrimary"
]
},
"textStyleSecondary": {
"extend": [
"textStyleBase0",
"mixinSecondary"
]
},
"textStyleBody": {
"extend": [
"textStyleBase1",
"mixinBody"
]
},
"textStyleSecondaryHint": {
"values": {
"fontFamily": "Bookerly",
"fontStyle": "italic",
"fontSize": "@textSizeSecondaryHint",
"color": "@colorTextPrimary"
}
}
},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"when": "${viewport.shape == 'round'}",
"type": "Container",
"direction": "column",
"width": "100vw",
"height": "100vh",
"items": [
{
"type": "Image",
"source": "${payload.bodyTemplate3Data.image.sources[0].url}",
"scale": "best-fill",
"width": "100vw",
"height": "100vh",
"position": "absolute",
"overlayColor": "rgba(0, 0, 0, 0.6)"
},
{
"type": "ScrollView",
"width": "100vw",
"height": "100vh",
"item": [
{
"type": "Container",
"direction": "column",
"alignItems": "center",
"paddingLeft": 30,
"paddingRight": 30,
"paddingBottom": 200,
"items": [
{
"type": "AlexaHeader",
"headerAttributionImage": "${payload.bodyTemplate3Data.logoUrl}",
"headerTitle": "${payload.bodyTemplate3Data.title}"
},
{
"type": "Text",
"text": "<b>告白と言えば</b> | <b>やはり学校ですよね</b>",
"style": "textStylePrimary",
"color": "#4dd2ff",
"width": "90vw",
"textAlign": "center"
},
{
"type": "Text",
"text": "<b>${payload.bodyTemplate3Data.textContent.title.text}</b>",
"style": "textStyleBody",
"width": "90vw",
"textAlign": "center"
},
{
"type": "Text",
"text": "${payload.bodyTemplate3Data.textContent.subtitle.text}",
"style": "textStylePrimary",
"width": "90vw",
"textAlign": "center"
},
{
"type": "Text",
"text": "${payload.bodyTemplate3Data.textContent.primaryText.text}",
"paddingTop": 40,
"style": "textStylePrimary",
"width": "90vw",
"textAlign": "center"
},
{
"type": "Text",
"text": "${payload.bodyTemplate3Data.textContent.bulletPoint.text}",
"paddingTop": 50,
"style": "textStylePrimary",
"width": "90vw",
"textAlign": "center"
}
]
}
]
}
]
},
{
"type": "Container",
"width": "100vw",
"height": "100vh",
"items": [
{
"type": "Image",
"source": "${payload.bodyTemplate3Data.backgroundImage.sources[0].url}",
"scale": "best-fill",
"width": "100vw",
"height": "100vh",
"position": "absolute"
},
{
"type": "AlexaHeader",
"headerTitle": "${payload.bodyTemplate3Data.title}",
"headerAttributionImage": "${payload.bodyTemplate3Data.logoUrl}"
},
{
"type": "Container",
"direction": "row",
"paddingLeft": 40,
"paddingRight": 72,
"grow": 1,
"items": [
{
"type": "Image",
"source": "${payload.bodyTemplate3Data.image.sources[0].url}",
"width": 340,
"height": 360,
"scale": "best-fit",
"align": "center"
},
{
"type": "ScrollView",
"height": "60vh",
"shrink": 1,
"item": [
{
"type": "Container",
"items": [
{
"type": "Text",
"text": "<b>やはり告白と言えば学校ですよね</b>",
"style": "textStylePrimary",
"color": "#4dd2ff"
},
{
"type": "Text",
"text": "<b>${payload.bodyTemplate3Data.textContent.title.text}</b>",
"style": "textStyleBody"
},
{
"type": "Text",
"text": "${payload.bodyTemplate3Data.textContent.subtitle.text}",
"style": "textStylePrimary"
},
{
"type": "Text",
"text": "${payload.bodyTemplate3Data.textContent.primaryText.text}",
"paddingTop": 40,
"style": "textStylePrimary"
},
{
"type": "Text",
"text": "${payload.bodyTemplate3Data.textContent.bulletPoint.text}",
"paddingTop": 50,
"style": "textStylePrimary"
}
]
}
]
}
]
}
]
}
]
}
}
{
"bodyTemplate3Data": {
"type": "object",
"objectId": "bt3Sample",
"backgroundImage": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://s3-学校画像.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://s3-学校画像.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"title": "APLを使って Echo Show で学校で(?)告白されよう!",
"image": {
"contentDescription": null,
"smallSourceUrl": null,
"largeSourceUrl": null,
"sources": [
{
"url": "https://s3-結城琴葉画像.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://s3-結城琴葉画像.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"textContent": {
"title": {
"type": "PlainText",
"text": " ヒロイン(結城琴葉)の告白"
},
"subtitle": {
"type": "PlainText",
"text": " 告白メッセージ"
},
"primaryText": {
"type": "PlainText",
"text": " 私、あなたが好きです。世界中の誰よりも・・あなたのことが、本当に好きなんです!私と・・付き合ってください. "
},
"bulletPoint": {
"type": "PlainText",
"text": " 結城琴葉ちゃんから告白されてみよう! "
}
},
"logoUrl": "https://s3-スキルのロゴ.png",
"hintText": "アレクサ、「ヒロインの告白」を開いて"
}
}
#まとめ
ask cliで躓いても、なんとかなります。
現時点では僕はこれが実機で動いたレベルですが、APLを使いこなせるようになるとそれだけで職業になる気がします。
以上