TwinMakerで、バーチャルなキャラクターに現実の家事を手伝ってもらう

# この記事について

**この記事をひとことで**

・AWS IoT TwinMakerを使った、`現実のお風呂の湯張りが終わったことを バーチャルなキャラクター(MMD)が教えてくれる仕組み`の作り方を紹介します。

**何が嬉しいの？**

- `現実世界の家事をバーチャルなキャラクターが手伝ってくれる`体験ができます。
- 3Dのキャラクターと現実世界のリンクが`ローコードで実装`できます。
- お風呂の監視が楽になります。

**難しい？**

- TwinMakerを使うので、大部分をプログラミングなしで構築できます。
- AWSの知識が必要です。
- データベース、3Dソフト、IoT等のスキルは不要です。
- プログラミングが少し必要になる場所:
    - TwinMakerのタグをMMDに置き換える部分にReactのコードを書きます
    - データを収集する部分にPythonのコードを書きます

**予算は？**

- センサー代の2,000円があれば大丈夫です。

**動いているところ: 動画(20秒)**

<iframe width="560" height="315" src="https://www.youtube.com/embed/WTCnVeh32us?mute=1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; mute; playsinline; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 前提条件: この記事で解決したいのはどんな課題？

筆者の家は水栓でお湯を入れるタイプの浴室です。このタイプです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/375185ea-bde0-2321-98e3-3ed6338791bb.jpeg" width="400">

お湯をためるためには、手で栓を緩めてお湯を出して、いい水位までたまるのを待って、手で栓を締める必要があります。

時間が経つと勝手にお湯が止まる機能はないので、じぃっと目視でお湯を監視します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/3b8e5726-50c5-52b8-bc65-883efedc2bd7.png" width="150px">

お湯がたまるのを待っている時間は、2分間憎悪ならぬ10分間虚無です。ビッグ・ブラザーは僕を見ている時間があるなら我が家のお湯を見ていてください。結構大変です。

今回の記事では、これをテクノロジーで解決します。

## 課題の解決方法: VTuberにお湯張りを見てもらう

AWS IoT TwinMakerを使って、お風呂の監視を「兎田ぺこら」さんにお願いするシステムを作りました。

![pekora-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/c5f5c492-0c50-9ec4-c5be-84848ab90fd7.png)

お湯を張り終わると、ぺこーらが教えてくれます。

![pekora-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/9e67e351-e11f-1acb-e39e-0c9063b593ff.png)

> **補足:** 
> MMDなら誰でも大丈夫です。ボーカロイド、アニメ、ゲーム、VTuber、色々なキャラクターが見つかると思いますので、気に入ったキャラクターで構築してください。

センサーはSwitchBotの`防水温湿度計`を使います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/4b0bed69-21b9-7de3-d569-2611d8662584.jpeg" width="250">

1,980円（税込）とリーズナブルですが、精度は高く、セットアップは簡単で、電池の絶縁フィルムを引き抜くだけで測定が始まります。コンセントは不要で、Bluetooth（BLE）で無線通信ができます。お風呂でも安心して使えます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/05e4730f-30f7-64a7-4bb0-afb704b53b61.png" width="150">

センサーとTwinMakerで実装する概要は以下のとおりです。

**センサー側ですること**

①防水温湿度センサーを袋に入れて、浴槽の内側にぶら下げます。
②センサーの数値をBLEで受信、SiteWiseに転送します。

→ センサーがお湯に沈むと温度が跳ね上がるので、それを検知させます。

**TwinMaker側ですること**

①TwinMaker上で、3Dの部屋、データの受信ルール、センサーの値によって色が変わるタグを作ります。
②iot-app-kitでTwinMakerを表示する時に、タグの表示をMMDに置き換えて、タグの色変えイベントをMMDがサブスクライブします。

→ タグの色が変わると、対応するモーションが再生されるようにします。

もうこれで10分間の虚無を過ごさなくてもよくなります。

## 記事の構成

必要なものは以下の通りです
- パソコン(Windows機、Bluetoothと接続できるもの)
- スマートフォン(SwitchBotの初期設定ができるもの)
- VSCode
- AWSアカウント
- SwitchBot 防水温湿度センサー
- 輪ゴム、何か円形のひも状のもの
- ビニルテープ
- チャック付きポリ袋（ジップロックの小さいやつ）
- ネックストラップ（首からぶら下げるやつ）
- Python（v3.8以降のバージョンのもの）
- Node.js（v14.15以降のバージョンのもの）

