Echo Show、Echo Spotの電源の切り方
この記事は ADVENTARの「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」 の14日目の記事です。
今回は明らかに小ネタです。
Echo Spotの時もハマったのですが、Echo Showで再度それをやることになりました。
けど、僕は完全に忘れていてハマったので記事にすることにしました。
電源の切り方がわからない
Echo Showのスタンドも買ってあって、それが昨日届きました。 これ、角度が結構調整できて大変嬉しいのですが、設置するときに(哲学的に?)壁に当たりました。
説明書が「電源ひっこぬけ」の絵しかない。
世界中に配るであろうスタンドの説明書・解説書なので、わざわざ日本語はありません。 そこにいきなり「1」で電源を抜く画像です。 初手から電源を引っこ抜く指示です。
もう動いちゃってます。
ところが、Echo Showの画面上部から下に向かって設定メニューを表示して、さ迷いましたが電源の切り方がない。 もちろん電源ボタンもないです。
で、もしかしたらと・・・
ミュートボタンを長押ししたら電源が切れることがわかりました。
Echo Spotでも同じです
ミュートボタンを長押しです。 ※ ミクさんが写りこんでいるのは僕の家の仕様です
さすがにEcho Spot、Echo Showで電源で何かあるのが怖い
それなりのお値段がするので、こんなことで何かあったら嫌なので、改めて記事にしました。
Dotはぶっこぬいてます。。。
以上、小ネタでした。
EchoShowでAPLを使って告白される
この記事は ADVENTARの「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」 の13日目の記事です。
ついにきましたね! Echo Show!
というわけで、さっそく試してみるわけです。
でかい
比較のために500mlの空き缶を添えてますがでかいです。
動画とかすごい
映画を観ると特に感じますが、ブラウン管かよってレベルの大きさであるだけ音がすごい良いです。 これはもう今後Echo ShowでPrime VideoやYoutubeを観ます。
スキルたちがより見やすくなる
■キャプテン九九
■乙葉の時間
■ヒロインの告白
ヒロインの告白をAPL対応にしてみる
結果、テストバージョンでこうなってます。
環境構築から実装まで、偉大なる @zono_0 さんが既にわかりやすい記事を掲載しています。 いつもありがとうございます。
上記、記事ではask cliが前提ですが、そうでなくてもAPLはもちろん使えます。 同じところは省いて、そうでないところを今回は補完していきます。
1.Amazon Developer Portal
僕は今回開いた際にに右下にデバイス検出エラーがでました。
が、 今回は 気にしないで大丈夫です。 JSONのフォーマットが欲しいので。
ちなみに、ここで対象のデバイスの大きさを以下のように選べます。
2.Lambdaでインラインエディタでも組める
ask cliは僕も使います。 というより、慣れるとそちらの方が開発は大変良いです。 コード管理とかもできますし。
かといって、ask cliの導入に躓く方もいらっしゃるとも思いますので、インラインでがんばってみました。
@zono_0 さんのように、APLのJSONは分割した方が良いと思います。
3.ソースコード
とりあえず起動して動いた段階のソースコードです。
■APLでのヒロインの告白のLaunchRequest
'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();
■homepage.json
{ "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" } ] } ] } ] } ] } ] } }
■data.json
{ "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を使いこなせるようになるとそれだけで職業になる気がします。
以上
スキル作成時のアイコンや音楽の素材サイトおよびツール類
この記事は ADVENTARの「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」 の12日目の記事です。
今日はAmazon Echo Showが届くはずなので、その記事を書くつもりでしたが、よくよく考えてみたら何時にくるかわからない。
ですので、別の日に予定していた記事として、僕がVUIのスキル・アプリを作成する時に利用検討する素材サイトを紹介します。
先日、某所のVUIイベントで 「Alexaのスキルは様々なツールで作れるようになってきてるけど、アイコンや効果音はみなさんどうしてますか?」 という話がありまして。
そういえばVUI業界の方々がどういう素材を用いているのか共有されていることを僕は認識していないので、僕が今までスキル作成をしていた時にたどり着いた素材サイトについて貼っておきます。
ライセンスが緩い素材サイト
以下に記載するサイトは著作権などのライセンスが比較的ゆるいサイトです。 ただ、もちろん利用規約はそれぞれのサイトに記載されておりますので、各自判断・自己責任でご利用ください。
1.アイコン
スキル作成時に地味に悩むのがアイコンです。
僕の中では以下の3点が利用しやすいと考えています。
■Alexaのicon-builder
■ICOOON MONO
ICOOON MONOさんはめっちゃおススメです。
■いらすとや
たまに「使い放題」と認識している方がおりますが 20点以上の利用など、制限が実はありますのでご注意を。
2.ジングル・効果音
VUIなので効果音が欲しい方はいらっしゃると思います。
ちなみに僕のスキル「ヒロインの告白」で起動時に 「しゃらららーーんスマートマキアート」 と、流れますが、あのような効果音を「ジングル」と呼びます。
ですので「効果音」「フリー素材」等でぐぐったりする方も多いと思われますが、「ジングル」を加えると、よりよいかもしれません。
以下が、僕がたどり着いた主なサイトです。
■ポケットサウンド
ご利用規約 – ポケットサウンド – フリー効果音素材・BGMダウンロード
■TURBO X
SOUND EFFECT| 商用利用可能なフリー効果音素材「TURBO X」
■DOVA-SYNDROME
■Music is VFR
■魔王魂
3.アイコン画像のサイズ変更
アイコン画像を拾ってきたとして、そのサイズ変更はどうしてるの?
との話もあったので僕が使っているツールを記載しておきます。
最近僕がwindowsで使っているのはリサイズ超簡単!Proです。
以下のように、サイズ変更したい画像を変換ファイルリストにいれて、 あとは縦横指定の箇所にAlexaの512×512と108×108を指定しておけば、覚えてくれるのでラジオボタンをポチって、「PNG」で「変換開始」でおしまいです。
本当に超簡単。
4.音楽編集ソフト
音声ファイルの編集はAudacityを使うことが多いです。直感的にわかりやすいです。
5.マイク
自分の声をVUIのスキル・アプリで流す!となるとマイクが必要になるわけですが、僕は以下です。
3000円くらいでいい感じに使えます。 本気声優でもない素人が使う分には十分かと思います。
6.オーディオファイルの変換
Alexaなどプラットフォームに対応したオーディオファイルに変換するならffmpegを使っています。
以下に僕がQiitaに書いた記事があるので適宜ご参照ください。
■Windows環境でffmpegを使ってmp3ファイルをAlexa対応形式に複数ファイル一括変換するメモ https://qiita.com/surumegohan/items/63a1b4e9fe404545ed06
まとめ
僕は上記に記載していた素材やツール等を使っていますが、VUIのスキル・アプリ作成をなさっている方で 「自分はここを使うよ!」 という素材やツールがあればコメントもらえると嬉しいです。
以上です。
Node-REDを使ってdialogflowで挨拶する
この記事は ADVENTARの 「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」 の11日目の記事です。
昨日はNode-REDを用いてAlexaでの挨拶スキルについて記載しました。
今度は同じくNode-Redを使ってdialogflow(要するにGoogle Home)の挨拶アプリを作ってみます。
今回も同じく以下の本に一部従ってみます。 もちろん、そのまま転記してはいけないので、内容は適宜変更、追加してます。
スマートスピーカーアプリ開発入門 3大スマートスピーカー Amazon Echo Google Home LINE Clova対応
作ってみる
1.昨日Alexaスキルを作っているので、まずはIBM Cloudのページに行きます。
そして、画面左上の人型マークからサインインします。
2.サインインをする
2回目以降の利用ユーザーならこの画面になるはず。
3.「起動」を押してダッシュボードを開く
上記画像の「起動」を押してダッシュボードを開きます。
前回のAlexaスキルの情報が表示されています。
4.過去に作成したインスタンスを削除
liteプランだと無料であるかわりに、インスタンスが1つしか作成できません。 そのため、liteプランを続けるならば作成済みのインスタンスを削除します。
5.インスタンスを再作成してNode-REDを検索
新しいインスタンスを「作成」したら、node-red で検索するとフィルタリングされて表示されるので選択します。
liteプランを継続している場合は、lite:ライトが既にフィルターにかかっているので、その後ろに node-red と入れてあげればOKです。
6.あとは画面をポチポチ
Node-REDの画面が開いたら、画面をポチポチします。
・HTTPSでPOST
・templeteで fulfillmentText をキーとした挨拶文を入れる
・httpResponse
あとは、この3つを線でつないであげます。
6.DialogFlowに設定すればおしまい
あとはDialogFlowで、プロジェクトを作り、Webhookに設定してあげれば、画面右のように動いてくれます。
まとめ
DialogflowでWebhookとして指定できるので、その先がNode-REDになります。
簡単な挨拶のようなスキルはプログラミングなしに画面ポチポチだけでいけます。 JSONを書くところがプログラミングに該当するなら、そこはプログラミングですが・・
ともあれ、すごく簡単にできるのでNode-REDを使ってGoogle Homeでやりとりできるようなアプリを作ってみてはいかがでしょうか。
以上です。
Node-REDを使ってAlexaスキルを創る
この記事は ADVENTARの 「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」 の10日目の記事です。
以前、Alexaスキルを作るツールとしてNOIDについてこのアドベントカレンダーで触れました
今回は同じくツールとしてNode-REDを使って挨拶スキルを作ります。
作り方
作り方と言っても、Qiitaや個人ブログ等に情報はあふれていると思います。
今回は以下の本に従ってみます。
スマートスピーカーアプリ開発入門 3大スマートスピーカー Amazon Echo Google Home LINE Clova対応
1.まずはNode-REDのアカウントを作成
2.次にIBM Cloudのアカウント作成
https://www.ibm.com/cloud-computing/jp/ja/lite-account/
ライトで大丈夫です。
3.IBM Cloudにログイン
ここで、アカウントIDはメールアドレスでログインできます。
4.何も考えないと一部英語になる
本の通りに進めていけば問題ないですが、何も考えないでポチポチやっていくと、画面の一部が英語になります。
が、特に躓くような英単語はないと思います。
5.作成できる画面
商業誌の内容をそのままは書けないのですが、以下のような画面になります。
話しかけられたら、インテントの種類を判別して起動時はLaunchRequestなのでそのまま挨拶をする。 それ以外はインテントの場合分けをswitchをかませて対応を分岐させ、応答を返したらHttpのResponseにつないであげます。
6.Alexa Consoleもほぼ通常通り
Node-REDで作成したインテント名で、挨拶のためのインテントを作成してあげます。
ただし、エンドポイントはLambdaではないので、Node-REDで作成したURLをWebhookとして指定します。
7.後は話しかけるだけ
きちんと動きます。
もし、「応答に時間がかかっている」というエラーになった場合は、Node-REDの画面で、線がすべて結びきれているか確認してみましょう。 JSONの作成はできていても、レスポンスを返すようになっていない場合があります。
まとめ
Node-REDでもスキル作成はできますし、本を参考にすれば、Node-REDもAlexaも初体験だったとしても、ぜいぜい2~3時間で作成できると思います。
※Amazonアカウント作成で躓く場合は別
Alexaのスキルをつくることに特化するならNOIDの方が断然楽だと思います。 ただ、Webhookとして、もっと汎用的にいろいろなことをやりたいとなると、Node-REDは選択肢の1つにはなるのではないでしょうか。
今回は以上です。
AMAZON.SearchQueryでビルドが通らない場合のTips
この記事は ADVENTARの 「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」 の9日目の記事です。
みなさん、SearchQuery使ってますか?
これを使うとユーザーから何かしら話しかけられ、他のIntentに分類できなかった場合、 このSearchQueryが実装されているIntentに割り振られます。
つまり、自由な単語・発話をSearchQueryに対応させたいという発想になりますが、ビルドが通らない場合あります。 今回はそれを 無理やり通す 方法について記載します。
もちろん、各自の自己責任にて。
一般的?の実装方法
SearchQueryはこの「なんでも該当」という強さから、一般的に他の単語と組み合わせて実装します。
例えば以下のように。
●●●と、サーチクエリ と話しかけれた際の●●の部分に用います。
ビルドに通らない場合
ただ、上記のような、「●●とサーチクエリ」のようにやりたくはなく、SearchQueryのみで実装したい場合、ビルドが通らない場合があります。
エラーメッセージは
Sample utterance "{searchQuerySlot} " in intent "SearchQueryIntent" must include a carrier phrase. Sample intent utterances with phrase types cannot consist of only slots.エラーコード: MissingCarrierPhraseWithPhraseSlot
自由度が高すぎて制御されているのではないかと考えています。 なので通常は他の具体的な話しかけ方と組みあわせて使うべきなのでしょう。
それでもビルドしたい場合
他のフレーズは一切いれず、何がなんでもSearchQueryのみ実装して、想定していないユーザーの発話はすべてここに集約したいという場合も存在します。
その場合、以下のようにするとビルドに通ります。
さて、どうやったでしょうか。
答えは 半角スペース + SearchQuery です。
こうすると他のIntentやSlotに割り振られない場合、このように作成したSearchQueryに無理やり分類させることができます。
ご利用は自己責任で
おそらくこれは、不具合に近いバグのような気がします。 仕様といえば仕様なのでしょうが。。。
ともあれ、ユーザーかどのように話しかけられたのかを丸々取得したい等の場合は、このような技で切り抜けることが一応できます。
実際には他のフレーズを組み合わせるべきだとは思いますが、このようなやり方も可能ではあるということを記載してみました。
本日は以上です。
技術書典5にて「スマートスピーカーを遊びたおす本」を執筆し、反響があった話
この記事は ADVENTARの 「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」 の8日目の記事です。
今回は技術書典5にむけて 「スマートスピーカーを遊びたおす本」 を執筆・編集し、頒布後に反響があった話について記載します。
■当日、僕が頒布した本「スマートスピーカーを遊びたおす本」
この本の概要は以下に記載のクラスメソッド社のDevelopersIOに掲載されているので、そちらをご確認ください。
というわけで、僕が記載した章の技術的な話。
本の内容の技術的な話
僕の担当箇所はたまたま1章になりましたが、Echo Spot対応のスキル「ヒロインの告白」の制作プロセスの話です。
本の内容をすべてブログに載せてしまうと、お金を払ってくれた方に申し訳ないので一部のみ記載します。
DynamoDBにputする関数は事前に作成してしまう
僕はDynamoDBにデータを格納する際の形式は自分で決めたい派です。
このアドベントカレンダーの3日目
でも取り上げましたが、SDKをいじるくらい自分で操りたい人です。
なので、今ではPut用のfunctionは共通化して作成してしまっています。
//DynamoDBにputする関数 function putDynamo(params) { console.log("=== putDynamo function ===" + params); docClient.put(params, function (err, data) { console.log("=== put ==="); if (err) { console.log(err); } else { console.log(data); } }); }
2回目以降の起動ユーザーは説明文を省くように実装する
上記のDynamoDBへのPutのfunctionをもちいて、LaunchRequestでの2回目以降の起動ユーザーは、スキルの説明をカットして、告白しても良いかどうかのみを結城琴葉ちゃんが話しかけるようにしています。
これはVUIのデザイン・設計として非常に重要だと僕は考えていて、2回目以降のユーザーは説明文をいちいち聞くのは極めて不快です。
もちろん、スキルの特性にもよりますが、このスキルは告白に対する受け答えをするスキルなので、複雑な操作は不要だと感じています。
また、2回目以降のユーザーでも初回起動時の説明文が流れるように「ヘルプ」では初回起動時のメッセージを流すようにしています。
ちなみに、ここでは余談ですが、僕がスキルを創るときは、ユーザーとVUIアプリケーションの対話の状況によって、「ヘルプ」のメッセージを変更するように実装しています。
// スタート音声 const kotoha_start_01 = '<audio src=\"https://hogehoge/kotoha_start_01.mp3\" />'; const kotoha_start_existuser = '<audio src=\"https://hogehoge/kotoha_start_existuser.mp3\" />'; //中略 const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, async handle(handlerInput) { // Amazonから提供されているTemplate 6を使用 if (supportsDisplay(handlerInput)){ const myImage1 = new Alexa.ImageHelper() .addImageInstance(DisplayImg1.url) // 結城琴葉の画像 .getImage(); const myImage2 = new Alexa.ImageHelper() .addImageInstance(DisplayImg2.url) // 今回はDisplayImg1.urlと同じ .getImage(); const primaryText = new Alexa.RichTextContentHelper() .withPrimaryText('') // 文字を出力させたくなかったのでこの記載 .getTextContent(); handlerInput.responseBuilder.addRenderTemplateDirective({ type: 'BodyTemplate6', token: 'string', backButton: 'HIDDEN', backgroundImage: myImage2, image: myImage1, title: "", textContent: primaryText }); } // 起動時メッセージを一旦、2回目以降の起動ユーザー用に代入 // 初回起動ユーザーで初期化するか迷いましたが、2回目以降のユーザー用に代入 let kotoha_start = kotoha_start_existuser; try{ // DynamoDBにあるテーブルに対して、既に存在するuserIdかどうか検索を実行 const queryItems = await docClient.query({ TableName: "kotohaTable", KeyConditionExpression: "#userId = :userId", ExpressionAttributeNames: {"#userId": "userId"}, ExpressionAttributeValues: {":userId": json_userId} // JSONからuserIdを事前取得しておいた値 }).promise(); try{ // 検索した結果、Items[0]にuserIdが入っているかログ出力を兼ねて確認 console.log("queryItems.Items[0].userId: " + queryItems.Items[0].userId); } catch (err){ // 検索した結果がゼロ件ならばログを出力できず、ここのcatchに分岐 // ここに流れてきた場合は、再生するmp3へのリンクを初回起動のユーザーとして再度代入 kotoha_start = kotoha_start_01; console.log("user Nothing"); // DynamoDBに存在しないuserIdだったため、格納する処理をここから記載 var item = { // JSONから取得しているuserIdをDynamoDBのuserIdとして格納する userId: json_userId }; // DynamoDBに格納する情報をparamsとしてまとめる var params = { TableName: kotohaTable, Item: item }; // DynamoDBに格納する // 本来はここでもエラー処理を入れているが割愛 await putDynamo(params); } } catch(err){ // エラー時の処理を入れているが割愛 } // sessionAttributesに起動後状態に移ったことを示す var sessionAttribute = ''; sessionAttribute = { "STATE": "after_start" }; handlerInput.attributesManager.setSessionAttributes(sessionAttribute); // 起動時の「スマートマキアート」の後に0.7秒の間を開けてから、起動メッセージを流す speechText = kotoha_smartmacchiato + '<break time="0.7s"/>' + kotoha_start; // 再生する内容はmp3のみであるため、speakもrepromtもspeakタグでくくっておく return handlerInput.responseBuilder .speak('<speak>' + speechText + '</speak>') .reprompt('<speak>' + speechText + '</speak>') .withShouldEndSession(false) //セッションを切らないことを明示する .getResponse(); } };
■各方面での反響
1.委託先のCOMIC ZIN 秋葉原店でド正面に平積み
BOOTHさん、とらのあなさん、ZINさんに売れ残った分を委託しておりますが、特にZINさんの扱いがすごすぎまして、秋葉原店の技術書典コーナーにないので店員さんに確認したら、まさかの入り口から入ってド正面のド真ん中に平積みしてもらっていました。
逆に気づかなかったという。。
2.クラスメソッド社のせーのさんが記事にしてくれた
日ごろ、非常に、大変、ものすごく、お世話になっているクラスメソッド社の清野さん(せーのさん)にDevelopersIOの記事として掲載いただけました。
非常にありがたいことです。
■VUIをトータルに勉強できる本「スマートスピーカーを遊びたおす本」を読んでみた。 #Alexa
3.この本をきっかけに商業誌の執筆依頼が出版社から頂く
この本をきっかけに、某スマートスピーカーおよび関連技術に関する商業誌の執筆依頼がきて、契約しました。
4.イベントでめっちゃ感謝された
先日の某イベントに参加していたら、この本と千代田まどか様(ちょまど)のファンの方から熱烈にお礼を伝えていただきました。
その人はGoogle Homeユーザーのようですが、著者としてもそういった声をいただけるのはうれしい限りです。
■アウトプットはいいぞ
というわけで、アウトプットをし続けていると、いろんな人が自分を観てくれます。 もちろんたまにはディスられます。
それでも、アウトプットをし続けることで、多くの人が応援してくれます。
みなさんもブログやイベントの登壇などでアウトプットをしてみてはいかがでしょうか。
以上です。