ホームアシスタント、Raspberry Pi、GridDBクラウドを用いたカリフォルニア州の大気質モニタリング

人類の(精彩を欠いた)努力にもかかわらず、気候変動は遍在し、否定できない力となっています。世界の平均気温の上昇に伴う副作用はたくさんありますが、今日私が注目したいのは、山火事とその周辺地域の大気質への影響です。100エーカーの山火事がカリフォルニアの森林を焼き尽くすと、瓦礫や粒子状物質はすべて大気中に蹴り上げられ、潜在的に危険な吸入可能物質となります。

もちろん、私たちが呼吸する空気の質には、他にもさまざまな要因があります。私にとっては、たとえ近くに山火事があってAQI(大気質指数)が急上昇していなくても、その時々の大気の質について情報を得、注意する理由があるということです。

プロジェクト

この記事のために、私たちは家の中の空気の質を測定することに興味がありました。具体的には、空気の質のデータをライブで読み取り、それを永続的なストレージに保存し、空気の質がある閾値を超えたら住人に通知したいと考えました。

目標を達成するために、私たちはGridDBクラウドをHome Assistantとして知られるオープンソースのスマートホーム・ソリューションと統合しようとしました。ホーム・アシスタントは、「モノのインターネット(IoT)エコシステムに依存しない統合プラットフォームであり、ローカル制御に重点を置いたスマートホームデバイスの中央制御システム」です。通常、エンド・ユーザーはこのソフトウェアをホーム・サーバーにインストールし、インターネットに接続された物理デバイスの制御を行います。例えば、Home Assistantをインストールし、スマート電球やスマートロボバックなどを制御するスマートハブとして機能させることができます。

ホーム・アシスタントの優れた点は、その汎用性と柔軟性にあります。例えば、Home Assistantは完全にオープンソースで開発されているため、ユーザーは特定のユースケースのために独自のソリューションを開発し、独自のオートメーションを構築することができます。私たちの場合、フィルターを通していない生のセンサー・データを1読み取り/秒の解像度でGridDB Cloudに保存し、Home Assistantを使って必要な値をクエリし、最終的にはセンサーと同じ空間に住んでいる人に、空気の質が危険なレベルに近づいていることを具体的な方法で通知できるようにすることに興味がありました。

プロジェクトの詳細とPM1粒子

アイデアは次のようなもので、シングルボードコンピューターを使ってセンサーに接続し、生の大気質データを取得します。そして、その生データを1粒子/秒の解像度でGridDBクラウドに送信します。ホーム・アシスタントはデータを照会し、目的を達成するために必要な情報をダウンサンプリングすることができます。

まず、大気質センサーAdafruit PMSA003I Air Quality Breakoutをラズベリーパイに接続し、pythonスクリプトを使って、センサーの測定値をHTTPリクエストで1秒ごとに読み取り、GridDBクラウドに送信します。データはGridDB Cloudの永続ストレージに保存されているので、HTTPリクエストでデータセットにSQL Select文でクエリを発行することができます。今回のケースでは、Home Assistantを使用してデータセットにクエリを発行し、私たちがいる場所の空気中の粒子状物質が通常より多いことを警告したいと思います。また、Home Assistantのダッシュボードに、センサーの測定値をローリングアベレージで簡単に表示したいと思います。