この記事では、以下のことを前提に記事を進めます
- React, Pythonについて、プログラミングの基本的な知識があること
- AWS（特にIAM、認証回り）の運用方法を知っており、ある程度の操作ができること

ソースコードはこちらにあります。手元で構築する際は、ローカル環境にダウンロードしてから作業していただきますようお願いいたします

https://github.com/ShotaOki/bath-alarm-twinmaker

# AWSに必要な環境を構築する

**すること**

- SiteWiseの環境を構築します
- TwinMakerの環境を構築します

> **補足と注意: リージョンについて**
> TwinMakerは東京リージョンにありません。
> SiteWiseのリージョンも、TwinMakerが利用できる`バージニア北部（us-east-1）`に合わせてください。

**構成図**

![aws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/42d56317-1b97-6eff-eae9-6032e8bcd8cc.png)

シンプルな構成図になります。登場するAWSサービスが少なく、VPCやEC2のような料金のかかるサービスも含まないため、ランニングコストも高くなりません。お風呂を見張るだけだと、1日1時間利用して1円かからないくらいだと思います。

> **補足:**
> 筆者が1週間の開発でかかったAWS利用料金は3円でした。

## SiteWiseの環境を構築します 

SiteWiseの構築を始めます。

`AWS IoT SiteWise`を簡単に説明すると、`IoTの裏方を全部やってくれるサービス`です。

![sitewise.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/3e7c0c05-aae4-d09a-40da-c82920268eb7.png)

IoTから受け取ったデータを収集、整形、保存して、他のサービスに連携することができます。データを収集するエンドポイントとしての機能だけでなく、データを変換処理をする機能、データベースとしてデータを保管する機能も持っています。

**1. リージョンをバージニア北部にする**

AWSマネジメントコンソールにログインしたら、まずTwinMakerが利用できるリージョンに変更します。

![region-versinia.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/f0628cba-eca0-866c-f3ac-fe5e278de827.png)

**2. IoT SiteWiseを開いて、モデルを作成します**

IoT SiteWiseを開いて、「ビルド」→「モデル」を選択、「モデルの作成」を選びます。

![init-sitewise.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/449c0f5d-ab4d-4f28-6153-fc4e831d1897.png)

入力するパラメータ

|大項目|項目|設定する値|備考|
|:--|:--|:--|:--|
|モデルの詳細|名前|SwitchBotTemperature|※好きな名前を付けます|
|モデルの詳細|説明||※空欄にします|
|属性の定義|||※全て空欄にします|
|測定値の定義|名前|temperature|※今回は温度を測定するので、温度にします|
|測定値の定義|単位|c|※単位の名前を付けます|
|測定値の定義|データ型|ダブル|※ダブル型を選びます|
|**以降の項目**|||※全て空欄にします|

**3. アセットを作成します**

モデルでデータの定義を設定したので、配置するセンサーの情報を作ります。

> プログラミングで言うと、モデルがクラス定義、アセットがインスタンスになります。現実のセンサーはアセットに自分のデータを同期します。

![init-asset.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/4dd10aa4-23cb-108a-877d-7840c2bdd9cc.png)

__入力するパラメータ__

|大項目|項目|設定する値|備考|
|:--|:--|:--|:--|
|モデル情報|モデル|SwitchBotTemperature|※先ほど作ったモデルを選びます|
|アセットの情報|名前|SensorA|※好きな名前を付けます|
|測定|temperature|record/temperature|パス形式で好きな名前を付けます|
|測定|MQTT通知ステータス|アクティブ|有効にします|

## TwinMakerの環境を構築します

次に、IoT TwinMakerの環境を構築していきます

`AWS IoT TwinMaker`を簡単に説明すると、`簡単に3D空間（メタバース）を作って、ほぼリアルタイムで現実世界と同期できるツール`です。

AWSの資料では、3D世界に風車を作って、現実の風車のデータ（回転数、異常だとか）を表示するデモが出ています。

![205143289-664e4a63-a421-497b-8265-a9a481a11db5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/e682c8a7-1063-de04-b66f-b88cd69ee870.png)

**1. リージョンをバージニア北部にする**

SiteWiseに続いて作業をするのなら作業開始時点からバージニア北部になっているはずです。もし違うリージョンになっているのなら、バージニア北部に変更します。

![region-versinia.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/f0628cba-eca0-866c-f3ac-fe5e278de827.png)

