インターホンとLINEを連携するためにFlashAirをひっぱりだしてみた

## はじめに

先日、家に私一人の時に部屋で籠って仕事をしていたところ、インターホンが鳴ったことに気づかずAmazonからの荷物を受け取り損ねてしまいました。
私の仕事部屋にはリビングで鳴っているインターホンの音が届かないため、どうにかして仕事部屋に居ながらインターホンに気づくことができないかということで今回の仕組みを作ってみました。

## 使ったもの

- FlashAir W-03
- Amazon API Gateway
- AWS Lambda
- LINE Messaging API
- LINE BOT SDK
- TypeScript

### FlashAirについて

今回の仕組みの要ともいえるデバイスで、無線LAN通信機能を搭載したSDカードというニッチな製品です。
[初代FlashAirは2012年2月にリリース](https://www.global.toshiba/jp/news/corporate/2011/09/pr0101.html)とのこと。TOSHIBA(現在のKIOXIA)からの発売でした。
残念なことに既に生産が終わっているとのことですが、この手のデバイスはアイデア次第で色々と使えると思います。
今だったらNode.jsが動くようにして、イベントハンドラーで更新されたファイルのパスなどの情報が取れればさらに使い道がありそう。KIOXIAさん...
6,7年前に似たような製品の[Eye-Fi](https://ja.wikipedia.org/wiki/Eye-Fi)とFlashAirを購入していたのですが、私はどちらもイマイチ活用できていませんでした。
Eye-Fiの方は現在、デジカメ用のSDカードになっています。

FlashAirは第一世代から第四世代まで発売され、その中の第三、四世代は[Lua言語](https://ja.wikipedia.org/wiki/Lua)によるプログラムを動作させることができます。
私が購入していたFlashAir W-03は第三世代で、任意のプログラムを動作させられるモデルだったのはラッキーでした。

## 考えたこと

いざインターホンから何かしらの方法で仕事部屋にいる自分が気付けるような方法で通知をするといっても、
我が家のインターホン自体にそのような機能は提供されていません。
そこで思いついたのが上で紹介したFlashAirを利用する方法です。
インターホンで録画した動画をFlashAirでDropBoxへアップロードするというような記事を以前に見た記憶があったので、
HTTPのリクエストを投げられるのであればどうにかなるのではないかと思い、
下記の流れでLINEにメッセージを送ることを検討しました。

![FlashAir.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196930/f0017439-eaf1-e6ff-ac1a-36cccd9a5438.png)

インターホンがFlashAirにファイルを書き込むタイミングでHTTPのリクエストを送信し、
最終的にスマートフォンのLINEアプリに「ピンポーン」というメッセージが届きます。

## 作ってみる

### LINE Messaging API

まずLINEでメッセージを送ることができるようにしておきます。
その為にはLINE Developersに登録し、インターホン用のチャネルを作成、LINE Messaging APIを利用できるようにする必要があります。
手順を解りやすく纏めてくれている記事が幾つもあるのでそちらを参考にしてください。
私は下記の記事の「LINEボットの作成」を参考にさせていただきました。

[AWSを活用してLINEチャットボットを作ってみた！①](https://www.ryucom.co.jp/blog/aws/4713/)

上記のページの手順を実行した後、Messaging API設定でチャネルアクセストークン（長期）を発行しておきます。

### AWS Lambda

LINEでメッセージを送る準備ができたら早速Lambdaからメッセージを送信できるようにします。
Lambdaを作成する手順についても詳しい記事は幾つもあるので省略します。
特別なことはしていないため、公式のページの手順通りに進めていけば大丈夫だと思います。

[Node.js による Lambda 関数の構築](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-nodejs.html)

今回はTypeScriptで処理を実装しました。
作成したLINEへメッセージを送信するソースコードは下記のとおりです。

```typescript
import { messagingApi } from '@line/bot-sdk';
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
const channelAccessToken = '上で発行したLINE Messaging APIのチャンネルアクセストークン(長期)';  

export async function handler(event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> {
  try {
    // 送信処理
    const client = new messagingApi.MessagingApiClient({ channelAccessToken });
    await client.broadcast({
      messages: [
        {
          type: 'text',
          text: '「ピンポーン」',
        },
      ],
    });
    return {
      statusCode: 200,
      body: 'OK',
    };
  } catch (error) {
    // 送信失敗
    throw error;
  }
}
```

LINEに送るメッセージは常に同じという仕様にしたので特に応答を考慮する必要はありません。
上記の関数が呼ばれたらチャンネルに「ピンポーン」をブロードキャストするだけです。
このソースコードをトランスパイル＆バンドルしてLambdaにアップロードしました。
アップロード後にLambdaのテストを実行するとLINEに通知が届くことを確認できます。
初めてLINE Messaging APIを利用したので、これだけでも満足感がありました。

### Amazon API Gateway

上記のLambdaはAPI Gatewayを介することでHTTPでのアクセスができるようになります。
毎度ですがAPI Gatewayの作成手順もいろいろと詳しい記事がありますので省略します。
古い記事なので細かい仕様は変わっていますが、私は下記の「API Gatewayで、REST APIを作成」を参考にしました。
今回はメソッドをPOSTで作成しています。

[API Gateway + LambdaでREST API開発を体験しよう [10分で完成編]](https://qiita.com/tamura_CD/items/46ba8a2f3bfd5484843f#-api-gateway%E3%81%A7rest-api%E3%82%92%E4%BD%9C%E6%88%90)

作成したAPI GatewayへのアクセスとAPIキーを使用して制限します。
下記の記事の「APIキーの作成」が参考になりました。

[API GatewayのAPIキー設定方法](https://zenn.dev/nenenemo/articles/8fe19ecd4ffb1b#api%E3%82%AD%E3%83%BC%E3%81%AE%E4%BD%9C%E6%88%90)

これでHTTPのリクエストをすればLINEへメッセージが送られるという仕組みができました。
あとはインターホンが鳴ったらAPI Gatewayへリクエストを投げる部分だけです。

### FlashAir

やっと本題です。
私はFlashAirやLua言語については、開発経験が無かったので1から調べる必要がありました。
しかし上にも書いた通りニッチな製品な上に既に販売は終了している為、残されている情報があまり多くありません。
ただ調べていて感じたのは、普段AWSやReactなどを扱っていると調べた情報を精査することが大事なのですが、
FlashAir向けの情報はその殆どが信頼できる情報なので、逆に調べやすかったということです。
その中でも下記のページはとても参考になり助かりました。

[FlashAir開発者向け非公式wiki](https://seesaawiki.jp/flashair-dev/)

また上記のページからもリンクされていますが、公式の開発者ページも残されています。

[FlashAir Developers](https://flashair-developers.github.io/website/)

では具体的な手順に入っていきます。

#### Lua言語

FlashAirはLua言語で記述したプログラムを実行することができます。
実行されるタイミングはFlashAirの起動時と、ファイルの書き込みがあった時の2つです。
今回はファイルの書き込みがあった時に実行するプログラムを作成しました。
ファイル名を`line.lua`とし、内容は下記のとおり実装しました。

```lua
b,c,h = fa.request{
    url = "https://Amazon API Gatewayで作成したリソースのエンドポイント",
    method = "POST",
    headers = {
        ["Content-Type"] = "application/json;charset=utf-8",
        ["x-api-key"] = "Amazon API Gatewayで発行したAPIキー"
    },
    body = "{}"
}

sleep(30000)
```

処理としてはAPI Gatewayにリクエストを投げ、30秒間待つということをしているだけです。
リクエスト後に30秒間待たなければならない理由は、我が家のインターホンの応答が30秒弱で終わることにあります。
最初にリクエストを送信するだけのプログラムで動作させたところ、インターホンでの応答中は数秒おきにLINEへメッセージが送信されるという状態になりました。
これはインターホンが動画の録画中に随時SDカードへの書き込みを行っている為に、その都度イベントが発生しているのではないかと推測しました。
これを回避するために、イベントの処理をインターホンの応答が終わるまで実行することによって、
追加で発生したイベントを無視することを期待して30秒の待機時間を設けてあります。
実際の動きを見ると、数秒おきにメッセージが送信されることはなくなったものの、30秒間隔で3件のメッセージが送信されるようになりました。
これはインターホンが応答時に3つのファイルを作成しているのと関連していそうな感じがしますが、これについては今後の課題としました。
またwikiの情報から見よう見まねで実装しているので、fa.requestの戻り値は受け取る必要なさそうだと思いつつもサンプルの記載のまま残しています。

#### CONFIG

上記のプログラムをFlashAirにコピーし、書き込みのタイミングで動作するように設定をします。
FlashAirをSDカードリーダーに差しアクセスすると、普通のSDカードと同じようにアクセスができます。
まずそのルートに上記で作成した`line.lua`ファイルをコピーします。
そして`SD_WLAN`というディレクトリの下に`CONFIG`という設定ファイルがあるので、
この中の`APPMODE`,`APPSSID`,`APPNETWORKKEY`,`LUA_SD_EVENT`を下記の様に修正または追加します。
これ以外の項目については[開発者向けの設定 | FlashAir開発者向け非公式wiki](https://seesaawiki.jp/flashair-dev/d/%b3%ab%c8%af%bc%d4%b8%fe%a4%b1%a4%ce%c0%df%c4%ea)を参考にして決めてください。

```config
APPMODE=5
APPSSID=自宅の無線LANアクセスポイントのSSID
APPNETWORKKEY=自宅の無線LANアクセスポイントのパスワード
LUA_SD_EVENT=/line.lua
```

FlashAir開発者向け非公式wikiでは`APPMODE`を`6`としていますが、
私は開発時に`6`としておき、インターホンで運用する段階ではFlashAirのアクセスポイントは無効にしたかったので`5`としました。
この設定をしたFlashAirをインターホンに挿入すれば、インターホンとLINEを連携する仕組みは完成です。

### まとめ

こうしてインターホンが鳴った際にLINEへ通知する仕組みを実現することができました。
まだ1回の呼び出しで3個のメッセージが30秒おきに届くという問題はありますので、
これは時間のある時に対策を考えてみたいと思います。