フロントエンドに優しいスマートスピーカースキルの作り方 #ゆるWeb札幌

フロントエンドに優しいスマートスピーカースキルの作り方 #ゆるWeb札幌

フロントエンド・Web技術をベースに作れる、スマートスピーカースキル (Google Nest Hub 向け) の導入の話。
https://mild-web-sap.connpass.com/event/147756/

13725f35541aa680ed5f85d16b85947a?s=128

Kihara, Takuya

October 26, 2019
Tweet

Transcript

  1. ΏΔ8FCษڧձ!ࡳຈ !UBDDL ϑϩϯτΤϯυʹ༏͍͠
 εϚʔτεϐʔΧʔεΩϧͷ
 ࡞Γํ ೥݄೔

  2. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ୭ • ໊લ tacck / ໦ݪ ୎໵ •

    ॴଐ גࣜձࣾϊʔεσΟςʔϧ • ओ࠵ • ΏΔWebษڧձ@ࡳຈ • εϚʔτεϐʔΧʔͰ༡΅͏ձ@ࡳຈ • ޷͖ͳϑΟΪϡΞεέʔτͷٕ εϓϨουɾΠʔάϧ
  3. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ΋݄͠೔ͷ4B$44ʹ ࢀՃ͞Εͨํ͕͍ͨΒ

  4. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ͍͍ͩͨҰॹͷ͜ͱΛ࿩͠·͢

  5. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ

  6. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ • Έͳ͞Μ͍࣋ͬͯ·͔͢?

  7. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ σϞ λεΫ֬ೝΞϓϦ ɹɾ5SFMMPͷΧʔυΛݺͼग़͠ ɹɾλονͰ׬ྃ

  8. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ͬ͘͟Γ࢓૊Έ

  9. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒
  10. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸
  11. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ ʮ"MFYBʯͱ ݺͼ͔͚ΒΕͨ ͜ͱΛೝࣝ
  12. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ ʮࠓͷ࣌ؒ͸ʯ Ի੠Λૹ৴
  13. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ Ի੠͔Β ςΩετʹม׵
  14. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ ʮࠓʯͷʮ࣌ؒʯ ˠݱࡏ࣌ࠁΛਘͶΒΕ͍ͯΔ ͱཧղ
  15. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ ݱࡏ࣌ࠁͷճ౴จΛ ࡞੒
  16. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ ճ౴จΛ Ի੠ʹม׵ͯ͠
 εϐʔΧʔ΁ฦ٫
  17. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ ड৴ͨ͠Ի੠Λ
 ࠶ੜ ൃ࿩ ޕޙ࣌ ෼Ͱ͢
  18. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εΩϧͷ࡞Γํ

  19. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ ݱࡏ࣌ࠁͷճ౴จΛ ࡞੒
  20. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ ɹΤϯδϯ Ϋϥ΢υ্ ௨৴ ϚΠΫ εϐʔΧʔ Ի੠ೝࣝ จষղੳ

    Ի੠ೝࣝ ର࿩࡞੒ Ի੠߹੒ "MFYB ࠓͷ࣌ؒ͸ εΩϧ࡞੒͸ ͜͜ͷ࡞ΓࠐΈ
  21. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ جຊతͳߟ͑ํ • Ϣʔβʔ͸εϚʔτεϐʔΧʔ΁ʮݺͼ͔͚ʯʮ࣭໰ʯΛߦͳ͏ • εϚʔτεϐʔΧʔ͸ͦΕʹԠͨ͡ʮ౴͑ʯΛฦ͢ • ݺͼ͔͚࣌ͷݴ༿͔ΒʮҙਤʯΛͲΕ͚ͩर͑Δ͔͕ΧΪ •

    Intent (ର࿩ҙਤ) • ʮͲ͏͍͏ҙਤΛ࣋ͬͨݺͼ͔͚͔ʯͱ͍͏ɺ
 ର࿩ͷछྨΛද͢΋ͷɻ
 • Slot (ύϥϝʔλ) • ݺͼ͔͚ͷதͷʮ೔෇ʯ΍ʮ਺ྔʯͱ͍ͬͨɺ
 มԽ͢Δ৔ॴΛද͢΋ͷɻ
  22. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ఱؾ༧ใͷ৔߹ • ʮ10݄17೔ʯ͕೔࣌Λද͢ Slot • ʮ౦ژʯ͕৔ॴΛද͢ Slot •

    ʮఱؾΛڭ͑ͯʯ͕ձ࿩ͷྲྀΕΛܾΊΔ Intent Ϣʔβʔ 10݄17೔ͷ౦ژͷఱؾΛڭ͑ͯɻ ໌೔ͷ౦ژͷఱؾ͸ಶΓͷ༧ใͰ͢ɻ εϚʔτ
 εϐʔΧʔ
  23. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ఱؾ༧ใͷ৔߹ • ʮ10݄17೔ʯ͕೔࣌Λද͢ Slot • ʮ౦ژʯ͕৔ॴΛද͢ Slot •

    ʮ࠷ߴؾԹΛڭ͑ͯʯ͕ձ࿩ͷྲྀΕΛܾΊΔ Intent Ϣʔβʔ 10݄17೔ͷ౦ژͷ࠷ߴؾԹΛڭ͑ͯɻ ໌೔ͷ౦ژͷ༧૝࠷ߴؾԹ͸20℃Ͱ͢ɻ εϚʔτ
 εϐʔΧʔ
  24. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ఱؾ༧ใͷ৔߹ • ʮࠓ೔ʯ͕೔࣌Λද͢ Slot • ʮఱؾΛڭ͑ͯʯ͕ձ࿩ͷྲྀΕΛܾΊΔ Intent Ϣʔβʔ

    ࠓ೔ͷఱؾΛڭ͑ͯɻ
 (ݱࡏҐஔ͸ࡳຈͰొ࿥ࡁΈ) ࠓ೔ͷࡳຈͷఱؾ͸੖ΕͰ͢ɻ εϚʔτ
 εϐʔΧʔ
  25. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ Ͳ͏͍͏ड͚౴͑ʹ͢Δ͔ • Ϣʔβʔͷ஌Γ͍ͨ͜ͱʹର͠ɺձ࿩ͷྲྀΕΛߟ͑Δɻ • ձ࿩ͷྲྀΕͷத͔Β Intent (ҙਤ) Λݟ͚ͭΔɻ

    • Intent ͝ͱʹɺ۩ମతͳฦ౴ͷϓϩάϥϜΛॻ͍͍ͯ͘ɻ ࠓ೔ͷఱؾ͸? ࡳຈͷࠓ೔ͷఱؾ͸ɺ
 ͘΋Γ ͷͪ ੖Ε Ͱ͢ɻ
  26. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ը໘෇͖εϚʔτεϐʔΧʔ

  27. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ը໘෇͖σόΠε ը໘දࣔͷ࢓૊Έ ɹɹɹɹɹɹɹAmazon Echo Show Echo Show 5

    Echo Spot APL ɹɹɹɹɹɹɹGoogle Google Nest Hub Interactive Canvas
 (Web) ɹɹɹɹɹɹɹLINE Clova Desk ඇެ։
  28. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ϑϩϯτΤϯυΤϯδχΞͷ
 օ༷ʹ࿕ใ

  29. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ "DUJPOTPO(PPHMF *OUFSBDUJWF$BOWBT

  30. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ੠ͱλονͰૢ࡞Ͱ͖Δ
 8FCΞϓϦέʔγϣϯ
 Ά͍ײ͡ͰεΩϧΛ࡞ΕΔ  ˠීஈ࡞͍ͬͯΔ΋ͷʹʮ੠ʯΛ෇Ճ͢Δ

  31. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ֬ೝ λεΫ֬ೝΞϓϦ ɹɾ5SFMMPͷΧʔυΛݺͼग़͠ ɹɾλονͰ׬ྃ

  32. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ දࣔίϯςϯπ 7VFKT  IUUQTHJUIVCDPNUBDDLTIPXUPEBZUBTLT

  33. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ mounted: async function () { // Callback Ͱ

    ΧʔυҠಈͷॲཧΛ࣮૷ window.interactiveCanvas.ready({ onUpdate: async data => { let cardId = '' let cardNumber = -1 if ('cardId' in data) { cardId = data.cardId cardNumber = this.list.findIndex(element => { return element.id === cardId }) } else if ('cardNumber' in data) { cardNumber = data.cardNumber - 1 const card = this.list[cardNumber] cardId = card.id } else { // ߋ৽ෆཁͳͷͰऴྃ return } await axios.put( `https://api.trello.com/1/cards/${cardId}?idList=${this.finishedListId}&key=${this.apiKey} &token=${this.apiToken}&pos=bottom` ) this.list.splice(cardNumber, 1) }, onTtsMark: markName => {} })
  34. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ mounted: async function () { // Callback Ͱ

    ΧʔυҠಈͷॲཧΛ࣮૷ window.interactiveCanvas.ready({ onUpdate: async data => { let cardId = '' let cardNumber = -1 if ('cardId' in data) { cardId = data.cardId cardNumber = this.list.findIndex(element => { return element.id === cardId }) } else if ('cardNumber' in data) { cardNumber = data.cardNumber - 1 const card = this.list[cardNumber] cardId = card.id } else { // ߋ৽ෆཁͳͷͰऴྃ return } await axios.put( `https://api.trello.com/1/cards/${cardId}?idList=${this.finishedListId}&key=${this.apiKey} &token=${this.apiToken}&pos=bottom` ) this.list.splice(cardNumber, 1) }, onTtsMark: markName => {} }) εϚʔτεϐʔΧΒͷ
 Πϕϯτड͚औΓ λοϓͷ৔߹ ੠ͷ৔߹ λοϓ͞ΕͨΧʔυΛ
 ׬ྃࡁΈϦετ΁Ҡಈ Ϧετ͔ΒΞΠςϜ࡟আ
  35. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ const response = await axios.get( `https://api.trello.com/1/lists/${this.listId}/cards?fields=all&key=${this.apiKey}&token=$ {this.apiToken}` )

    this.list = response.data }, methods: { finish: function (id) { window.interactiveCanvas.sendTextQuery(`finishedTask-${id}`) } }
  36. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ const response = await axios.get( `https://api.trello.com/1/lists/${this.listId}/cards?fields=all&key=${this.apiKey}&token=$ {this.apiToken}` )

    this.list = response.data }, methods: { finish: function (id) { window.interactiveCanvas.sendTextQuery(`finishedTask-${id}`) } } TrelloͷಛఆͷList͔Β
 CardҰཡΛऔಘ Card ͕λοϓ͞ΕͨΒ Intent ૹ৴
  37. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ όοΫΤϯυ
 "DUJPOTPO(PPHMF  IUUQTHJUIVCDPNUBDDLTIPXUPEBZUBTLTMBNCEB

  38. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ 'use strict'; const axios = require('axios'); const {

    dialogflow, HtmlResponse } = require('actions-on-google'); const app = dialogflow({ debug: false }); const tasksUrl = 'https://show-today-tasks.netlify.com' const currentListId = process.env.CURRENT_LIST_ID const finishedListId = process.env.FINISHED_LIST_ID const apiKey = process.env.API_KEY const apiToken = process.env.API_TOKEN // ىಈ࣌Intent app.intent('welcome', (conv) => { conv.ask('ࠓ೔ͷλεΫҰཡͰ͢ɻ֬ೝ͠·͠ΐ͏ɻ'); conv.ask(new HtmlResponse({ url: `${tasksUrl}/?listId=${currentListId}&apiKey=${apiKey} &apiToken=${apiToken}`, suppress: true }));
  39. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ 'use strict'; const axios = require('axios'); const {

    dialogflow, HtmlResponse } = require('actions-on-google'); const app = dialogflow({ debug: false }); const tasksUrl = 'https://show-today-tasks.netlify.com' const currentListId = process.env.CURRENT_LIST_ID const finishedListId = process.env.FINISHED_LIST_ID const apiKey = process.env.API_KEY const apiToken = process.env.API_TOKEN // ىಈ࣌Intent app.intent('welcome', (conv) => { conv.ask('ࠓ೔ͷλεΫҰཡͰ͢ɻ֬ೝ͠·͠ΐ͏ɻ'); conv.ask(new HtmlResponse({ url: `${tasksUrl}/?listId=${currentListId}&apiKey=${apiKey} &apiToken=${apiToken}`, suppress: true })); දࣔίϯςϯπ͕
 ը໘ʹग़ΔΑ͏ʹࢦఆ
  40. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ app.intent('finishedTaskByVoice', async (conv, { cardNumber }) => {

    // ੠Ͱ׬ྃʹ͢Δ conv.ask('ΧʔυΛ׬ྃʹ͠·ͨ͠ɻ') conv.ask( new HtmlResponse({ url: `${tasksUrl}/?listId=${currentListId}&finishedListId=$ {finishedListId}&apiKey=${apiKey}&apiToken=${apiToken}`, suppress: true, data: { cardNumber: cardNumber } }) ) })
  41. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ app.intent('finishedTaskByVoice', async (conv, { cardNumber }) => {

    // ੠Ͱ׬ྃʹ͢Δ conv.ask('ΧʔυΛ׬ྃʹ͠·ͨ͠ɻ') conv.ask( new HtmlResponse({ url: `${tasksUrl}/?listId=${currentListId}&finishedListId=$ {finishedListId}&apiKey=${apiKey}&apiToken=${apiToken}`, suppress: true, data: { cardNumber: cardNumber } }) ) }) ੠Ͱࢦఆ͞Εͨ ΧʔυͷදࣔॱΛऔಘ ίϯςϯπଆʹ
 ΧʔυͷදࣔॱΛૹ৴
  42. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ app.intent('finishedTask', async (conv, { cardId }) => {

    // λοϓͰ׬ྃʹ͢Δ conv.ask('ΧʔυΛ׬ྃʹ͠·ͨ͠ɻ') conv.ask( new HtmlResponse({ url: `${tasksUrl}/?listId=${currentListId}&finishedListId=$ {finishedListId}&apiKey=${apiKey}&apiToken=${apiToken}`, suppress: true, data: { cardId: cardId } }) ) })
  43. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ app.intent('finishedTask', async (conv, { cardId }) => {

    // λοϓͰ׬ྃʹ͢Δ conv.ask('ΧʔυΛ׬ྃʹ͠·ͨ͠ɻ') conv.ask( new HtmlResponse({ url: `${tasksUrl}/?listId=${currentListId}&finishedListId=$ {finishedListId}&apiKey=${apiKey}&apiToken=${apiToken}`, suppress: true, data: { cardId: cardId } }) ) }) λοϓ͞ΕͨΧʔυͷ
 IDΛऔಘ ίϯςϯπଆʹ
 CardId Λૹ৴
  44. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ·ͱΊ

  45. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ (PPHMF/FTU)VC͍͍Α • εΩϧ։ൃʹɺطଘͷWebΞϓϦ։ൃͷ஌͕ࣝɺ
 ͔ͳΓ׆͔ͤΔɻ • ը໘͕ಈ͘ͷ΋ָ͍͠ɻ • ࣮͸ɺInteractive

    Canvas ͸ɺ
 Android ͷ Google Assistant Ͱ΋࢖͑Δ!
 → AndroidϢʔβʔͳΒɺ͙͢ʹࢼͤΔͶ!
  46. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ ͦΕͧΕͷಛ௃ • Echo • γϦʔζల։͕๛෋ɾຊؾग़͢ͱʮεϚʔτϗʔϜʯԽՄೳ • ը໘෇͖ͷ “Echo

    Show 5” ͕͓खࠒ (¥9,980) • Google Home • Android ΋ؚΊͨڧΈ • ը໘෇͖ͷ “Google Nest Hub” Λ࣠ʹࠓޙల։ͷํ޲ • LINE Clova • LINE ΍ LINE Bot ͱͷ࿈ܞ͕ڧ͍(ϝοηʔδ࢖͏ͳΒ͜Ε) • ࠓޙͷల։͸ɾɾɾΧʔφϏ?
  47. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ εϚʔτεϐʔΧʔ • Έͳ͞Μങ͍·͠ΐ͏!

  48. !UBDDL ΏΔ8FCษڧձ!ࡳຈΏΔ8FCࡳຈ FOE