**2.TwinMaker開いて、`ワークスペースを作成`します**

![create-workspace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/0aa128ac-658e-0a8d-46c7-ad8553e65688.png)

入力項目は以下のように入力します。

- 名前：自由に名前を付けます
- 説明：空欄でもOKですが、必要があれば説明を書きます
- S3バケット：1番上にある、`S3バケットを作成`を選択します
- 実行ロール：1番上にある、`新しいロールを自動生成`を選択します

入力できたら、**「確認をスキップして作成」** を選択します
　→　スキップしないとGrafanaのダッシュボードの作成画面が出てきます。
　　　今回の手順では使わないため、Grafanaのダッシュボードは不要です。

**3.ワークスペースを作成したら、`シーン`を作成します**

![scene.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/230f38c5-d358-050b-0e6f-4767694b09e4.png)

入力項目は以下のように入力します。

- シーンID：自由に名前を付けます
- 説明：空欄でもOKですが、必要があれば説明を書きます

シーンを開くと、TwinMakerの編集画面が出てきます

![scene-open.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/90e43139-ccf0-bc8e-0339-f965d6fc2dba.png)

**4. IAMロールを編集します**

ワークスペースを開いて、`実行ロール`を確認します。

![workspace-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/df16ecc7-971d-722b-8942-0a2a9ce14a1f.png)

実行ロールの名前を確認したら、IAMに移動して、実行ロールの内容を編集します。

![policy-added.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/45670964-0e9b-2161-3042-dc283169c3e5.png)

以下の2つのポリシーを許可するようにします

- AWSIotSiteWiseFullAccess
- AmazonS3FullAccess


**5. TwinMakerがSiteWiseのデータを参照できるように設定します**

「エンティティ」→「エンティティを作成」で、エンティティ情報を作成します。

![create-entity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/b2fbce28-73db-43f5-0963-58eff490e5a0.png)

**入力するパラメータ**

|大項目|項目|設定する値|備考|
|:--|:--|:--|:--|
|エンティティ情報|名前|SwitchBotTemperature|※任意の名前を付けます|

作成したエンティティを選んで、「コンポーネントを追加」をします

![create-component.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/06500c8b-10b4-5ff7-521c-73a36eb79676.png)

**入力するパラメータ**

|大項目|項目|設定する値|備考|
|:--|:--|:--|:--|
|コンポーネント情報|名前|Sensor|※任意の名前を付けます|
|コンポーネント情報|タイプ|com.amazon.iotsitewise.connector|※接続先がSiteWiseだと指定します|
|コンポーネント情報|アセットモデル|SwitchBotTemperature|※SiteWiseのモデル名を指定します|
|コンポーネント情報|モデル|SensorA|※SiteWiseのアセット名を指定します|
|**以降の項目**|||※変更なしでOKです|

コンポーネントが追加できたら、TwinMakerとSiteWiseの接続は完了です。

**6. リソースに3Dモデルを登録します**

TwinMakerに表示する3Dモデルを登録します。

「リソースライブラリ」→「リソースを追加」を選んで、ファイルをアップロードします。

![add_resource.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/a327633f-092a-5e80-0e50-dd1d2f0c1fd2.png)

リソースには、GLTF、GLB形式のファイルを使うことができます。もしPNGの2Dイメージを使いたい場合は、筆者のツール（<a href="https://shotaoki.github.io/png2gltf/">PNG画像をGLTFに変換するツール</a>）を使うことで、TwinMakerに2D画像を取り込むことができます。

※ここでは背景だけ登録します。MMDのファイルはここで登録しなくても大丈夫です

> **補足: GLTF(GLB)ってどうやって用意するの？**
> <a href="https://sketchfab.com/feed">Sketchfab</a>で探すと、無料で公開されている3Dモデルをダウンロードすることができます。
> 欲しい3Dモデルの拡張子がobj形式にしか対応していなくても、<a href="https://github.com/CesiumGS/obj2gltf">obj2gltf</a>でglbに変換することで利用できます。
> スマートフォンの3Dスキャンアプリ（Scanniverse、Matterportなど）を使って、iPhoneで撮影した3Dデータを使うこともできます

**7. シーンを開いて、3Dの空間を作っていきます**

シーンの初期状態はこの状態です。

![scene-open.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/90e43139-ccf0-bc8e-0339-f965d6fc2dba.png)

