Home Assistantへの自作CO2センサのMQTTによるデータ送信

以前、温湿度CO2センサを製作しました。

dededemio.hatenablog.jp

このセンサ値を、Home Assistant上で表示したいと思います。 これにはいくつかやり方がありますが、今回はMQTTでHome Assistantにデータを定時送信することとし、そのためのプログラムを作成しました。

今回作成したプログラムは、以前のCO2センサレポジトリに追加してあります。

github.com

1. MQTTとは?

MQTT(Message Queuing Telemetry Transport)は軽量なメッセージングプロトコルで、以下の特徴があります。

  • シンプル・軽量で効率的
  • パブリッシャ・サブスクライバモデルであり、送信元・受信元を柔軟に設定可能
  • トピックを指定して送受信。トピックは階層構造が可能
  • 通信品質QoS指定可能
  • 多数言語で利用可能。PythonならPahoが有名

クライアント(パブリッシャ)→サーバ(ブローカー)→クライアント(サブスクライバ)という流れで通信します。今回は、パブリッシャがCO2センサ、サブスクライバがHome Assistantで、MQTTブローカーとしてHome AssistantアドオンのMosquittoを用います。

2. HAへのMQTTブローカーの追加

まず、アドオンをインストールします。

HomeAssistantの設定→アドオン→アドオンストアでMosquitto brokerを検索しインストールボタンを押します。インストールできたら、設定→アドオン→Mosquitto broker→情報タブで開始し、動作しているか、ログ出力結果を確認します。

INFO: Successfully send discovery information to Home Assistant.INFO: Successfully send service information to the Supervisor.がログ出力されたらOKです。

次に、MQTTブローカー用のユーザーを用意します。

Home Assistantの設定→メンバー→メンバーを追加→メンバーにログインを許可する、でユーザー名・パスワードを入力します。メンバー名はユーザー名と同一、ローカルからのみログイン可能はOFF、管理者もOFFとしておきます。

[設定]→[デバイスとサービス]→[統合]で、発見されたMQTTの設定ボタンを押し、「アドオン: Mosquitto broker によって提供される、MQTT brokerに接続するようにHome Assistantの設定をしますか?」で送信をクリックします。

MQTTが発見に表示されてない場合は、Home Assistantを再起動するとよいです。

3. HAへのMQTTデバイスの追加方法

MQTTデバイス追加方法は2つあります。

  • configuration.yamlを修正して手動セットアップする
    • こちらは以前ECHONETLite2MQTTを使ったときに設定した方法です。
    • configuration.yamlの編集が必要ですが、確実にHome Assistantにデバイスを追加できます。

dededemio.hatenablog.jp

  • configuration dataを送信して自動セットアップする(Auto Discovery)
    • MQTTでconfigの内容を事前に一度送信した後、通常データ送信することで、configuration.yamlに記述しなくともHome Assistant側で自動で認識させることができます。
    • 接続する先が変わっても、デバイスのconfigを一部変えればすぐ動くので便利です。

今回は、Auto Discoveryによる自動セットアップを試してみます。

4. MQTTデバイスの自動セットアップ

