LaMetricでリアルタイム電力監視!家庭の消費電力・温湿度を常時表示する方法

InfluxDB

スマートホームを運用していると、「今どれだけ電力を使っているのか」がパッと目に入る場所に表示されていると、自然と節電意識が高まります。
本記事では、LaMetric TIMEというスマートディスプレイに、スマートメーターから取得した家庭の現在の消費電力(W)・室温(℃)・湿度(%)をリアルタイムで常時表示する方法を解説します。
Nature Remo EでスマートメーターのデータをInfluxDBに蓄積し、PythonからLaMetricのローカルAPIに送信する仕組みです。

なぜこの機能を作ったのか?

我が家ではNature Remo Eでスマートメーターのデータを取得してInfluxDBに記録しており、Grafanaのダッシュボードで電力使用状況を可視化しています。
ただ、Grafanaのダッシュボードは「見に行かないと見られない」という欠点がありました。電力を意識して節約するには、常時目に入る場所にリアルタイムで表示されていることが大切です。
そこで、リビングに置いてあるLaMetric TIMEに消費電力・温湿度を表示することで、家族全員が意識せずに電力使用状況を把握できるようにしました。

見るだけで節電意識が高まる!LaMetricを電力モニターにしてしまおう!

実現したいこと

  • LaMetric TIMEに家庭の現在の消費電力(W)を表示する
  • 消費電力に加えて室温・湿度もあわせて表示する
  • 1分ごとに自動更新してリアルタイムに近い状態で表示し続ける
  • 他のPythonプログラムからライブラリとして呼び出せる汎用的な実装にする

この記事でわかること

  • LaMetric TIMEのローカルAPIを使ってPythonからテキストと絵文字を表示する方法
  • 複数のフレーム(スライド)を使ってLaMetricに複数の情報を表示する方法
  • InfluxDBに蓄積した電力・温湿度データをPythonで取得してLaMetricに送信する方法
  • cronで1分ごとに自動実行する方法

必要な準備と用意するもの

ハードウェア
  • Raspberry Pi(例:Raspberry Pi 5)
  • LaMetric TIME(スマートディスプレイ)
  • Nature Remo E(lite)
    • スマートメーターとBルートで接続し、ECHONET Liteで電力データを取得するデバイス
  • SwitchBot 温湿度計(室温・湿度の取得に使用)
ソフトウェア/サービス
  • Python
    • requests
  • InfluxDB(電力・温湿度データの蓄積に使用)
  • Nature Remo クラウドAPI(スマートメーターデータの取得に使用)
  • LaMetric TIME ローカルAPI(LaMetricへの表示送信に使用)

InfluxDBへの電力データの蓄積(home_energy_status_record.py)やNature Remo EのAPIを使ったデータ取得については別記事を参照してください。本記事ではInfluxDBにデータが既に蓄積されている前提で解説します。

完成イメージ

完成すると、LaMetric TIMEが下記の3つのフレームをスライドショー形式でローテーション表示します。

  1. 現在の消費電力:⚡ 450W(電力アイコン+瞬時電力)
  2. 室温:🌡 24.5°C(温度計アイコン+温度)
  3. 湿度:💧 58 %(湿度アイコン+湿度)

crontabで1分ごとに自動実行されるため、常に最新の状態が表示されます。

システムの仕組み

全体の流れは下記の通りです。

  1. スマートメーター:家全体の電力使用状況を計測
  2. Nature Remo E(lite):ECHONET Liteプロトコルでスマートメーターからデータを取得し、クラウドAPIで外部に公開
  3. Raspberry Pi(home_energy_status_record.py):Nature Remo クラウドAPIから15秒ごとに電力データを取得し、InfluxDBに保存
  4. Raspberry Pi(home_status_display.py):1分ごとにInfluxDBから最新の電力・温湿度データを取得し、LaMetricのローカルAPIに送信
  5. LaMetric TIME:受信したデータをスライドショー形式で常時表示

実装のポイント

LaMetricのローカルAPIについて

LaMetric TIMEにはクラウドAPIとローカルAPIの2種類があります。今回はレスポンスが速くインターネット接続に依存しないローカルAPIを使用します。

