DYSON のデータを MQTT プロトコルでモニタリングした話
はじめに
DYSON の羽根の無い扇風機。
家にあったのですが、どうやら PM2.5 や NOx (窒素酸化物) の濃度が表示されており、空気清浄機能も付いていたらしいと知りました。
DYSON のディスプレイ
アプリで色々な項目を見ていると当然、これを記録してグラフにしたり日付ごとの変化を見たいな…と思い始めます。
調べると同じようなことをしている記事があり、PC から操作やデータ取得ができるようなのですが、 DYSON のマシンバージョンによって設定の確認方法や見られる情報が異なるようで、いくつか詰まりポイントがあったので記事に残しておきます。
環境
DYSON のバージョン: Dyson Purifier Hot + Cool Gen1 HP10
PC のバージョン: M3 chip Macbook Air, MacOS 14.5
スマホ: iPhone
パケットキャプチャ
DYSON は "MQTT" という通信プロトコルでローカルネットワーク内の通信をしています。
MQTT はひとことで言うと pub/sub 形式のメッセージ送信プロトコルで、DYSON の場合 DYSON 本体がブローカーと呼ばれるサーバーの役割を担い、スマホアプリがクライアントとしてそこに接続しています。
そのため、PC がクライアントとして DYSON に接続する際、スマホアプリと同じ認証情報を付与する必要があります。
[参考URL1] によると、この認証情報は DYSON 本体の Wifi SSID・パスワードから生成できると書いてあります。しかし、うちのマシンは Wifi で接続するタイプのバージョンではなかったため、この方法は使えませんでした。
そこで、[参考URL2] にあるパケットキャプチャを使う方法で認証情報を取得しました。
iPhone アプリと DYSON との通信をパケットキャプチャするために、Mac につないで "Remote Virtual Interface" という方法を使います。よくは分かっていないですが、[参考URL3] の方法でセットアップしました。
ここで、bootstrap look up(): 1102
のようなエラーが出た際、[参考URL3] に書いてある以下のコマンドでは解決しませんでした。 (そもそも /System/Library/LaunchDaemons/com.apple.rpmuxd.plist
が存在しませんでした)
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.rpmuxd.plist
僕の環境では以下の方法で csrutil
を無効化することで解決しました。([参考文献4])
- 「command + R」を押しながら Mac を起動し、リカバリーモードに入る
- 途中で上のメニューからターミナルを開く
-
csrutil disable
を実行
この後、以下コマンドが正常実行され Remote Virtual Interface が作成されました。
rvictl -s <UDID>
認証方法の抽出
引き続き [参考URL2] の方法で認証情報を抽出します。
Wireshark を起動すると、作成した Remote Virtual Interface が選択できるようになっています。
キャプチャを開始し、iPhone の DYSON アプリから適当に風量変更などの操作をします。
キャプチャを見ると、MQTT
プロトコルの通信があるため DYSON の通信パケットは簡単に分かりました。
参考URLのように、connect パケットから認証情報を抜き出します。
以下の情報まで分かればこの後の PC からの通信を行うことができます。
- DYSON の IPアドレス
- MQTT の User Name (connect パケットから取得)
- MQTT の Password (connect パケットから取得)
- MQTT の topic (適当な MQTT パケットから、
527/ZZ1-JP-XXXXXXXX/command
などのトピック名を取得)
wireshark 画面
PC から Python で情報を取得
mqtt-paho
パッケージを使います。
以下は定期的にデータを取得する簡単なサンプルコードです。
from datetime import datetime
import json
import time
import paho.mqtt.client as mqtt
# dyson settings
DYSON_HOST = '192.168.XXX.XXX'
DYSON_PORT = 1883
DYSON_USERNAME = 'ZZ1-JP-XXXXXXX'
DYSON_PASSWORD = '(抽出したパスワード)'
dyson_command_topic = f'527/{DYSON_USERNAME}/command'
dyson_status_topic = f'527/{DYSON_USERNAME}/status/current'
# broker接続時
def on_connect(client, userdata, flags, respons_code):
print('status : ' + str(respons_code))
client.subscribe(dyson_status_topic)
def on_message(client, userdata, msg):
data = json.loads(msg.payload.decode())
if data['msg'] == 'ENVIRONMENTAL-CURRENT-SENSOR-DATA':
print(f'センサデータ: {data["data"]}')
if __name__ == '__main__':
dyson_client = mqtt.Client(protocol=mqtt.MQTTv311)
dyson_client.username_pw_set(DYSON_USERNAME, DYSON_PASSWORD)
dyson_client.on_connect = on_connect
dyson_client.on_message = on_message
dyson_client.connect(DYSON_HOST, port=DYSON_PORT, keepalive=60)
dyson_client.loop_start()
# 10秒ごとにセンサデータのリクエストを送信
while True:
dyson_client.publish(dyson_command_topic, json.dumps({
'msg': 'REQUEST-PRODUCT-ENVIRONMENT-CURRENT-SENSOR-DATA',
'time': datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'),
'mode-reason': 'LAPP',
}))
time.sleep(10)
以下のようなデータが取得できました。
{
'hact': '0064',
'noxl': '0000',
'p10r': '0003',
'p25r': '0003',
'pm10': '0001',
'pm25': '0003',
'sltm': 'OFF',
'tact': '2993',
'va10': '0017'
}
それぞれ以下のような意味のようです。
フィールド | 意味 |
---|---|
hact | 湿度 (humidity) (%) |
noxl | 窒素酸化物の濃度 (NOx) |
p10r | PM10 の濃度 (高精度) |
p25r | PM2.5 の濃度 (高精度) |
pm10 | PM10 の濃度 |
pm25 | PM2.5 の濃度 |
sltm | スリープタイマー (Sleep Timer) |
tact | 気温 (tempreture) (絶対温度 x 10) |
va10 | VOC の濃度 |
(p10r
, pm10
部分: 参考 GitHub comment)
フィールドごとの数値の意味やバージョンごとの取得内容は [参考URL5] の Qiita 記事も参考になります。
これでセンサデータが取得できるようになりました!ラズパイでデータを取得してクラウドに保存してグラフ表示して…と妄想がはかどりますね。
Discussion