😎

音声で買い物リストを操作しつつ外部連携したい2024年夏

2024/08/23に公開

概要

わたし「アレクサ!買い物リストに豆腐を追加して!」
Alexa「買い物リストに豆腐を追加しました」
LINE「買い物リストに豆腐が追加されました」
アレクサに話しかける様子
アレクサに話しかける様子
AlexaからスプレッドシートやLINEにIFTTTやTodoistなどで連携していた方。
2024年7月の仕様変更により、サードパーティ製スキルが使用できなくなって困ってませんか?(私は大変困りました。)

この記事は、音声認識で買い物リストにアイテムを追加しながらそのデータを連携したい私の試行錯誤記録です。同じような悩みを抱えた方の参考になれば幸い。
買い物リストの他デバイスへの連携
買い物リストの他デバイスへの連携

私について

プロダクトマネージャー。プログラミング知識が僅かにあるDIYプログラマ。
2023年にはChatGPTについての記事を書きました。
https://zenn.dev/toridori/articles/2be77ce697afd6#fn-7f09-3

免責

素人の試行錯誤なので誤った部分もあるかもしれません。大目に見てください。
むしろもっとイイ方法があれば教えてください。

試行錯誤

2024年7月の仕様変更はAmazonのShopping Listへアクセスするための大元である、Alexa ListスキルとList APIが終了したというものでした。自前でスキル開発することもできません。お手上げです。詰み。
Amazon Alexaを捨てる様子
Amazon Alexaを捨てる様子

Google Nestを買ってみた

そこで、Google Homeを買ってみました。スマートスピーカー自体はGoogle Nestという名前らしいですね。今ウチにはAmazon AlexaとGoogle Nestがあります。優秀な方を残してやろう。

早速、連携する方法を検討します。

Dialogflow CX

GoogleにはDialogflow CXというテキストや音声での会話をデザインできるプラットフォームがあります。要するに、会話の意味を理解して後続の処理をうまいことやれるようなすごいブツです。

これを使って自前でスキル開発できそうです。これを期待して買ったまであります。
ドキュメントを読んでみます。

2023年6月にDialogflowとGoogleアシスタントとの連携が廃止された

えっ?

2023年6月にDialogflowとGoogleアシスタントとの連携が廃止された

廃止?

Google Nestを捨てる様子
Google Nestを捨てる様子

いや、まだだ、まだ諦めるには早い……!

Google Keepにアイテムを追加してKeep APIを使う方法

どうやら、Google NestからGoogle Keepには音声で入力できるようです。あとはこの中身を外から取得できればよさそうです。しかもGoogle KeepにはAPIがあります。

ドキュメントを読んでみます。
APIの提供はエンタープライズ版のみ
APIの提供はエンタープライズ版のみ

提供はエンタープライズ版のみ……

Google Nestを捨てる様子
Google Nestを捨てる様子

もうちょっと探してみます。

pythonで非公式のgkeepapiというものが公開されているようです。今回はサーバーレスにしたいなどの理由で見送りました。pythonがデキル方は調べてみてもいいかもしれません。

LINEを諦めてショッピングリストをスマホで見る

通知が飛ばせないとか自前のカスタマイズができない等の理由で見送り。うーん、諦めたくない

諦めない様子
諦めない様子

でどうするの

音声認識について調べる中で、素敵な記事を発見しました。
https://qiita.com/hmmrjn/items/4b77a86030ed0071f548

お金も、登録も、認証キーも一切いらない。
軽いJavaScriptの知識があれば誰でも使える。
HTML/CSS/JSだけの静的ページでも動く。(ブラウザ上で動くので、バックエンドサーバは不要。)
ブラウザのネイティブAPIなので、JavaScriptライブラリをインポートする必要もなし。

こちらの記事によるとWeb Speech API Speech Recognitionが使えそうです。

Web Speech API Speech Recognition

カンタンなwebページを作ってhtml内でAPIを叩くだけ。であれば、GASで公開できそうです。GASで公開すればサーバーレス(しかも無料)だし、環境構築が不要なので非プログラマの私でもできそうとイイコト尽くめです。

コーディングにあたってはこちらの記事を参考にさせていただきました。
https://qiita.com/glory/items/58091984715e968c3480

できました

コード.gs

function doGet(e) {
return  HtmlService.createTemplateFromFile('index').evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}

function inputTextToSpreadSheet(text){
const url = "https://docs.google.com/spreadsheets/d/hogehoge";//値を記録したいスプレッドシートのURL
const sh = SpreadsheetApp.openByUrl(url).getSheets()[0]//記録したいシートの番号(0は1枚目)
sh.getRange(sh.getLastRow()+1,1).setValue(text);//末尾に記録
}

index.html

<!DOCTYPE html>
<html>

