Teachable MachineとLINE Botで消耗品の棚卸をちょっと楽に。IT初心者が在庫記録システムに挑戦した記録。

## 新しい技術は使わないともったいない！

こんにちは、私は食品検査会社で働く会社員です。
デジタルもITもよくわからぬままにアラフォーになってしまいましたが、
近頃、頑張ってデジタル技術を勉強しています。

今回、簡単に機械学習を行える「 **Teachable Machine** 」を知り、触ってみることにしました。

https://teachablemachine.withgoogle.com/

 **モノと情報を簡単に紐づけられるこの技術**、どうやって使ったらいいんだ～？ と頭をひねった結果、**消耗品の在庫記録システム** があったら便利かもと思いました。
![isogashii_hakui_woman縮小.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/934f3b19-fe84-ce84-d608-b4341636ce0d.png)
## 課題
弊社では3か月ごとに検査で使う消耗品の棚卸をしています。 **消耗品ごとに個数と単価、購買先を調べて記録しないといけない** ので、若干面倒です。また、検査担当ではないメンバーが棚卸を実施すると、そもそも **物品を見ても名前がわからない** ！
この課題を解消したいと思いました。

## 使用したツール
* [Teachable Machine](https://teachablemachine.withgoogle.com/)
* Node-RED
* LINE Developer
* [Make（旧integromat）](https://www.make.com/)
* Googleスプレッドシート

https://nocodedb.world/archives/5489

## 作りたかった流れ
Webカメラで消耗品の画像を撮影 → Node-REDにつないだTeachable Machineで判別 → 結果をMakeへ送る → スプレッドシートの単価・購買先リストと照合 → 消耗品の名前、単価、購買先をスプレッドシートに追記＆LINEへ送信 → <font color="Red">LINEにメッセージが来たら消耗品の個数を入力してLINE Botへ送る → Makeで受信して個数をスプレッドシートに追記 → LINE Botから完了しましたのメッセージが届く</font> ）

~~なお、今回は残念ながら、取り消し線の部分の実装はできませんでしたので、こうやったらできなかったよの試行錯誤の記録としてお読みください。~~

<font color="Red">2022.6.7追記　赤文字部分の実装にこぎつけました！</font>

## 完成品の動画

Node-REDの様子

https://twitter.com/fefwo0778th/status/1533371895038738434

LINEの様子

https://twitter.com/fefwo0778th/status/1533372832197279744


## Teachable Machine
消耗品3種類のデータセットを作りました。
（Teachable Machineの詳しい使い方は下記の参考資料を参照ください。）

![teachable machine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/a8a8eafb-ea37-5b3e-e432-f85f103bc320.png)

## Node-RED
 **全体像**
![nodered.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/8595647a-2c9c-0a75-9a15-fc535195cff8.png)
<details><summary><b>各ノードの設定を表示（折りたたみ）</b></summary><div>

![nodered tmノード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/06d6fd32-5378-ea2e-e2ec-f16ca1bdf080.png)

Urlには自分がアップロードしたTeachable MachineのデータセットのURLを入れてください。

![nodered tempノード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/df30f649-1670-a620-5def-baf6e62822be.png)

プロパティのpayloadの後ろには後述のMakeで作ったカスタムWebhookの変数名として設定したものを書いてください。
（例：payload.bihin）

![nodered http reqノード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/1e7660c9-ce0d-fa25-1582-28b463cc6508.png)

メソッドは「GET」、URLはMakeで作ったカスタムWebhookのURLを入れてください

そのほか、「debugノード」「http responseノード」に変更はありません。

</div></details>

## Make
<details><summary><b>うまくいかなかったときの試行錯誤の記録（折りたたみ）</b></summary><div>

 **全体像①（Node-REDからTeachable Machineの結果を受信してスプレッドシートとLINEへ送る）**
![make.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/e6f68be3-57b4-28b9-61a3-fe65511e9258.png)
<details><summary><b>各モジュールの設定を表示（折りたたみ）</b></summary><div>

![make cwh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/bebd297d-b697-48f2-7a5c-c9ed4c381a5b.png)

このWebhookのアドレスをコピーしてブラウザに貼り付け、「WebhookのURL?変数名=テキスト」を入力してエンターを押すとAcceptedと出るのを確認する（この手順はWebhookに型を教えている？教えてもらった手順ですが、正直何が起こっているのか理解していません）。
（例：WebhookのURL?bihin=キムワイプ） 

![gs sr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/ce9f411b-0a28-4d7c-d0bb-da3e20879140.png)

別途用意したスプレッドシートの中身をSeach Rowsのモジュールで照合します。A列（備品名）とWebhookの変数「bihin」が同じ行を見つけます。エクセルのvlookupみたいなもの？

![gs ar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/b65fea7c-65c2-9280-8bc4-f24b3521bdfe.png)

書き込み用のシートにAdd a rowモジュールで入力します。

![line.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/9cc1aa1a-c319-aace-ad50-a3398d77ff3c.png)

LINEにも結果を送ります。ユーザーIDはLINE Developerで確認したユーザーIDを入力してください。

</div></details>

 **全体像②（①が終わった後で、ユーザーが消耗品の個数を入力すると個数をスプレッドシートに追記する）**
![make2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/bdc56467-7299-025e-90c1-1a5483807b4e.png)

 **ここで問題発生！**
Makeの「Update a row」モジュールでユーザーが入力した数値をスプレッドシートの個数の列に入力したかったのですが、
Makeでスプレッドシートの最終行の取得ってどうやるの？？
エクセルのVBAでも次の空の行に入力したいけど現在何行目かが固定でない場合は、最終行を取得してプラス1するっていう流れをよく書きます。

![line画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/b1ded986-1223-b7e0-d3ae-2c0e3105f079.png)

![表.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/5464502f-9eae-c2d0-3f8e-390f0590fc32.png)

教えてもらったところによると、同一のシナリオだったら「Add a Row」モジュールが現在何行目かの情報をもっているとのこと。「Add a row」モジュールを追加してためしに「Row number -1」のような設定が可能か見てみましたが、エラーが出てだめでした。
![agaki.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/57001be3-b5ab-78bc-83eb-8e6478841bd2.png)

また、さらに教えてもらったところによると、現在、２つのフローは別々のシナリオ上にありますが、２つのフローを同一のシナリオ内で動かすことができるそうです。同一のシナリオの中で「set variable」などの変数の受け渡しができるモジュールに「Add a Row」のRow number情報を設定すればいけるかも！
しかし！ **残念ながら、単純に２つのフローを１つのシナリオに持ってきただけでは動きませんでした。**
やはり、ルーターのような高度なテクニックを使わないとだめ？

![agaki2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/e67a4682-9ebe-89b5-19e3-25095e2808b5.png)

</div></details>
<font color="Red">2022.6.7追記　前回試行錯誤したところから、助言をもらって完成に持っていけましたので情報共有いたします！</font>

**【改】全体像①（Node-REDからTeachable Machineの結果を受信してスプレッドシートとLINEへ送る）**
![完成品①.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/ab2eae0f-91e9-92e5-1a6d-f9392cac7054.png)

**【改】全体像②（①が終わった後で、ユーザーが消耗品の個数を入力すると個数をスプレッドシートに追記する）**
![完成品②.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/1efa17a8-74fb-e148-1e79-07c6a9e26213.png)

ポイントは **Data store** です！！
**２つの独立したシナリオにおいて、それぞれのシナリオの変数の値を受け渡したい場合はData storeが使えます。**
ここでは①のシナリオの「Add a Row」が持っている現在の行数「Row number」の値をData storeへ渡して、②のシナリオを動かすときにData storeから値を引っ張り出してきています。
![data store.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/d30e1480-4342-96c2-8533-4b57d90de68f.png)
![data store設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/4caa9780-377f-bd08-dafe-b02790310a8b.png)
（行数の値は数値なので、型はNumberです）

<details><summary><b>シナリオ①②の各モジュールの設定</b></summary><div>

![data store設定2入れるほう.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/af9b1c08-ff86-64f8-d704-bf2588c8ed19.png)
①のシナリオのData storeモジュール（Update a record）で「Row number」を記録します。

![data store設定2出すほう.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/341cd908-21ec-d354-20ba-e5925aeebadf.png)
②のシナリオのData storeモジュール（Get a record）では行数の値をもらってきます。

![data store設定2gsの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/bf69cf1a-5b6d-5758-395e-98279c059539.png)
さらに②のシナリオの「Update a row」の「Row number」にData storeの行数の値をセットしてあげれば完成！

</div></details>
ちなみにData storeの中はこんな感じ

![data storeの中.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/82775074-49d3-2239-34f2-ed1e96217e16.png)

## 消耗品の個数が入力できるようになった完成品
![LINE完成３.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667068/752ec0c5-e27a-e990-90e2-bb8335e76ca2.png)
わ～！！完成！！嬉しい！！

## まとめ
機械学習というと遠い世界のお話のように感じられましたが、今回やってみて意外と簡単に自分のPCでもできちゃうんだなぁと驚きました。また、Makeでのスプレッドシートの最終行の取得が意外と難しいというのは盲点だったので、引き続きどうしたらよいか調べていきたいと思います。VBAやGASだったらできるのにくやしい！ローコードツールは便利だけど、ついていない機能についてはどうしようもないので、自分でコードが書ける力を身に着けたいと強く思いました。

2022.6.7追記
おかげさまで完成に持っていくことができました。
この記事がMakeのシナリオ間での値の受け渡しに困っている誰かに届いてくれると嬉しいです。

## おまけ
Teachable Machineから出力されたJavaScriptのコードを使って、CodePenで動かすのもやってみました。本当はCodePenから情報をNode-REDあるいはMakeへ送れたらよかったのですが、できませんでした。ついでにJavaScriptの本に書いてあったHTMLの文字を書き換えるという処理をボタンを押して実行できるようにしました。これはTeachable MachineのWebカメラの下に表示されている文字がちらちら切り替わるので、ボタンを押すと、（ボタンを押したタイミングで表示されていた文字が）上部の「ここを書き換える」欄に表示されるというのに挑戦したものです。初JavaScript動いて嬉しい！初心者には大きな一歩！

https://twitter.com/fefwo0778th/status/1533388819466448898

以上、御覧いただきまして誠にありがとうございました。

## 参考にした資料

https://paiza.hatenablog.com/entry/2019/12/11/%E8%AA%B0%E3%81%A7%E3%82%82%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%82%92%E6%B4%BB%E7%94%A8%E3%81%97%E3%81%9FWeb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E9%96%8B%E7%99%BA%E3%81%A7%E3%81%8D%E3%82%8B%E3%80%8CTe

https://qiita.com/shoito66/items/2f67e4de549dd844c0f4