レスポンスが速い
ローカルネットワーク内での通信なのでクラウド経由と比べてほぼ遅延なしで表示できる

インターネット不要
ルーターの不具合やインターネット回線の障害があっても動作する

同一ネットワーク内からのみアクセス可能
外出先からLaMetricを操作したい場合はクラウドAPIを使う必要がある

複数フレームの表示

LaMetricのAPIは1回のリクエストで複数のフレーム(スライド)を一括送信できます。model.frames配列に複数のオブジェクトを入れるだけで、LaMetric側が自動的にローテーション表示してくれます。

アイコンの指定

LaMetricのアイコンはLaMetric Developer Siteで公開されているアイコンIDを数値で指定します。今回使用したアイコンIDは下記の通りです。

  • 21256:電力(落雷マーク)
  • 14592:温度計
  • 22157:湿度(水滴)

アイコンIDはLaMetric Developer Site(developer.lametric.com)のIcon Libraryで検索できます。数万種類のアイコンが公開されているので、好みのものを選んでください。

事前準備

LaMetric TIMEのAPIキーを取得する

LaMetricのローカルAPIを使うにはAPIキーが必要です。下記の手順で取得できます。

  1. LaMetric Developer Site(developer.lametric.com)にアクセスしてアカウント登録・ログインする
  2. 「My Devices」からLaMetric TIMEを選択する
  3. 「API Key」をコピーしてメモしておく

APIキーは外部に漏れないよう大切に管理してください。settings.jsonに保存して、プログラム内にハードコードしないようにしましょう。

LaMetric TIMEのIPアドレスを確認する

LaMetricのIPアドレスはルーターの管理画面で確認するか、LaMetricアプリの「Settings → WiFi」から確認できます。
ルーターでLaMetricに固定IPアドレスを割り当てておくと、再起動のたびにIPが変わる心配がなくなるのでおすすめです。

settings.jsonの準備

APIキーとIPアドレスをsettings.jsonに記載します。

{
    "raspberrypi5_ip": "192.168.xxx.xxx",
    "lametric": {
        "api": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "ip": "192.168.xxx.xxx"
    },
    "remo": {
        "api": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "api_url_appliances": "https://api.nature.global/1/appliances",
        "api_url_devices": "https://api.nature.global/1/devices",
        "remo_e_no": 2
    },
    "switchbot": {
        "device_id": {
            "living_thermometer": "xxxxxxxxxxxx"
        }
    }
}

実装方法

実装は2つのPythonファイルで構成します。

  1. lametric.py:LaMetricに送信する汎用ライブラリ(他のプログラムから再利用できる)
  2. home_status_display.py:InfluxDBから電力・温湿度データを取得してLaMetricに表示する本体

LaMetric送信ライブラリ (lametric.py)

# -*- coding: utf-8 -*-
from pathlib import Path
import sys
PROJECT_ROOT = Path(__file__).resolve()
while PROJECT_ROOT.name != "smarthome":
    if PROJECT_ROOT.parent == PROJECT_ROOT:
        raise RuntimeError("smarthome project root not found")
    PROJECT_ROOT = PROJECT_ROOT.parent
sys.path.insert(0, str(PROJECT_ROOT))
from libs.bootstrap import Bootstrap
bootstrap = Bootstrap(__file__)
settings = bootstrap.settings
# ------------------------------------------------------------
import requests
import json
import smarthomelog
from influx.v2 import client as influxclient
from influx.v2 import writer as influxwriter

influx_client = influxclient
influx = influxwriter.Writer()
log = smarthomelog.Log(__file__, smarthomelog.LogType.LAMETRIC)

# LaMetricの接続情報
LAMETRICAPI = settings["lametric"]["api"]
LAMETRICIP  = settings["lametric"]["ip"]

HEADERS = {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
}
URL = "http://" + LAMETRICIP + ":8080/api/v2/device/notifications"

class Priority:
    INFO     = 'info'
    WARNING  = 'warning'
    CRITICAL = 'critical'