「＋」ボタンを押して、シーンに3Dのリソースを配置していきます。

![scene_overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/5d07f709-9eb2-43da-7983-1999c3ec36bc.png)

カメラ、部屋、吹き出し画像を配置しました。

![positioning.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/04597545-b15c-881b-0f8c-f0f3cdff77b9.png)

> **補足: 記事で使ったGLBデータ**
> 部屋のモデルはSketchfabからお借りしました（ライセンスはCC 4.0です）
> https://sketchfab.com/3d-models/studio-apartment-vray-baked-textures-included-cae2d96ede1d4112b1fd391099a43f77
> 
> 吹き出し画像は、<a href="https://fukidesign.com/">ふきだし素材専門サイト「フキダシデザイン」</a>で作成したものを、筆者のツール（<a href="https://shotaoki.github.io/png2gltf/">PNG画像をGLTFに変換するツール</a>）でGLTFに変換して用意しました。

**8. タグを作成します**

部屋を作成したところに、「Add Tag」でタグを作成します。

![add-tag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/d681680a-1643-550a-cc78-afb4055f248a.png)

タグの位置は、MMDを配置したい場所の足もとを指定します。

![visible-tag.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/788c32fc-400f-b1bd-f2d1-a42d61b22dae.jpeg)

タグにはMMDとして呼び出したいキャラクターの名前を付けてください。
記事では「Usada-Pekora」にします。

![tag-name-changed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/59d99d3a-8439-2337-4cc2-a6ebd4d9441f.png)

**9. 表示ルールを作成する**

SiteWiseのデータが変わった時に、TwinMakerでどう表示するのかを設定します。

「ルール」→`temperature-rule`の名前で新規ルールを作成してください。

作成したルールを開いて、`temperature<38`の式と、`temperature>=38`の式を設定します。ターゲットには情報アイコンと警告アイコンを指定して、アイコンの見た目が変わるようにします。

以下のことを守って、正しいルールを設定する必要があります。

- `temperature`はSiteWiseアセットの測定値の名前と同じであること
- `temperature`がどの数字になっても、どれかのステートメント（式）に当てはまること（※temperature>40を作る時は、必ずtemperature<=40の式も作ってください）

![temperature-rule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/29bd1a74-7975-31ed-b00f-3f999da6f817.png)

以下の3つのルールを作成します

|ルール名|ステートメント|表示ルール|
|:--|:--|:--|
|temperature-rule|temperature<38|アイコン：情報|
|temperature-rule|temperature>=38|アイコン：エラー|
|visible-rule|temperature<38|不透明度：1|
|visible-rule|temperature>=38|不透明度：0|
|initializer-rule|temperature>=-999|不透明度：0|
|initializer-rule|temperature<-999|不透明度：1|

**10. タグにルールを適用する**

タグ（Usada-Pekora）を開いて、タグに表示ルールを設定します。

![tag-visible-rule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/5046a9f9-b6f6-3381-b052-64f267f2ec36.png)

|大項目|項目名|設定値|備考|
|:--|:--|:--|:--|
|タグ|デフォルトのアイコン||※空欄でOK|
|タグ|エンティティID|SwitchBotTemperature|※TwinMakerのエンティティがリスト表示されます|
|タグ|コンポーネント名|Sensor|※TwinMakerに登録したコンポーネント名がリスト表示されます|
|タグ|プロパティ名|temperature|※SiteWiseの測定値名を選びます|

**11. 吹き出しにルールを適用する**

吹き出しを選んで、「＋」から「モデルシェーダーを追加」をします

![model-shader.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/1bfb8371-66c8-f8f3-13e3-b745e54e86d8.png)

モデルシェーダーを設置すると、タグと同じように、3Dモデルの見た目をSiteWiseのデータに合わせて変えることができるようになります。

![model-shade-property.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/5a9097b4-e21f-4c72-0db0-ced063d49391.png)

ルールID以外はタグ（Usada-Pekora）の表示ルールと同じ内容を設定します。

ルールIDを`visible-rule`にすると、タグ（Usada-Pekora）が情報状態になっているときだけ、吹き出しが消えるようになります。

ルールIDを`initialize-rule`にすると、SiteWiseにデータが入っているときは吹き出しが非表示に、データがないときは吹き出しが表示されるようになります。

