Home Assistant のECOマネシステム用 Custom Component

自宅に導入されている Panasonic のHEMS (Home Energy Management System)の一種である ECOマネシステム(電気・ガス・水 計測タイプ)(生産中止品)の情報を Home Assistant に取り込むための Custom Component を作成した。

以前、「ECOマネから基本的な情報のHome Assistantでの取得」において、Home Assistant の configuration.yaml への記述で ECOマネシステムの情報を取り込むことを試みたが、多くの情報を取り込むためには configuration.yamlの記述が多くなって、取り扱いが難しいので、Custom Component を作成することにした。

ECOマネシステムからの情報取得

ECOマネシステムからは API が提供されていない。Echonet Lite の機能が(経済産業省のHEMS補助対象機器になった関係で?) アップグレードのような形で付加されたが、ちょっと調べた範囲では、電気量すら取得できないという残念なレベルの極めて限定的なものであった。このため、Echonet Lite では必要な情報が取得できない。

ただ、ECOマネシステムは、Webブラウザで電気量等を表示する機能は持っているので、Webページを解析することで、情報を取得することはできる。

Webページをスクレイピングして情報を取得するカスタムコンポーネントを作成した。

custom_components/ecomane

カスタムコンポーネントとして ecomane というセンサーコンポーネントを作成した。

https://github.com/kunsen-an/ha-eco-mane にアップロードしてある。

コンポーネント全体に関連したファイル

manifest.json

ecomane コンポーネントの manifest.json は以下の通り。