def display(priority: Priority, icon: str, text: str):
    """1フレームをLaMetricに表示する"""
    body = {
        "priority": priority,
        "model": {
            "frames": [
                {
                    "icon": icon,
                    "text": text
                }
            ],
        }
    }
    res = requests.post(URL, data=json.dumps(body), headers=HEADERS, auth=('dev', LAMETRICAPI))
    log.write(body)
    influx.insert_execution_api_log(__file__, influxwriter.LogApiType.LAMETRIC, LAMETRICIP, URL, text, len(text), str(body))

def display_body(body: dict):
    """複数フレームをLaMetricに表示する"""
    res = requests.post(URL, data=json.dumps(body), headers=HEADERS, auth=('dev', LAMETRICAPI))
    log.write(body)
    total_text = ''.join(frame['text'] for frame in body['model']['frames'])
    total_text_length = sum(len(frame["text"]) for frame in body["model"]["frames"])
    influx.insert_execution_api_log(__file__, influxwriter.LogApiType.LAMETRIC, LAMETRICIP, URL, total_text, total_text_length, str(body))

if __name__ == '__main__':
    args = sys.argv
    display(args[1], args[2], args[3])

電力状況表示プログラム (home_status_display.py)

# -*- coding: utf-8 -*-
__group__       = '省エネ'
__description__ = 'LaMetricに電力使用状況を表示'
from pathlib import Path
import sys
PROJECT_ROOT = Path(__file__).resolve()
while PROJECT_ROOT.name != "smarthome":
    if PROJECT_ROOT.parent == PROJECT_ROOT:
        raise RuntimeError("smarthome project root not found")
    PROJECT_ROOT = PROJECT_ROOT.parent
sys.path.insert(0, str(PROJECT_ROOT))
from libs.bootstrap import Bootstrap
bootstrap = Bootstrap(__file__)
settings = bootstrap.settings
# ------------------------------------------------------------
import datetime
from influx.v2 import client as influxclient
from influx.v2 import writer as influxwriter
import lametric
import smarthomelog
import common
from zoneinfo import ZoneInfo

def main():
    influx_client = influxclient

    # 瞬時電力(W)を取得
    res = influx_client.query_dict_by_influxql('remo_e', {'measured_instantaneous'}, query_mode=influxclient.QueryMode.LAST)
    measured_instantaneous = res[0].get('measured_instantaneous') if res else 0

    # 室温を取得
    res = influx_client.query_dict_by_influxql('meter', {'temperature'}, {'deviceId': settings['switchbot']['device_id']['living_thermometer']}, query_mode=influxclient.QueryMode.LAST)
    temperature = res[0].get('temperature') if res else None

    # 湿度を取得
    res = influx_client.query_dict_by_influxql('meter', {'humidity'}, {'deviceId': settings['switchbot']['device_id']['living_thermometer']}, query_mode=influxclient.QueryMode.LAST)
    humidity = res[0].get('humidity') if res else None

    # LaMetricに表示するフレームを組み立てる
    body = {
        "priority": "info",
        "model": {
            "frames": [
                {
                    "icon": "21256",        # 電力アイコン
                    "text": str(measured_instantaneous) + "W"
                },
                {
                    "icon": "14592",        # 温度計アイコン
                    "text": str(temperature) + "°C"
                },
                {
                    "icon": "22157",        # 湿度アイコン
                    "text": str(humidity) + " %"
                }
            ],
        }
    }

    lametric.display_body(body)

if __name__ == '__main__':
    try:
        if common.is_active_service_status('influxdb.service'):
            main()
            success_log = smarthomelog.Log(__file__, smarthomelog.LogType.SUCCESS)
            success_log.write('処理が正常に終了しました。', __file__)
    except Exception as e:
        log = smarthomelog.Log(__file__, smarthomelog.LogType.SYSTEM_ERROR)
        log.write_error(sys.exc_info())

crontabの設定

1分ごとに自動実行するようにcrontabに登録します。

crontab -e
# 1分ごとにLaMetricに電力状況を表示
* * * * * /home/<username>/Projects/venv/bin/python /home/<username>/Projects/smarthome/core/display/home_status/home_status_display.py >> /home/<username>/Projects/smarthome/logs/crontab/home_status_display.log 2>&1

コードの解説

lametric.py の解説

接続先URLとヘッダー

LaMetricのローカルAPIのエンドポイントは固定です。ポート番号は8080で、パスは/api/v2/device/notificationsです。