吹き出しを2枚重ねて、手前の「データはどこにあるぺこ？」には`initializer-rule`を、奥の「お風呂にお湯が入ったぺこ！」には`visible-rule`を設定します。そうすることで、`データがないとき`、`注水中`、`お湯はり完了後`のメッセージを切り替えることができます。

![fukidashi-double.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/e079f6cb-6f6a-4f9c-da2b-d0c47d2dfb17.png)

# iot-app-kitを使って、ブラウザ上でTwinMakerを表示する

iot-app-kitを使って、TwinMakerを読み込みます。

iot-app-kitについて説明すると、`TwinMakerで作ったプロジェクトを、Reactのウェブアプリで表示できるようにするOSS`です。AWSが開発、公開しています。

https://github.com/awslabs/iot-app-kit

**1. ソースコードをダウンロードしてください**

記事冒頭で紹介した、こちらのソースコードをgitからダウンロードしてください。

https://github.com/ShotaOki/bath-alarm-twinmaker

**2. npm installで必要なライブラリをインストールします**

ソースコードを落としたら、Reactのディレクトリに移動して、必要なライブラリをインストールします。

```bash
cd projects/react-web
npm install
```

**3. .envファイルを作って、必要な認証情報を登録します**

package.jsonと同じ場所に、.envファイルを作ります

![env-place-react-project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/9a048cf9-653b-bb68-775c-ed91de7a92a0.png)

.envファイルには、認証情報、TwinMakerのワークスペースの情報、SiteWiseの情報を書き込みます。

```text: .env
REACT_APP_AWS_ACCESS_KEY_ID="${AWSの認証情報: アクセスキーID}"
REACT_APP_AWS_SECRET_ACCESS_KEY="${AWSの認証情報: シークレットキー}"
REACT_APP_AWS_WORKSPACE_NAME="${TwinMakerのワークスペース名}"
REACT_APP_AWS_SCENE_NAME="${TwinMakerのシーン名}"
REACT_APP_AWS_REGION="${TwinMakerのリージョン: 例: us-east-1}"

REACT_APP_AWS_ENTITY_ID="${TwinMakerのエンティティID}"
REACT_APP_COMPONENT_NAME="${TwinMakerのコンポーネント名}"
REACT_APP_PROPERTY_NAME="${測定値のプロパティ名}"
```

ワークスペース名、シーン名、リージョンは、TwinMakerでシーンを開いたときのURLをコピーすれば、まとめて確認することができます。

```text
https://us-east-1.console.aws.amazon.com/iottwinmaker/home?region=${リージョン}#/workspaces/${ワークスペース名}/scenes/${シーンID}
```

TwinMakerのエンティティ名、コンポーネント名、プロパティ名は、タグの表示ルールを設定したときに出ていた値をそのまま設定すればOKです。

![tag-visible-rule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/acc639fd-9da8-58d7-2594-fa0812ba90b9.png)

**4. MMDファイルを配置します**

MMDモデルはgitに上げられないので、MMDモデルとモーションは、ご自身の環境でダウンロードしたものをpublic以下に配置するようにしてください。

この記事では、こちらのモデルとモーションをお借りしています。

兎田ぺこら【公式】　投稿者：ホロライブ運営さん
https://3d.nicovideo.jp/works/td88332

【MMD】手を振るモーション【モーション配布あり】　投稿者：yuaoさん
https://www.nicovideo.jp/watch/sm40489290

【MMD】移動モーションv1.3　投稿者：むつごろうさん
https://bowlroll.net/file/154665

**5. App.tsxを編集します**

App.tsxの70行目付近の処理を、自分の環境に合わせて書き変えます。

replaceTagToMMDは、TwinMakerにある`Usada-Pekora`のタグを、MMDファイルに置き換える処理です。MMDモデルの場所をpmxPathで指定してください。モーションデータはmotionMapのパスに配置します。

TwinMakerのデータの状態が変わると、bindOnStateChangeEventが呼ばれます。DefaultAnchorStatus.Errorが警告アイコン、DefaultAnchorStatus.Infoが情報アイコンのことです。

アイコンの状態に応じて、MMDで再生するモーションのモーション名を配列で返します。（※配列で返したものは同時に再生されます。MMDは表情と体のモーションでファイルが分かれていることがあるため、このような形にしています）