<body onLoad="vr_function()">
    <p>認識されている音声は、確定前は『グレー色』、確定後は『黒色』で表示されます。<br>
    このページを起動すると音声認識が自動で開始されます。</p>

    <p>現在の状態<br>
    <textarea id="status" cols="100" rows="1">
    </textarea>
    </p>
    <p>音声認識されている言葉<br>
    <textarea id="result_text" cols="100" rows="4">
    </textarea>
    <!-- <input type="button" onClick="vr_function(true);" value="音声認識開始" input id="start_button"> -->
</body>

<head>
    <meta charset="UTF-8">
    <title>Web Speech API</title>
    <script type="text/javascript">
        var form = document.forms[ document.forms.length - 1 ];

        var flag_speech = 0;
        function vr_function() {

            window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition
            if( ! window.SpeechRecognition ) {
                window.alert("お使いのブラウザでは、Speech APIはサポートされていません。");              
            }
            else{
              var recognition = new webkitSpeechRecognition();
              recognition.lang = 'ja';
              recognition.interimResults = true;
              recognition.continuous = true;

              recognition.onsoundstart = function() {
                  document.getElementById('status').innerHTML = "音声認識中";
              };
              recognition.onnomatch = function() {
                  document.getElementById('status').innerHTML = "もう一度試してください";
              };
              recognition.onerror = function() {
                  document.getElementById('status').innerHTML = "エラー";
                  if(flag_speech == 0)
                    vr_function();
              };
              recognition.onsoundend = function() {
                  document.getElementById('status').innerHTML = "停止中";
                    vr_function();
              };

              recognition.onresult = function(event) {
                  var results = event.results;
                  var confidence_value;
                  for (var i = event.resultIndex; i < results.length; i++) {
                      confidence_value = results[ i ][ 0 ].confidence;

                      //認識確定
                      if (results[i].isFinal)
                      {
                        var result_text_str = results[i][0].transcript;

                          if(result_text_str.indexOf("買い物リストに")>-1&&result_text_str.indexOf("を追加して")>-1){
                          //キーワードを検知
                          //GAS側で記述した関数inputTextToSpreadSheetを叩く
                            google.script.run.withSuccessHandler(playAudio).inputTextToSpreadSheet(result_text_str);
                            function playAudio(isSuccess) {
                              if (isSuccess) {
                                speak(result_text_str);//返答を喋らせる
                                result_text_str = "";//初期化
                              }
                            }
                            function speak(result_text_str) {
                                if (!"speechSynthesis" in window) {
                                    alert("このブラウザは音声合成に対応していません。")
                                    console.log("このブラウザは音声合成に対応していません。");
                                }
                                const before_text = "買い物リストに";
                                const after_text = "を追加して";
                                const item = result_text_str.substring(result_text_str.indexOf(before_text)+before_text.length,result_text_str.indexOf(after_text));
                                console.log("item:"+item);
                                const speech = new SpeechSynthesisUtterance();
                                speech.text = "買い物リストに"+item+"を追加しました";
                                speech.lang = "ja-JP"
                                speech.rate = 1
                                speech.pitch = 1
                                speech.volume = 1
                                speechSynthesis.speak(speech);
                            }
                          }

                          document.getElementById('result_text').innerHTML = result_text_str;
                          document.getElementById('result_text').style.color = 'black';
                          vr_function();

                      }
                      //認識途中
                      else
                      {
                          document.getElementById('result_text').innerHTML = result_text_str;
                          document.getElementById('result_text').style.color = 'gray';
                          flag_speech = 1;
                      }
                  }
              }
              flag_speech = 0;
              document.getElementById('status').innerHTML = "スタンバイ中";
              recognition.start();
            }
        }

    </script> 
</head>

</html>

GASで作られた音声認識のwebページ
ウェブアプリとしてデプロイしたもの

このウェブページを余ったノートPCで開いておけば、スプレッドシートに任意の文字列を音声入力できる!!

たったこれだけのコーディング(ほとんどコピペ)で音声認識できるのは感動モノですね。

あれ、Amazon AlexaとGoogle Nestは……?

Amazon AlexaとGoogle Nestを捨てる様子
Amazon AlexaとGoogle Nestを捨てる様子

今回はたまたまノートPCが余っていたので、ウェブページを開きっぱなしで24時間音声認識しっぱなしの環境を作れました。スマートスピーカーなんかいらんかったんや……!

とはいえ、何か音声認識でやりたいことがあるたびにコーディングしないといけないし、できることは多くないしで、全然スマートじゃないです。

なのでスマートスピーカーたちは捨ててません。進化に期待したいというのが本音。そのうち何でもできるAIエージェントが登場すると信じて、しまっておきます。

この記事が同じような悩みを抱える方の助けになれば幸いです。

toridori tech blog

Discussion