アレクサを好きなようにしゃべらせる(Webインターフェイス編)

ひとりアドベントカレンダー24日目
途中で詰まってしまったので抜けたところは飛ばします。
と言いつつ24日目にも間に合っていませんが。

# はじめに

　コマンドラインでアレクサをしゃべらせられるようになったので、Webインターフェイスを作ります。
　ローカルで動けばいいのでRaspberryPi上でExpressでWeb APIとスマホ等から操作するためのページを一つ作ります。

# 環境

- RaspberryPi
- Raspbian GNU/Linux 10 (stretch)

``` bash
$ node --version
v12.22.3
$ npm --version
6.14.13
```

ところで、Raspberry Pi の初代はArmv6搭載のため新しいnodejsがパッケージで提供されていないため、以下のページの手順に従いUnofficialビルドをインストールしてあります。

https://qiita.com/homelan/items/4b61715bec5e805bf15d

使用したUnofficialイメージは[ここ](https://unofficial-builds.nodejs.org/download/release/v12.22.3/)から

# 手順

1. Expressを導入
1. メッセージをスクリプトに渡す処理を実装し、  
   メッセージを受け取るエンドポイントを追加
1. 任意のテキストコマンドを実行する処理とエンドポイントを追加
1. リクエストを送るフォーム入りのページを作成し、ルートに割り当て
1. サービス起動

## Expressを導入

``` bash
mkdir speak-alexa
cd speak-alexa
npm init
npm install --save express
```

テンプレートとか使わず app.js 1ファイルで済ませてしまいます。

## メッセージをスクリプトに渡す処理を実装し、メッセージを受け取るエンドポイントを追加

app.jsに実装していきます。

``` app.js
var express = require("express");
var app = express();

var server = app.listen(3000, function(){
    console.log("Node.js is listening to PORT:" + server.address().port);
});

app.get("/api/speak/free", (req, res) =>{
    console.log("acsess to /api/speak/free");
    const utterance = req.query.u;
    speak(utterance);
    res.json(utterance);
});

function speak(mes){
    console.log(`speak: ${mes}`);
    exec(`~/alexa/alexa_remote_control_plain.sh -d 'DeviceName' -e speak:'${mes}'`, (err, stdout, stderr) => {
        if (err) {
          console.log(`stderr: ${stderr}`)
          return
        }
        console.log(`stdout: ${stdout}`)
        }
    )
}
```

-dオプションで指定しているDeviceNameは -aオプションで実行すると得られるデバイスリストの名前を使用します。

## 任意のテキストコマンドを実行する処理を追加

しゃべらすだけでなく、通常alexaに話しかけるのと同じ内容をテキストで送ると実行されるtextcommandも実行できるようにしておきます。

``` app.js
app.get("/api/textcommand/free", (req, res) =>{
    console.log("acsess to /api/textcommand/free");
    const cmd = req.query.cmd;
    textcommand(cmd);
    res.json(cmd);
});

function textcommand(cmd){
    console.log(`textcommand: ${cmd}`);
    if ( cmd == 'undefined') {
      return
    }
    exec(`~/work/alexa/alexa_remote_control_plain.sh -d 'DeviceName' -e textcommand:'${cmd}'`, (err, stdout, stderr) => {
        if (err) {
          console.log(`stderr: ${stderr}`)
          return
        }
        console.log(`stdout: ${stdout}`)
        }
    )
}
```


## リクエストを送るページを作成し、ルートに割り当て

Web APIの口はできたので、スマホ等から操作しやすいようにUIのページを作ります。

``` text-form.html
<html>
<body>
<h1>Text-to-Alexa 入力画面</h1>
入力されたテキストをアレクサに送信してしゃべらせたり、<br>
呼びかける事無くテキストでコマンドを実行できます。
<hr><br>
しゃべってもらうテキスト
<form action="http://raspberrypi.local:3000/api/speak/free" method="get">
<input type="text" name="u" size="64"><br>
<input type="submit" value="speak">
</form>
テキストコマンド
<form action="http://raspberrypi.local:3000/api/textcommand/free" method="get">
<input type="text" name="cmd" size="64"><br>
<input type="submit" value="command">
</form>
</body>
</html>
```

これをルート(/)へのアクセスで返すようにしておきます。

``` app.js
app.get("/", function(req, res, next){
    console.log("access to /");
    res.status(200).sendFile('/home/pi/alexa-speak/html/text-form.html');
});
```

## サービス起動

あとはこれを起動します。

``` bash
$ node app.js
[2021-12-20T02:42:57.461Z] Node.js is listening to PORT:3000
```
package.jsonのscriptsに以下のように追記しておけば```npm start```で起動できるようになります。

``` package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node app.js"
  },
```

スマホからアクセスした画面は以下のように

![2F474A07-4570-4227-92B3-4ED61B44228D.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112265/d2a8283e-f3f5-d369-5f6b-42fd7a64a8e1.png)

# まとめ

　アレクサに好きなように発言させることができるようになり、アレクサっぽい口調で送ると自発的にしゃべりだしたようでびっくりさせることができます。しかし、やりすぎると慣れてしまうのでたまに思い出したように使っています。
　それだけでなく、声を出さずに操作できるようになったのは案外便利で、隣の部屋で鳴っているタイマーを遠隔で止めたりできるようになりました。
　自由にしゃべらせられるようになったので、センサーが異常値になった時の通知とかにも使ってます。