URL = "http://" + LAMETRICIP + ":8080/api/v2/device/notifications"

Basic認証

LaMetricのローカルAPIはBasic認証が必要です。ユーザー名は固定でdev、パスワードはDeveloper Siteで取得したAPIキーです。

res = requests.post(URL, data=json.dumps(body), headers=HEADERS, auth=('dev', LAMETRICAPI))

リクエストボディの構造

POSTするJSONの構造は下記の通りです。frames配列に複数のオブジェクトを入れることで、LaMetricが自動的にスライドショー表示します。

{
    "priority": "info",
    "model": {
        "frames": [
            {
                "icon": "21256",
                "text": "450W"
            },
            {
                "icon": "14592",
                "text": "24.5°C"
            },
            {
                "icon": "22157",
                "text": "58 %"
            }
        ]
    }
}
  • priority:通知の優先度。info / warning / critical の3種類
  • icon:LaMetric Developer SiteのIcon LibraryのアイコンID(文字列)
  • text:表示するテキスト

home_status_display.py の解説

InfluxDBからの瞬時電力取得

InfluxDBのremo_eテーブルから最新の瞬時電力(W)を取得します。QueryMode.LASTで最新の1件だけを取得します。

res = influx_client.query_dict_by_influxql('remo_e', {'measured_instantaneous'}, query_mode=influxclient.QueryMode.LAST)
measured_instantaneous = res[0].get('measured_instantaneous') if res else 0

InfluxDBからの温湿度取得

SwitchBot温湿度計のデータはInfluxDBのmeterテーブルに保存されています。デバイスIDでフィルタリングして最新の1件を取得します。

res = influx_client.query_dict_by_influxql(
    'meter',
    {'temperature'},
    {'deviceId': settings['switchbot']['device_id']['living_thermometer']},
    query_mode=influxclient.QueryMode.LAST
)
temperature = res[0].get('temperature') if res else None

InfluxDBが起動しているかを確認してから実行

InfluxDBが起動していない状態でプログラムが実行されるとエラーになります。common.is_active_service_status('influxdb.service')でInfluxDBのサービスが起動しているか確認してから処理を実行するようにしています。

if common.is_active_service_status('influxdb.service'):
    main()

common.is_active_service_status()は自作のユーティリティ関数です。内部でsystemctlコマンドを呼び出してサービスの起動状態を確認しています。

動作確認

まずはコマンドラインから手動で実行して動作を確認します。

python /home/<username>/Projects/smarthome/core/display/home_status/home_status_display.py

実行後、LaMetric TIMEの画面で下記を確認してください。

  • 電力アイコンと現在の消費電力(W)が表示されること
  • 温度計アイコンと室温(°C)が表示されること
  • 湿度アイコンと湿度(%)が表示されること
  • 3つのフレームがスライドショーでローテーション表示されること

LaMetricが表示されない場合は下記を確認してください。
・settings.jsonのIPアドレスとAPIキーが正しいか
・LaMetricとRaspberry Piが同じネットワークに接続されているか
・LaMetricの電源が入っていてWi-Fiに接続されているか

手動実行で問題なければ、crontabの設定を確認して1分ごとに自動実行されるようにします。

# crontabの設定確認
crontab -l | grep home_status_display

1分ほど待ってLaMetricの表示値が更新されることを確認できれば完成です。

まとめ

今回はLaMetric TIMEのローカルAPIを使って、家庭の現在の消費電力・室温・湿度をリアルタイムで常時表示する仕組みを作りました。
実装のポイントをまとめると下記の通りです。

  • LaMetricのローカルAPIはBasic認証+JSON POSTで手軽に利用できる
  • model.frames配列に複数オブジェクトを入れるだけで自動スライドショー表示になる
  • LaMetricライブラリ(lametric.py)を汎用化しておくことで、他のプログラムからも再利用できる
  • crontabで1分ごとに自動実行することで、常に最新の情報を表示し続けられる

LaMetricはAPIが公開されていてカスタマイズしやすいので、電力以外にも天気・鉄道遅延・宅配ステータスなど様々な情報を表示する用途に応用できます。
スマートホームのステータスボードとして積極的に活用してみてください。

タイトルとURLをコピーしました