自宅ではスマートリモコンのNatureRemoをスマートスピーカーのリモコンとして利用しているが、リモコン操作以外にも部屋の温度や湿度を定期的に測定する機能が付いている。今までほとんど気にする事はなかったが、せっかく測定してくれているので、測定したデータを可視化したり、統計取ったりしたいところ。そういったことを簡単にできるツールがないか探したところ、MetabaseというOSSを見つけた。
色々試してみるとMetabaseがとても使いやすかったので、こちらを利用してNatureRemoで収集したセンサーデータを可視化するダッシュボードをMetabaseで作成してみた。
Metabaseは分析対象のデータを保存するDBが必要なので、今回はNoSQLのDBであるMongoDBを利用した。 全体構成は以下の通り。
家にあるNatureRemoは、以下の第二世代のものを利用している。
MongoDBとMetabaseの構築
MongoDBとMetabaseの構築は、docker-composeを用いて行う。また、MongoDBのブラウザツールであるmongo-expressは、MongoDBのcliを利用することなくGUI操作でMongoDBを操作できて便利なので、こちらも合わせて構築する。
services: mongodb: image: mongo container_name: mongodb restart: always environment: MONGO_INITDB_ROOT_USERNAME: mongodb_user MONGO_INITDB_ROOT_PASSWORD: mongodb_pass ports: - 27017:27017 volumes: - ./mongo/db:/data/db - ./mongo/configdb:/data/configdb mongo-express: image: mongo-express container_name: mongo-express restart: always links: - 'mongodb:mongo' ports: - 8081:8081 environment: ME_CONFIG_MONGODB_ADMINUSERNAME: mongodb_user ME_CONFIG_MONGODB_ADMINPASSWORD: mongodb_pass metabase: image: metabase/metabase:v0.33.5 container_name: metabase ports: - 3333:3000 volumes: - ./metabase:/metabase.db
実行の際は、以下のコマンドを用いる。
docker-compose up -d
各ツールのエンドポイントは以下の通り。
名称 | エンドポイント |
---|---|
mongodb | localhost:27017 |
mongo-express | localhost:8081 |
metabase | localhost:3333 |
注意すべき点は、Metabaseのバージョン。Metabaseの最新のバージョンv0.38、v0.39ではMongoDBと接続後、MongoDB内のデータを取得する際に、DBのコレクション内にデータが存在するにも関わらずコレクション内のデータを見つけられない事象が発生し、MongoDBのデータを取得できなかった。 いろいろ試した結果v0.33.5では、データを取得しMetabaseでデータを可視化できたので、今回はこちらのバージョンを利用する。
Nature RemoのセンサーデータをMongoDBに保存
NatureRemoのセンサーデータを取得し、先ほど作成したMongoDBに取得したデータを保存するスクリプトをPythonで作成する。MongoDBに接続するライブラリはpymongo
を利用する。
pip install pymongo
NatureRemoのAPITokenの取得は、以下のURLに自身のアカウントでログインして行う。
作成したスクリプトを、以下に示す。
import argparse import requests import datetime as dt from pytz import timezone from pymongo import MongoClient class MongoRepository(object): def __init__( self, db_name, collection_name, username, password, host="localhost", port=27017 ): _client = MongoClient( "mongodb://%s:%s@%s:%d" % (username, password, host, port) ) _db = _client[db_name] # 認証情報を付与 _db.add_user( username, password, roles=[ { "role": "dbAdmin", "db": db_name, }, ], ) self.collection = _db[collection_name] def insert_list(self, insert_list) -> list: return self.collection.insert_many(insert_list) def get_room_info(api_token: str) -> {}: """部屋の温度の情報を取得する。 Args: api_token (str): NatureRemoのAPIToken """ headers = { "accept": "application/json", "Authorization": "Bearer " + api_token, } response = requests.get("https://api.nature.global/1/devices", headers=headers) return response.json() def collect_to_mongodb(api_token: str, mongo_client): """NatureRemoからデータを取得し、MongoDBに送信する。 Args: api_token (str): NatureRemoのAPIToken """ room_info_list = [] # 取得したデータをデバイスごとに整形 for room_info in get_room_info(api_token): request_at_date = timezone('Asia/Tokyo').localize(dt.datetime.now()).strftime('%Y-%m-%dT%H:%M:%SZ') tmp_room_info = { "name": room_info["name"], "newest_events": room_info["newest_events"], "updated_at": room_info["updated_at"], "request_at": request_at_date } room_info_list.append(tmp_room_info) print("room info: {}".format(room_info_list)) # mongodbへ保存 mongo_client.insert_list(room_info_list) print("save complete!") if __name__ == "__main__": # 引数の設定 parser = argparse.ArgumentParser() parser.add_argument("api_token", help="NatureRemoのAPIToken") parser.add_argument("mongo_username", help="ユーザ名") parser.add_argument("mongo_password", help="パスワード") parser.add_argument("mongo_db_name", help="保存するDatabase名") parser.add_argument("mongo_collection_name", help="保存するCollection名") parser.add_argument("--mongo_host", default="localhost", help="対象のMongoDBのホスト名") parser.add_argument("--mongo_port", default=27017, help="対象のMongoDBのポート番号") args = parser.parse_args() # Mongo Clientの初期化 mongo_client = MongoRepository( db_name=args.mongo_db_name, collection_name=args.mongo_collection_name, username=args.mongo_username, password=args.mongo_password, host=args.mongo_host, port=args.mongo_port, ) collect_to_mongodb(api_token=args.api_token, mongo_client=mongo_client)
NatureRemoのAPIは、アカウントに紐づく端末が複数ある場合に、それらの端末全ての情報を配列で返すようになっている。現在私の家にはNatureRemoは1台しかないが、今後増える可能性も考えてMongoDBに保存するデータを配列で渡せるようにしている。
18行目の_db.add_user(...
の箇所は、実行時にDBにアクセス権限を付与するもの。MetabaseからMongoDBのコレクションにアクセスする際に、DBに対してユーザのアクセス許可がないと認証の段階でエラーになるためその対応として入れた。
本来はDB作成時、一度だけ実行すれば問題ない。
実行時のコマンドを以下に示す。database名をhome
、collection名をenviroment
とした。
# 各引数の説明 $ python main.py ${NatureRemoのAPIToken} ${MongoDBのユーザ名} ${MongoDBのパスワード} ${保存するデータベース名} ${保存するコレクション名} # 実行のサンプル $ python main.py ${NatureRemoのAPIToken} mongodb_user mongodb_pass home environment room info: [{'name': 'Remo', 'newest_events': {'hu': {'val': 48, 'created_at': '2021-05-08T15:37:53Z'}, 'il': {'val': 203, 'created_at': '2021-05-08T13:43:51Z'}, 'mo': {'val': 1, 'created_at': '2021-05-05T23:39:27Z'}, 'te': {'val': 24, 'created_at': '2021-05-08T13:30:25Z'}}, 'updated_at': '2021-05-08T13:43:16Z', 'request_at': '2021-05-09T00:49:42Z'}] save complete!
実行後、データベースとコレクションが自動で作成され、そちらにNatureRemoから取得したデータが保存される。以下のmongo-expressのURLから、データが保存されていることを確認する。
以下の画面のように、取得したデータがmongo-expressの画面上で閲覧できていれば、保存に成功していることとなる。
上記のpythonスクリプトを、cronなどで15分おきに実行するように設定することで、15分ごとのセンサーデータの値をMongoDBに保存できる。
Metabaseでダッシュボードの作成
MongoDBと接続
Metabaseには、以下のURLからアクセスする。
初回アクセス時はユーザの作成が求めらるので、任意のユーザを作成すること。ユーザ作成完了後、接続するMongoDBの設定を行う。 入力内容の例は、以下の通り。
「ホスト」の箇所は、今回はdocker-compsoeを利用して構築していることからdockerのdnsで名前解決されるため、localhostではなく、container名を指定する(今回の場合はmongodb
を指定)。
加えて、Metabase+Mongoの接続の際は、こちらの方の説明にあるようにいくつかハマりポイントがあるので注意。
特にMongoDBでユーザ認証を利用する場合に、Authentication Database
の欄に認証用のdatabase(デフォルト設定のままであればadmin
)を入力する必要がある点などは忘れがちなので、MongoDBに認証を設定した場合は確認しておくこと。
ダッシュボードを作成
MongoDBとの接続が完了したら、画面上部の「照会する」パネルからグラフを作成していく。
ここでも一つ注意点があり、今回のようなネストされたデータの場合、ネストされているカラムが、グラフ作成時に選択可能なカラムの一覧に表示されない事象が発生する。この場合、グラフを作成しようとしても、ネストされた値が選択肢に表示されないので、グラフを作成することができない。 それを解決するためには、画面右上の「エディターを表示する」より、ネストされたカラムを手動で表示させる必要がある。
今回であれば、NatureRemoから取得したデータのうち温度に関する情報を利用したいため、赤枠で囲った「Newest Event > Te > Val」と「Newest Event > Te > Created At」にチェックを入れ、Metabase にカラムとして認識させた。
その後、Metabaseの画面に従いこのようなグラフを作成した。
最終的に照度や湿度も合わせてダッシュボードを作成した。自動更新機能があるため、リアルタイムに近い情報が常に確認できてIoTのようなセンサーデータの表示にも便利。
終わりに
Metabaseをほとんど触ったことなかったが、指示に従うだけで簡単にグラフを設定できてとても便利だった。一部バグとみられる箇所があったり、グラフ作成も細かい箇所の調整ができなかったりと不具合はあるが、開発も頻繁に行われているため今後の成長に期待。