( https://developers.home-assistant.io/docs/creating_integration_manifest )

domain は、Integration を識別する名前である。同じ Integration に属する entity は複数ありうる。

const.py

ecomane コンポーネントで利用している定数を定義している。

Integration の Config

Ecomane コンポーネントを Integration として Home Assistant に登録する際の Configuration 関連のファイルは以下の通り。

__init__.py

ecomane モジュールの初期化 __init__.py では、config entry を(非同期で)設定する async_setup_entry()、(非同期で) config entry を unload する async_unload_entry() を定義している ( https://developers.home-assistant.io/docs/config_entries_index/ )。

Home Assistant のコンポーネントの startup の際に async_setup_entry() が呼び出される。

async_setup_entry では、データ更新のためのコーディネーター EcoManeDataCoordinator を作成し、ECOマネシステムからどのようなエントリが存在するかという初期データを取得する。Home Assistant のデータ構造 (hass.data)にコーディネータを保存する。

config entry を async_forward_entry_setups() でプラットフォーム (ここのPLATFORMSは const.py で定義されており、センサーを意味する Platform.SENSOR のみを含むリスト)に設定する。

config_flow.py

コンポーネントをユーザインタフェースを利用して登録できるようにするために config entry を生成する config_flow.py を定義している( https://developers.home-assistant.io/docs/config_entries_config_flow_handler/ )。

config_flow.py では、登録済みのエントリを取得する configured_instances() と EcoManeConfigFlow を定義している。

EcoManeConfigFlow では、async_step_user() によって、ユーザの入力 user_input を処理する。

user_input に辞書形式のデータが与えられている場合は、そのデータに基づいてasync_create_entry() によってエントリを作成する( https://developers.home-assistant.io/docs/data_entry_flow_index/ )。

引数 user_input が None の場合には、 async_show_form() によって、ユーザ入力を受け付けて、その入力に基づいてエントリを作成する。

ユーザ入力の際に利用されるフォームの形式は、 data_schema で定義している。必要な項目 (vol.Required)として、名前 (CONFIG_SELECTOR_NAME = "name") と IPアドレス (CONFIG_SELECTOR_IP="ip")が指定されている( https://developers.home-assistant.io/docs/data_entry_flow_index/ )

ECOマネシステムからのデータ取得

ECOマネシステムから取得するデータは3種類に分類できる。

  • 回路別の電力(W)
      • プレフィックス EcoManeCircuitPower
  • 回路別の電力量(kWh)
      • プレフィックス EcoManeCircuitEnergy
  • 全体使用量(電力量、水、ガス)
      • プレフィックス EcoManeUsage
    • 購入電気量
    • 太陽光発電量
    • ガス消費量
    • 水消費量
    • CO2排出量
    • CO2削減量
    • 売電量

Webページからのscrapingには、beautiful soup 4 を用いた。

coordinator.py

ECOマネシステムからのデータ取得を制御するコーディネータ  EcoManeDataCoordinator を定義している。

EcoManeDataCoordinator では、_async_update_data()によってデータ更新を行う。_async_update_data() は、update_usage_data() と update_circuit_power_data() を呼び出している。

update_usage_data()は、全体使用量のデータを更新する。全体使用量は、回路数などの影響を受けないので個々のECOマネシステム環境に固有のURLである f"http://{self._ip_address}/{SENSOR_TODAY_CGI}" のWebページを取得し、そのページの内容を ecomane_usage_sensors_descs の情報に基づいて解析して、全体使用量を取得する。ここで、SENSOR_TODAY_CGI = "ecoTopMoni.cgi" である。

ここのURLに限らないが、ECOマネシステムのWebページのエンコーディングはshift-jis なので、ページデータを取得する際にはそれを指定する必要がある。

update_circuit_power_data()は、回路別の電力のデータを更新する。また、回路別の電力量を取得するためには、どのような電力センサーがあるかが必要なため、このupdate_circuit_power_data()のなかから、update_circuit_energy_data() を呼び出して、回路別の電力量を取得している。

update_circuit_power_data(self)では、URL が f"http://{self._ip_address}/{SENSOR_CIRCUIT_CGI}" のWebページを取得しparse_circuit_power_data () で解析を行う。
ここで、SENSOR_CIRCUIT_CGI = "elecCheck_6000.cgi?disp=2"  である。また、回路別の電力のページは複数あるので、それらのページを f"http://{self._ip_address}/{SENSOR_CIRCUIT_CGI}&page={page_num}" から順次取得して処理を行う。
parse_circuit_power_data(self, text: str, page_num: int) は、各ページのtext を解析して、ページ内の各回路の電力を取得する。
電力量は、各回路の電力を取得したのちに、update_circuit_energy_data(self, page_num: int, total_page: int, selNo: str, prefix: str)を呼び出して取得する。 電力量のページをアクセスするための selNo は回路ごとに決まっていると思うが、回路別の電力を表示される際の順番とは必ずしも一致しないようである。電力と電力量の対応がずれたりすることを防ぐために、電力を取得する際にわかる selNo を基に電力量を取得するようにしている。

update_circuit_energy_data()では、URLがf"http://{self._ip_address}/{SENSOR_CIRCUIT_ENERGY_CGI}?page={page_num}&maxp={total_page}&disp=0&selNo={selNo}&check=2" のページを取得し、そのページから対象の回路の今日の電力量を取得している。ここで、SENSOR_CIRCUIT_ENERGY_CGI = "resultGraphDiv_4242.cgi" である。

ECOマネシステムの内部的にはもう少し細かい精度で電力量が管理されていると思うが、Webページでは、小数点以下2桁までしか含まれていない。

次のスクリーンショットのオレンジ色の楕円で示された部分の情報を抽出して回路の今日の電力量を取得している。

sensor.py

config entry に基づいて、sensor entity を設定するための async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) が定義されている。この中で、async_add_entities(sensors, update_before_add=False) の update_before_add=False は、オーバービューに自動でエントリが登録され、表示されてしまわないようにするためである。この執筆時点ではupdate_before_addをTrue にすると、オーバービューに自動で登録される。自動登録は、update_before_addの元来の意味とは異なるので将来的には何か変更があるかもしれない。

ECOマネシステムのデータをSensor Entityとして提供する EcoManeUsageSensorEntity、 EcoManeCircuitPowerSensorEntity、 EcoManeCircuitEnergySensorEntity が定義されている。

EcoManeUsageSensorEntityは、(全体の)使用量を示すセンサー、 EcoManeCircuitPowerSensorEntityは回路別の電力を示すセンサー、 EcoManeCircuitEnergySensorEntityは回路別の電力量を示すセンサーである。

センサーのエンティティには translation_key を設定し、日本語への変換などが適切に行えるようにした。

名前の翻訳関係

ECOマネシステムに登録してある回路名とecomaneコンポーネントで利用する(Home Assistant 内部で利用する)名前、Home Assistant で表示される名前の変換のためにname_to_id.py、strings.json、translations/ja.jsonがある。

name_to_id.py

ECOマネシステムには回路名などを日本語で登録してあるが、それをそのまま Home Assistant内部で利用すると、漢字部分が中国語読みのようなローマ字に変換されてしまい、何を意味しているのか分かりづらい。そこで、ECOマネシステムのWebページ内に含まれている回路名をHome Assistant 内部で利用する英単語を連結したIDに変換するために、name_to_id.py を定義した。name_to_id.py は、実際に利用するECOマネシステムに応じて修正をする必要がある。

strings.json

内部のIDを表示名にするための標準を strings.json に定義した。name_to_id.pyによる変換結果の内部のIDの影響を受けるので、実際に利用するECOマネシステムに応じて修正をする必要がある。

translations/ja.json

内部のIDを日本語の表示名にするための例を translations/ja.json に示す。これも、実際に利用するECOマネシステムに応じて修正をする必要がある。また、他言語への変換が必要な場合には、その言語に応じて .jsonファイルを作成する必要がある。

Home Assistantでの利用

ecomane のインストール

まず、ecomane を Home Assistant にインストールする必要がある。homeassistantのディレクトリの config/custom_components/ の下にインストールすることでIntegrationとして登録可能になる。

手動でのインストール

GitHub の https://github.com/kunsen-an/ha-eco-mane からダウンロードして、ファイルを修正する必要がある。

https://github.com/kunsen-an/ha-eco-maneのトップが、homeassistantのディレクトリの config/custom_components/ha-eco-mane になるようダウンロードする。

以下のファイルの内容をECOマネシステムに登録してある回路名に合わせて修正する。

  • name_to_id.py
  • strings.json
  • translations/ja.json

また、const.py の DEFAULT_IP_ADDRESS も修正しておけば、Home Assistant への登録時にデフォルトとして利用できる。

HACSを利用したインストール

実際には、name_to_id.py、strings.json、translations/ja.json のECOマネシステムに合わせた修正してある リポジトリがあれば、Home Assistant Community Store (HACS) を利用してインストールすることが可能である。

HACSは Home Assistant にインストール済みとする。

まず、HACSを開いて、右上の3点メニューから Custom repositories を選択する。

Custom repositories のパネルに、リポジトリのURL (https://github.com/kunsen-an/ha-eco-mane)を入力し、Type に Integration を選択肢、ADD をクリックする。

HACSで検索できるようになったので、 ecomane で検索をする。

検索結果の ecomane の3点メニューから download を選択する。


HACS に正式に登録されていないので警告が表示されるので、確認をして、Downloadをクリックする(以下はテスト用環境でのスクリーンショットのため、ディレクトリが本番環境とは異なる)。Home Assistant の config/custom_components/ecomane にダウンロードされる。

ダウンロードが完了したら、Home Assistant の再起動が必要である。

Integration としての登録

インストールした ecomane を Integration として登録することで、ECOマネシステムから情報を取得して Home Assistant で表示したりできるようになる。

統合へのECOマネの追加

「+統合を追加」を選択し、「ecomane」を検索する。

Panasonic Eco Mane HEMS が見つかったら、それを選択する。ユーザ入力フォーム「ECOマネの設定」が表示されるので、設置環境に合わせて、名称とECOマネのIPアドレスを設定し「送信」する。

うまく設定されれば、統合に「ECOマネHEMS」が追加される。

ダッシュボードでのデバイスの表示

sensor.py の async_add_entities(sensors, update_before_add=False) の update_before_add=Falseによって、オーバービューに自動登録されなくなっているので、ダッシュボードで表示するためには表示のためのカード追加やエネルギーの設定等の設定が必要である。

オーバービューでの表示

ecomaneのIntegrationに含まれる3種類のデバイス(今日の使用量、消費電力、今日の消費電力量)をオーバービューに追加して(一部を)表示すると次のようになる。消費電力、今日の消費電力量には分電盤の回路数分のエンティティが含まれる。

エネルギーでの表示

デバイス「今日の使用量」のエンティティをエネルギーのページに設定することで、次のような表示を得ることができる。ECOマネシステムの小さなディスプレイよりも広い領域を使うことでわかりやすく表示される。

ECOマネシステムのネットワーク

別記事に書いたが、ECOマネシステムのネットワークは癖が強く、ECOマネシステムが受信するパケットが(ブロードキャストやマルチキャストであっても)少し多くなるとポート遮断を行い、しばらく通信ができなくなる。この問題に対処するためにセグメントを分けてトラフィックを少なくするようにしていたが、トラフィックがまだ多い場合がありポート遮断が頻発するようになっていた。また、IPアドレスがXX.YY.ZZ.220に固定されており、同一セグメントからでないと HTTP request に対する responseが受け取れない謎仕様である。この謎仕様のために、ECOマネシステムだけのセグメントを作って、トラフィックを減らすということをするためには、別セグメントからアクセスできるように reverse proxy を設置する必要がある。同一セグメントに他の機器をつなぎつつ、ECOマネシステムのポート遮断が起きないようにするために、パケットフィルタリングをするtransparent firewall (bridge) を OpenWrt で作成し、利用することにした。パケットフィルリングについては、「OpenWrt の nftables を使用した Transparent Firewall」 に記録している。