```typescript: App.tsx
      new SceneController(composerId, {
        /** TwinMakerのタグを上書きする */
        overrideTags(rootScene) {
          return {
            // TwinMakerのタグをMMDモデルに置き換える
            "Usada-Pekora": (ref, anchor) =>
              replaceTagToMMD(ref, anchor, getObject3DBySceneNodeRef)
                ?.create({
                  rootScene, // ルートになるシーン
                  scale: 0.088, // オプション: 表示スケール
                  angle: -20, // オプション: MMDの角度(単位はDegree)
                  pmxPath: "mmd/UsadaPekora/PMX/UsadaPekora.pmx", // MMDファイル
                  motionMap: {
                    // モーションファイル
                    "swing-hand":
                      "mmd/motion/手を振るモーション2/手振_スタンダード(右手).vmd",
                    "swing-hand-body":
                      "mmd/motion/手を振るモーション2/体_スタンダード(右手用).vmd",
                    "swing-hand-face":
                      "mmd/motion/手を振るモーション2/表情.vmd",
                    "motion-waiting":
                      "mmd/motion/5.待機モーション・その他/待機モーション/1.呼吸_(90f_移動なし).vmd",
                  },
                })
                .bindOnStateChangeEvent({
                  /** タグの状態が変わったのであれば通知を受ける */
                  onChangeState(mesh, model, state) {
                    // 初期化実行の時は、モーションを実行しない
                    if (state === SystemLoadingStatus.Init) {
                      return [];
                    }
                    // 今のタグの状態に応じて、モーションを変更する
                    switch (stringToDefaultAnchorStatus(state)) {
                      case DefaultAnchorStatus.Error:
                        // モーションファイルのキーを返却してモーション再生
                        // 配列で投げたモーションは同時に再生する（合成される）
                        // ※MMDは表情と体の一部のモーションが別ファイルで提供されることがあるため
                        return [
                          "swing-hand",
                          "swing-hand-body",
                          "swing-hand-face",
                        ];
                      default: // DefaultAnchorStatus.Info
                        // モーションファイルのキーを返却してモーション再生
                        return ["motion-waiting"];
                    }
                  },
                }),
```

タグをMMDファイルに置き換えるだけですので、ご自身の環境に合わせたものを作りこんでいただければと思います。

**6. 実行する**

以下のコマンドを実行して、Webサーバを立ち上げます。

```
npm run start
```

Chromeで`http://localhost:3000`を開いて、画面を確認します。

MMDモデルがタグの位置に立っていればOKです。

![pekora-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/c5f5c492-0c50-9ec4-c5be-84848ab90fd7.png)

> 補足：
> MMDの光源設定はTwinMakerの光源設定に比べてかなり明るく、そのまま表示するとMMDが白飛びしてしまうため、MMDの光源設定に合わせる処理を入れています。ですので、TwinMakerで表示した3Dモデルと比べると、やや暗い見た目で表示されるようになっています。

# お湯張りセンサーを作る

お湯張りセンサーはSwitchBotの`防水温湿度計`を使います。

![bousui-file-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/7d41cefb-8a53-14df-6189-cfeadc14f6d6.jpeg)

センサーの小ささを伝えるために、10円玉とサイズ比較をしています。ボンタンアメの箱に入るくらい、グリコのおまけの箱に入るくらいのサイズ感です。

**1. センサーの初期設定**

初期設定にはスマートフォンのSwitchBotアプリが必要になります。電池の絶縁フィルムを抜いて、ペアリングのボタンを押しながら、Switchbotアプリと紐づけます。

初期設定が完了するとアプリ上でMACアドレスが分かるので、それを手元にメモします。

**2. センサーで湯量を計れるように工作する**

- 円形のひも（輪ゴムでOK）
- チャック付きポリ袋
- ネックストラップ（首からぶら下げるストラップ）
- ビニルテープ

を用意します。どれも100円均一にあります。

センサーのストラップを通す場所に円形のひも（輪ゴム）を通します

![bousui-file-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/a1d78fec-af61-3679-41bb-cc29e8cac554.jpeg)

チャック付きポリ袋にセンサーを入れて、ひもの先を袋の口から出します

![bousui-file-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/8cc9aa16-a3ef-8727-7e4a-0fba97fa65ae.jpeg)

チャックをしめたら、ひもとチャックの隙間から水が入ってこないように、袋の口を念入りにビニルテープでふさぎます。ひもの端はビニルテープから出ている状態にします。

![bousui-file-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/d0d33f2f-0d04-2530-729c-90d301907010.jpeg)

ひもにネックストラップを付けます。

![bousui-file-5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/fdabfd9f-087e-7b36-9d6e-48b0540556b2.jpeg)

