💨

DYSON のデータを MQTT プロトコルでモニタリングした話

2024/10/27に公開

はじめに

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])

  1. 「command + R」を押しながら Mac を起動し、リカバリーモードに入る
  2. 途中で上のメニューからターミナルを開く
  3. 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 記事も参考になります。

これでセンサデータが取得できるようになりました!ラズパイでデータを取得してクラウドに保存してグラフ表示して…と妄想がはかどりますね

参考URL

  1. https://qiita.com/ta_endo/items/86d1e7807cccda96633d
  2. https://aakira.hatenablog.com/entry/2016/08/12/012654
  3. https://zenn.dev/yken/articles/c00bbe7c365043
  4. https://qiita.com/mochidan/items/dc290bbb451114a34a3a
  5. https://qiita.com/KenNagami/items/af259863cd302af97360

Discussion