Home AssistantにおけるAuto Discoveryでは、以下のトピックにデータを送ると、MQTTデバイスを自動で検出してくれるようです。まず、こちらにconfig topicを送信します。

  • <discovery_prefix>/<component>/[<node_id>/]<object_id>/config
    • 例:`homeassistant/sensor/plant_sensor_1/temperature/config
    • config topicの送信例は例えば以下があります。

stevessmarthomeguide.com

github.com

あとは、Home Assistant Communityを探すと以下のようなコード例を見つけることができます。

上記を参考に、Pythonのpaho-mqttを使って、configトピックにデータ送信するスクリプトを作成してみます。 *1

import json
from mqtt_pub import MQTT_Pub # MQTTパブリッシャクラス

payload={'name': '温度',
    'state_topic': 'homeassistant/sensor/co2meter_raspi',
    'value_template': '{{ value_json.temperature }}',
    'unit_of_measurement': '°C',
    'unique_id': 'co2meter_raspi_temperature',
    'object_id': 'co2meter_raspi_temperature',
    'suggested_display_precision': 1,
    'icon': 'mdi:thermometer',
    'device': {'identifiers': 'co2meter_raspi', 'manufacturer': 'I-O DATA DEVICE, INC.', 'model': 'UD-CO2S', 'name': '書斎CO2センサ'}
topic = "homeassistant/sensor/co2meter_raspi_temperature/config"
client = MQTT_Pub()
client.read_config(config_file)
client.set_client_id("co2meter_raspi")
client.set_topic("homeassistant/sensor/co2meter_raspi)
client.connect_mqtt()
client.client.publish(topic, json.dumps(payload), qos=1, retain=True).wait_for_publish()

MQTT_Pubは、わたしがpaho-mqttで作成したMQTTのパブリッシャクラスです。configファイルとして、以下のような情報を記載して渡す必要があります。(詳細はレポジトリを参照)

  • ユーザー(Home AssistantでMQTTブローカー用に追加したユーザー)
  • host = 192.168.XX.XX(MQTTブローカーのIPアドレス
  • port = 1883
  • client_id = co2meter
  • topic = co2meter

これを使い、センサ情報をpayloadに辞書型で格納し、それをjsonとしてpublishすればよいです。

Home Assistantでは、その中の固有のセンサであることを示すため、unique_id、object_idを指定しておく必要があります。複数のセンサでid重複を避けるため、raspiの部分はセンサ固有の値とする必要があります。わたしはPCのネットワーク名を用いましたが、例えばMACアドレスなど、なんらか固有の値を使えばよいです。

そのほかの注意点としては、以下があります。

  • 他のコード例を見ると、state_topicは自由に設定できるように見えますが、homeassistant/sensor...等にしないとAuto discoveryで発見してもらえませんでした。homeassistantと、センサtypeをつけないといけなさそうです。
  • configトピックを送信するときは、必ずretain=Trueにします。これを入れておかないと、Home Assistantが再起動したときにconfig情報が認識されず、MQTTデバイスが利用不可になってしまいます。retain=Trueを入れておけば、Home Assistant再起動時にはconfigが再送されて、デバイスを継続して利用できます。*2
  • config送信時はQoS通信品質を1にしておくと良さそうです。Config以外のデータ送信は失敗してもいいので0でいいですが、configは確実性を高くしたいので。

5. MQTTによるデータ送信

通常のMQTTのデータ通信は様々なスクリプト例を見つけることができるので、それを参照すればいいです。

以下のような形で、1分毎にデータの最新値をCSVファイルから読みに行き、payloadに格納して送信します。

base_minute = datetime.now().minute
while True:
    time.sleep(1)
    # 1分毎にデータ取得->送信
    if base_minute != datetime.now().minute:
        # 最新値の取得
        dt, temperature, humidity, co2 = read_data()
        # 送信データの作成
        payload = {
            "time": dt,
            "temperature": temperature,
            "humidity": humidity,
            "co2": co2
        }
        client.publish(payload)
        base_minute = datetime.now().minute

1分毎実行を、1秒毎に現在時刻の分の値をチェックするという泥臭い方法でやってますが、これには理由があります。

pythonには、定期実行をサポートするパッケージがいくつか存在するのですが、少なくともわたしが触った範囲では、1分といいつつ1分以上の間隔で実行したり、ブレがあったりなど、定時性を担保しない方法しかありませんでした。毎分、必ず分が異なる時間でデータを送信したかったので、このような方法をとってます。

ただし、送信データの時刻は、そのデータが取得された時刻にしなければならないので、CSVのデータが記録された時刻をデータと一緒に送信するようにしています。

これで、以下のようにHome Assistantでもしっかり自作センサのデータが見れるようになりました。

おわりに

ここまで頑張って、自作CO2センサーをHome Assistantにつなげたわけですが、SwitchBotからCO2センサーが登場してしまいました。

Home Assistant側のインテグレーションのアップデートがされていれば、簡単につながるのではないかと思います。温湿度CO2を測ってHome Assistantに統合するなら、こちらを購入したほうが簡単に済みそうです。

*1:paho-mqttは、24/2のバージョン2.0.0で破壊的変更があった模様。旧来の情報を参考にスクリプト作成するため、2.0.0未満をインストールする

*2:MQTT Discovery: to retain or not? - Configuration - Home Assistant Community