余談ですが、この特殊なセンサーの興味深い点は、1ミクロン(PM1`と表示されている)という小さな物質を読み取ることができるということです。この粒子は非常に小さいため、肺組織を貫通して直接血流に入ることができます。この粒子は非常に小さいため、上記のリンク先のセンサーを含む特殊な機器でしか検出できません。PM1の測定値を容易に入手できるソースがないため、この粒子は、そのレベルの上昇を家庭の住人に通知するためのクエリの焦点となります。

プロジェクト要件

このプロジェクトに参加するには、以下のものが必要です:

  1. 1.GridDBクラウドアカウント a. GridDBクラウドアカウントの登録方法はこちらを参照: GridDBフリープランブログ
  2. 大気質センサー
  3. センサーをコンピュータに接続する手段(シングルボードなど)

必要なハードウェアをセットアップし、GridDBクラウドデータベースに接続できるようになりましたら、ここからソースコードを入手することができます: GitHubからソースコードを入手し、センサーの測定値を送信するための簡単な python スクリプトを実行します。

ハードウェアの接続

私の場合、STEMMAコネクタが付属した空気品質センサーを購入しました。Raspberry Pi 4に接続するために、STEMMA Hat とSTEMMAワイヤーを購入しました。STEMMAハットを購入したくない場合は、センサーにピンをハンダ付けし、ブレッドボードを使ってRaspberry PiのGPIOピンに接続することもできます。この方法を取る場合、我々のソースコードで提供されているpythonスクリプトを変更する必要があるかもしれません — この空気品質センサーを物理的に接続する方法については、彼らのドキュメントページで詳しく読むことができます: Docsを参照してください。

ソフトウェア

それでは、このプロジェクトを動かすソフトウェアに注目してみましょう。

コンテナ作成とデータ送信のためのPythonスクリプト

何よりもまず、センサーの測定値を HTTP リクエストとして送信するスクリプトについて説明します。これは、ada fruit のドキュメントで直接提供されているスクリプト例を修正したものです。受信データのデータ構造は、すでに便利なディクショナリにレイアウトされているので、コンテナ作成時にどのようにスキーマをレイアウトしたかを繰り返し、値を一致させるだけで大丈夫です。それではまず、コンテナ作成スクリプトをご覧いただきましょう:

import http.client
import json

conn = http.client.HTTPSConnection("cloud5197.griddb.com")
payload = json.dumps({
  "container_name": "aqdata",
  "container_type": "TIME_SERIES",
  "rowkey": True,
  "columns": [
    {
      "name": "ts",
      "type": "TIMESTAMP"
    },
    {
      "name": "pm1",
      "type": "DOUBLE"
    },
    {
      "name": "pm25",
      "type": "DOUBLE"
    },
    {
      "name": "pm10",
      "type": "DOUBLE"
    },
    {
      "name": "pm1e",
      "type": "DOUBLE"
    },
    {
      "name": "pm25e",
      "type": "DOUBLE"
    },
    {
      "name": "pm10e",
      "type": "DOUBLE"
    },
    {
      "name": "particles03",
      "type": "DOUBLE"
    },
    {
      "name": "particles05",
      "type": "DOUBLE"
    },
    {
      "name": "particles10",
      "type": "DOUBLE"
    },
    {
      "name": "particles25",
      "type": "DOUBLE"
    },
    {
      "name": "particles50",
      "type": "DOUBLE"
    },
    {
      "name": "particles100",
      "type": "DOUBLE"
    }
  ]
})
headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Basic <redacted>'
}
conn.request("POST", "/griddb/v2/gs_clustermfcloud5197/dbs/B2xcGQJy/containers", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))</redacted>

スキーマについては、センサーの読み取り値から返されるaqdata辞書に1:1でマッピングするだけです。これらのデータポイントをすべて使うつもりはなくても、データの読み出しはとても小さいので、そのままにしておきます。

次に、GridDB Cloud インスタンスにセンサーの読み取り値をプッシュするスクリプトを見てみましょう。このスクリプトは、1 秒ごとにセンサー・データを読み取り、1 秒ごとに HTTP Request を発行して、そのデータをクラウドにプッシュします。

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

"""
Example sketch to connect to PM2.5 sensor with either I2C or UART.
"""

# pylint: disable=unused-import
import time
import datetime
import board
import busio
from digitalio import DigitalInOut, Direction, Pull
from adafruit_pm25.i2c import PM25_I2C


import http.client
import json

conn = http.client.HTTPSConnection("cloud5197.griddb.com")
headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Basic <redacted>'
}

reset_pin = None
i2c = busio.I2C(board.SCL, board.SDA, frequency=100000)
# Connect to a PM2.5 sensor over I2C
pm25 = PM25_I2C(i2c, reset_pin)

print("Found PM2.5 sensor, reading data...")

while True:
    time.sleep(1)

    try:
        aqdata = pm25.read()
        # print(aqdata)
        current_time = datetime.datetime.utcnow().replace(microsecond=0)
        now = current_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
       # print(now)
        temp = []
        temp.append(now)
    except RuntimeError:
        print("Unable to read from sensor, retrying...")
        continue
    
    for data in aqdata.values():
        temp.append(data)
    payload = json.dumps([temp])
    print(payload)
    conn.request("PUT", "/griddb/v2/gs_clustermfcloud5197/dbs/B2xcGQJy/containers/aqdata/rows", payload, headers)
    res = conn.getresponse()
    data = res.read()
    print(data.decode("utf-8"))</redacted>

上記で説明したように、このスクリプトには派手さや特別なことは何もありません。単純にセンサーデータを読み込んで、タイムスタンプを付けてすぐにクラウドにプッシュするだけです。ただ、注意しなければならないのは、GridDBクラウドのデフォルトはUTC時間なので、整合性を取るために、データ結果に添付するタイムスタンプをUTCに合わせるように変更しました。

クラウド上でデータを利用できるようになったので、次は他のテクノロジーとの統合に移りましょう。

ホームアシスタント

ホームオートメーションソフトウェアには様々な種類があり、多くの企業が独自のハブを自社製品と一緒に販売しています。ホーム・アシスタントがユニークなのは、1.完全にオープンソースであること、2.あらゆる物理的・仮想的デバイスと統合できるように作られていることです。例えば、私個人の自宅環境では、ホーム・アシスタントはラズベリー・パイ上で動作しています。他の物理デバイスと通信するために、Zigbee/Z-Wave USBスティックをインストールしています。ZigbeeとZ-Waveは、スマートホームデバイスがハブと通信するために使用するプロトコルです。例えば私の場合、スマート電球のほとんどはZigbeeで通信しています。

いずれにせよ、Home Assistantを通じて、物事を実行するための様々なスクリプト/オートメーションを作成することができます。例えば、起床時に寝室の照明を1%点灯させるとか、ランチタイムにSpotifyのプレイリストを毎日再生する、などなど。空気の質センサーの場合、センサー・データに対して直接HTTPクエリーを送信し、測定値がある閾値より高ければ「何か」を実行することができます。このブログでは、リビングルームの電球を「点灯」させ、過去1時間以内に「pm1」粒子がある閾値より高ければ「赤色」になるように設定しました。もちろん、ホーム・アシスタントは柔軟なので、メールや電話のプッシュ通知、あるいはロボバックのスイッチを入れるなど、どんな通知方法でも設定できます!

では、次に進むために、まずセンサーの読み取り値に関するクエリーを作成し、Home Assistantを通じてHTTPリクエストを行う方法を学び、クエリーから返されたデータに対してどのようにアクションを起こすかを学びます。センサー読み取り値の平均が高い場合の通知システムを設定した後、Home Assistantのダッシュボードにすべてのセンサー読み取り値を表示します。

クエリーの作成

まず、クエリを設定しましょう。ざっと調べたところ、「pm1」粒子は私たちの健康を最も脅かす可能性があります。

始める前に、cURLまたはPostmanを使って、求めているものが得られるまでHTTPクエリをテストしてみましょう。私の場合、結果に満足するまでpostmanを使った: SELECT AVG(pm1) FROM aqdata WHERE ts > TIMESTAMP_ADD(HOUR, NOW(), -1) AND ts < NOW() AND pm1 > 10 “. このクエリは過去1時間のデータを調べ、平均値が10以上であればデータを返します。pm1パーティクルの不健全な量について厳密に調査したわけではありません。しかし、クエリができたので、Home Assistantを使ってHTTP REQUESTS`を作成する方法を見つけることができます。

ホームアシスタントでHTTPリクエストを作成する

Home Assistantの/configディレクトリ内には、ソフトウェア自体の設定や自動化の変更を行うためのyamlファイルがたくさんあります。私たちの場合、設定yamlの中にrest_commandと呼ばれるものを追加したいです。また、rest_command から返されたデータに基づいて、どのようなアクションを実行するかの自動化を追加したいです。すべての yaml ファイルは上記のリンク先の GitHub ページにあるソースコードに含まれていることに注意してください。

それでは、HTTP Request を行うために必要な configuration.yaml ファイルを以下に示します:

#configuration.yaml
rest_command:
  griddb_cloud_get_aqdata:
    url: https://cloud5197.griddb.com/griddb/v2/gs_clustermfcloud5197/dbs/B2xcGQJy/sql
    method: post
    content_type: "application/json"
    headers:
      authorization: "Basic <redacted>"
    payload: '[{"type" : "sql-select", "stmt" : "SELECT AVG(pm1) FROM aqdata WHERE ts > TIMESTAMP_ADD(HOUR, NOW(), -1) AND ts < NOW() AND pm1 > 10 "}]'</redacted>

ここでは、クラウドインスタンスに HTTP Sql Select リクエストを行うために必要なものをすべて含めています。ここでは、rest_commandgriddb_cloud_get_aqdata という名前を付けているので、scripts ファイルでこの service を直接呼び出すことができます。

#scripts.yaml
get_griddb_data:
  sequence:
    - service: rest_command.griddb_cloud_get_aqdata
      response_variable: aqdata
    - if: "{{ aqdata['status'] == 200 }}"
      then:
        - alias: Parse data
          variables:
            results: "{ {aqdata['content']['results'] }}"
        - if: "{{ results != 'null'}}"
          then:
            service: light.turn_on
            target:
              entity_id: light.living_room
            data:
              rgb_color:
                - 240
                - 0
                - 0

見ての通り、サービスとして rest_command.griddb_cloud_get_aqdata を呼び出しています。これはHTTP Requestを実行し、結果のデータを解析します。もし結果がNULL(つまり閾値を超えるデータがない)なら何も起こらないですが、もし何らかのデータが得られたなら、何らかのアクションを起こすことができます。

yamlファイルがどのように機能するかもう少し説明すると、コンフィギュレーションyamlではサービスを作成することができます。scriptsファイルは、異なる場所で何度も実行される可能性のあるアクションのためのもので、ソフトウェアにおける関数に少し似ています。最後に、スクリプトを実行するトリガーを設定する automations.yaml を使用します。つまり、10分ごとにホーム・アシスタントが過去1時間のpm1の平均値を調べ、高すぎる場合はアクションを起こします。

#automations.yaml
- id: '1713475588605'
  alias: Get GridDB Data
  description: get the griddb data
  trigger:
  - platform: time_pattern
    minutes: /10
  condition: []
  action:
  - service: script.get_griddb_data
  mode: single

ここでは、オートメーションがGridDBのデータを取得するためにスクリプトを呼び出していることがわかります。また、トリガーは 10 分ごとであることもわかります。

センサーデータをホームアシスタントダッシュボードに表示する

最後に、センサーの測定値をホームアシスタントダッシュボードに直接表示します。これにより、すべてのホームユーザーが常に測定値を認識できるようになります。これを行うには、sensors.yaml ファイルを使用し、センサーの平均センサーデータを読み取る HTTP リクエストの新しい仮想「センサー」を確立する必要があります。そのためには、新しいSQLクエリを作成する必要がある。今回は、過去24時間の平均値を使用することにしましょう。そのクエリは以下のようになります: SELECT ROUND(AVG(pm1)),ROUND(AVG(pm25)),ROUND(AVG(pm10)),ROUND(AVG(particles03)),ROUND(AVG(particles05)),ROUND(AVG(particles10))、 ROUND(AVG(particles25)),ROUND(AVG(particles50)),ROUND(AVG(particles100)) FROM aqdata WHERE ts > TIMESTAMP_ADD(DAY, NOW(), -1) AND ts < NOW()`。単純に過去 1 日間の丸めた平均をすべて取得し、ダッシュボードに表示するためにその情報を使用します。

#sensors.yaml
- platform: rest
  resource: https://cloud5197.griddb.com/griddb/v2/gs_clustermfcloud5197/dbs/B2xcGQJy/sql
  method: POST
  headers:
    authorization: "Basic <redacted>"
    Content-Type: application/json
  payload: '[{"type" : "sql-select", "stmt" : "SELECT ROUND(AVG(pm1)),ROUND(AVG(pm25)),ROUND(AVG(pm10)),ROUND(AVG(particles03)),ROUND(AVG(particles05)),ROUND(AVG(particles10)),ROUND(AVG(particles25)),ROUND(AVG(particles50)),ROUND(AVG(particles100)) FROM aqdata WHERE ts > TIMESTAMP_ADD(DAY, NOW(), -1) AND ts < NOW() "}]'
  value_template: "{{ value_json[0].results[0][0] }}"
  name: "Particle Matter 1"
  scan_interval: 3600</redacted>

ここでは、このセンサーをエンティティとして選択したときに表示される結果として、 value_template を使用しています。残念ながら、1つのHTTP Requestを複数の値で使用する方法が分からなかったので、それぞれの値を独自のHTTP Requestとして作成する必要がありました。例えば、上の例は pm1 に対するもので、コンテナスキーマにより結果配列のインデックス 0 になっています。

これでセンサーのセットアップが完了したので、ダッシュボードに行き、表示するセンサーを追加することができます。edit(右上にある鉛筆)をクリックし、add card(カードを追加)、glance card(カードを一目で見る)の順にクリックし、手動ですべてのセンサーを追加することができます。私の場合、テキストエディタはこのように表示されます:

show_name: true
show_icon: true
show_state: true
type: glance
entities:
  - entity: sensor.particle_matter_1
  - entity: sensor.particle_matter_2_5
  - entity: sensor.particle_matter_10
  - entity: sensor.particles_03
  - entity: sensor.particles_05
  - entity: sensor.particles_10
  - entity: sensor.particles_25
  - entity: sensor.particles_50
  - entity: sensor.particles_100
title: Daily Air Quality Averages
columns: 3
state_color: false

そして今、毎日の平均値を一目で見ることができます。オープンソースのHome Assistantがあなたのホームオートメーションにパワーを与えることの素晴らしさであり、GridDB Cloudがあなたのデータすべてをホスティングすることの素晴らしさでもあります。もちろん、遠隔地や遠く離れた大切な人のいる場所の空気の悪さを監視し、適切と思われる行動を取ることもできます。

まとめ

この記事では、物理的なハードウェアのセンサーデータを接続する方法と、その素晴らしいデータをGridDBクラウドにプッシュする方法を学びました。そして、Home Assistantを使ってそのデータに直接アクションを起こすことができることを学びました。

ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb

Leave a Reply

Your email address will not be published. Required fields are marked *