水栓にネックストラップをぶら下げれば完成です。

![bousui-file-6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/288ca64b-4f1b-e7a5-079b-248c30293d7c.jpeg)

袋が弱ってきたら、はさみで輪ゴムと袋を切って新しいものに交換してください（※ストラップをセンサーに直接結んでしまうとストラップを切ることになるので、切ってもいい輪ゴムを間に挟むようにします）。

**3. 測れるのかを見る**

（この手順はスキップ可能です）

下の画像は、お湯を入れて、センサーの温度がどう変化していくのかを確認したところです。

![bousui-file-7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/ae54e685-dca2-0fbd-5c0a-b1ec6f4328e9.jpeg)

湯気でじわじわと上がった後、センサーがお湯に沈んでからは一気に上昇します。温度センサーが38度を超えたら「センサーがお湯に沈んだ＝お湯張りが終わった」と判定できそうです。

**4. センサーデータの受信機をダウンロードする**

記事冒頭で紹介した、こちらのソースコードをgitからダウンロードしてください（TwinMakerの手順でダウンロード済みであればこの作業は不要です）。

https://github.com/ShotaOki/bath-alarm-twinmaker

> **補足: 受信機の仕様について**
> SwitchBotの開発元が、防水温湿度計のセンサーデータの受信機のソースコードを公開しています。今回利用する受信機は、それをWindowsで動かせるように書き変えたものになります。
> 
> https://github.com/OpenWonderLabs/python-host/blob/master/switchbot.py

**5. 受信機をセットアップする**

受信機はboto3を使ってSiteWiseにデータを転送します。もし認証情報の設定が済んでいないのであれば、あらかじめ設定をお願いします。

venvを使って、受信機のセットアップをします。

```bat
cd projects\python-sensor-gateway
python -m venv .
```

仮想環境の構築ができたら、仮想環境を立ち上げます。

```bat
Scripts\activate.bat
```

仮想環境に必要なライブラリをインストールします

```bat
pip install -r requirements.txt
```

.envファイルを作成します

```text: .env
MAC_ADDRESS=${Macアドレス}
ASSET_ID=${SiteWiseのアセットID}
PROPERTY_ID=${SiteWiseのプロパティID}
```

.envファイルを配置する場所はこちらです

```text
projects\python-sensor-gateway\src
```

![env-file-python.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/8f961f10-1152-f54d-cab1-cf67eb0e5303.png)

**7. 受信機を動かす**

Scripts\activate.batした環境で以下のコマンドを実行して、受信機を動かします

```bat
cd src
python app.py
```

> **補足:**
> センサーからした受信データではなく、任意に設定したデータをSiteWiseに送りたいときは、
> `python app.py --value 99.9`
> のように実行すると、データを1件だけ送信して終了します。
> `99.9`の部分は任意の数字です。

**6. お疲れ様です、これで全手順が完了しました**

![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/ee15c89b-70ee-a8d6-8309-37086bb2d373.png)

センサーの値に応じて、MMDが動いているはずです。

> **補足: 外出先でもTwinMakerでセンサ値を監視したいときの注意点**
> TwinMakerのプロジェクトをWebサーバにデプロイするときは、Amplifyなどを使ってログイン画面を設置して、AWSの認証情報をCognito経由で取るようにしてください（**絶対に固定値の認証情報をクラウドに上げないでください**）。

# あとがき

この記事はシリーズで続ける予定です

- 「ぺこら、エアコンつけて」で家電を操作する
- Azure系のAIと連携して、TwinMakerの中にいる3Dキャラと会話をする

についても記事を書く予定です。<font color="#CCC">ソニーのELF-SR2 空間再現ディスプレイとTwinMakerの連携も、お金と機会があったらやりたいです。。。</font>

TwinMakerは産業用のIoT基盤をベースにしており、対応するデータソースも幅広いです。
活用事例はまだ少ないのですが、メタバースをブラウザだけで簡単に作ることができる、フルセットのThree.jsを組み込めるだけでなく、スマートホームのセンサーを丸ごと取り込む、大量の監視カメラの映像を自分の目にする、といったことが簡単にできるサービスです。

自由度が高い一方で、TwinMakerはソースコードを書く場所が少ないことも特徴です。吹き出しを切り替えるような場所はローコードで動いていますし、データベースも意識することがありません。少ないコーディングで何でもできる、面白いサービスだと思っています。