ECOマネから基本的な情報のHome Assistantでの取得

Home Assistant から Panasonic の古い (生産終了になっている)HEMS である ECOマネシステム(電気・ガス・水計測タイプ)の情報を取得することを試みた。Home Assistant にあるコンポーネントを利用することで、情報の取得はできるが、多数の情報を取得する際には、同じ記述を繰り返し行う必要がある。もう少しすっきりとした記述にするためには、Home Assistant の custom component を開発する必要がある。

情報取得を試みていた途中でECOマネシステムで「ポート遮断発生」が発生して通信ができなくなったので、それを解消する作業のために、時間が空いてしまった (Ecoマネへの不要パケットのフィルタリング)。

環境

ECOマネシステムは、電気使用量などをWebブラウザで見ることができるが、情報アクセスのためのAPIが整備されていない。経済産業省のHEMS補助対象機器になってECHONET Liteのインタフェースを(形式的には)持つようになったが、電気使用量などは取得できず、役にたたない。そこで、Home AssistantからWeb表示のページをスクレイピングして電気使用量などを取得する必要がある。

1回のWebの読み込みから複数のデータを取得できるカスタムコンポーネント multiscrape を利用することにし、HACSからダウンロードしてインストールした。

  • Home Assistant Core (Version 2023.7.1)
  • Multiscrape (Version 6.7.1)

Multiscrapeの日本語対応

Multiscape は、Webページが Unicode であることを前提にしている。しかし、ECOマネシステムのページは、charset=shift_jis となっているため、日本語などが文字化けしてしまう。このため、文字コードを指定するように、Home Assistant のconfig/custom_components/multiscrape/http.py の class HttpWrapper の async def async_request(self, context, method, resource, request_data=None): の中で次のように response.encoding = 'shift_jis' の行を加えた。Multiscrape v6.7.1では、http.py の 53行目に加えることになる。

        try:
            response = await self._client.request(
                method,
                resource,
                headers=self._request_headers,
                params=self._params_renderer(None),
                auth=self._auth,
                data=request_data,
                timeout=self._timeout,
                follow_redirects=True,
            )
            response.encoding = 'shift_jis' # SHIFT_JIS

全体の消費電力量等の取得

次のような省エネモニターページ (EcoTopMoni.cgi) から消費電力等を取得する。ページの左側に当日の消費電力などが示されており、右側に売電の電力量とCO2などへの換算値が示されている。

ecoTopMoni.cgi の HTML

EcoTopMoni.cgi の HTMLの主要部分は下記のようなものであった。抽出するデータに関係しない部分は削除してある。id が num_L? や num_R? の部分を取り出せばよいことがわかる。

num_L1 が買電電力量(kWh)、num_L2が太陽光発電電力量(kWh)、num_L4がガス消費量(m³)、num_L5が水消費量(m³)、num_R3 が売電電力量(kWh)である。

<html lang="ja" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=shift_jis">
<meta http-equiv="content-script-type" content="text/javascript">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="-1">
<title>省エネモニター</title>
</head>
<body onload="preloadImages0vp(10001);penginAnime('allgood');disp('pen');startclock();penginInitFunc()">
<div id="content">
	<div id="telop"></div>
	<div id="nbs_L1"><img src="ecoimages/zvp_sm_p_li_denki_sun.png" alt="" width="141" height="98" /></div>
	<div id="nbs_L2"><img src="ecoimages/zvp_sm_p_li_gm_g_m.png" alt="" width="141" height="58" /></div>
	<div id="nbs_R1"><img src="ecoimages/zvp_sm_p_li_de_all.png" alt="" width="110" height="120" /></div>
	<div id="num_L1">15.9</div>
	<div id="num_L2">14.1</div>
	<div id="num_L4">0.18</div>
	<div id="num_L5">0.89</div>
	<div id="num_R1">13.4</div>
	<div id="num_R2">6.0</div>
	<div id="num_R3">0.2</div>
</div>
</BODY>
</HTML>

ecoTopMoni.cgi に対する multiscape の YAML

ecoTopMoni.cgi に対して、次のような configuration.yaml ファイルを設定した。

この例でのECOマネシステムのIPアドレスは、192.168.1.220である。

multiscrape コンポーネント を利用して、select で要素を選択している。selectでは他に同じidが使われていないようなので num_L?だけでも選択できるが、以下の例ではパスを指定している。

scan_interval: 600 で 600秒(10分)おきの更新にしている。ここで抽出しているのは当日の累積量で、単位が0.1kWh なのであまり短い間隔で取得しても意味がないと思うが、必要に応じて間隔を調整する必要がある。

センサーとして買電電力量(unique_id: eco_day_self_power_consumption)、太陽光発電電力量(unique_id: eco_day_photovoltaic_power_generation)、ガス消費量(unique_id: eco_day_gas_consumption)、水消費量(unique_id: eco_day_water_consumption)を定義して、取得するようにしている。unit_of_measurement: "kWh" などで単位も示している。

# Loads default set of integrations. Do not remove.
default_config:

# Load frontend themes from the themes folder
frontend:
  themes: !include_dir_merge_named themes

automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

multiscrape:
  - resource: http://192.168.1.220/ecoTopMoni.cgi
    name: Eco Mane # ECO Mane Energy Saving Monitor
    scan_interval: 600
    sensor:
      - name: "ECO (Day) Self Power Consumption"
        select: "html > body > div > div#content > div#num_L1"
        unit_of_measurement: "kWh"
        unique_id: eco_day_self_power_consumption
      - name: "ECO (Day) Photovoltaic Power Generation"
        select: "html > body > div > div#content > div#num_L2"
        unit_of_measurement: "kWh"
        unique_id: eco_day_photovoltaic_power_generation
      - name: "ECO (Day) Gas Consumption"
        select: "html > body > div > div#content > div#num_L4"
        unit_of_measurement: "m³"
        unique_id: eco_day_gas_consumption        
      - name: "ECO (Day) Water Consumption"
        select: "html > body > div > div#content > div#num_L5"
        unit_of_measurement: "m³"
        unique_id: eco_day_water_consumption

上記の configuration.yaml によって、オーバービューでエンティティカードを追加して、情報を表示させることができる。

ecoTopMoni.cgi から抽出したセンサーのオーバービューでの表示

オーバービューで、センサーからの情報を表示するように複数のエンティティを指定できるカードをダッシュボードに追加した。

オーバービューの表示は以下のようになる。

回路の消費電力の取得

次のような回路ごとの消費電力のページ (elecCheck_6000.cgi) から消費電力を取得する。

elecCheck_6000.cgi の HTML

elecCheck_6000.cgi の HTMLの主要部分は次のようなものであった。抽出するデータに関係しない部分は削除してある。

idが ojt_?? のdivisionに、回路の情報が含まれている。class が、txt、txt2 のところには回路の説明、num には消費電力が書かれている。これらの情報を抽出すればよいことになる。

<html lang="ja" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=shift_jis">
<meta http-equiv="content-script-type" content="text/javascript">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="-1">
<title>電気使用量確認(1/4)</title>
</head>
<body onload="startclock();">
<div id="all" style="display:none;">
<div id="header">
	<div id="h_text">エネルギーモニター></div>
	<div id="h_pict"><img src="ecoimages/zvp_p_head_eco.png" alt="" width="39" height="29" /></div>
	<div id="h_title_pict">電気使用量確認(1/4)</div>
	<div id="clockLayer"> </div>
</div>
<form name="elec" method="post" action="elecCheck_6000.cgi">
<input type="hidden" name="page" value="1" /><input type="hidden" name="maxp" value="4" /><input type="hidden" name="selNo" value="0" /><input type="hidden" name="disp" value="0" /><input type="hidden" name="check" value="2" /><div id="content">
	<div id="tab_01"><a href="javascript:moveTab(1)"><img src="ecoimages/zvp_t_kini_dn.png" alt="" width="49" height="120" /></a></div>
	<div id="tab_02"><img src="ecoimages/zvp_t_zen_up.png" alt="" width="49" height="120" /></div>
	<div id="ojt_01" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_taiyoukou.png" alt="" width="50" height="50" /></div>
		<div class="txt"></div>
		<div class="txt2">太陽光</div>
		<div class="num">0W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('38')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
	<div id="ojt_02" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_syokon.png" alt="" width="50" height="50" /></div>
		<div class="txt">キッチン</div>
		<div class="txt2">照明&コンセント</div>
		<div class="num">123W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('0')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
	<div id="ojt_03" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_konsento.png" alt="" width="50" height="50" /></div>
		<div class="txt">ダイニング(北)</div>
		<div class="txt2">コンセント</div>
		<div class="num">64W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('1')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
	<div id="ojt_04" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_syokon.png" alt="" width="50" height="50" /></div>
		<div class="txt">ダイニング</div>
		<div class="txt2">照明&コンセント</div>
		<div class="num">0W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('2')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
	<div id="ojt_05" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_syokon.png" alt="" width="50" height="50" /></div>
		<div class="txt">ダイニング(南)</div>
		<div class="txt2">照明&コンセント</div>
		<div class="num">0W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('3')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
	<div id="ojt_06" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_syokon.png" alt="" width="50" height="50" /></div>
		<div class="txt">リビング(南)</div>
		<div class="txt2">照明&コンセント</div>
		<div class="num">43W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('4')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
	<div id="ojt_07" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_syokon.png" alt="" width="50" height="50" /></div>
		<div class="txt">リビング(北)</div>
		<div class="txt2">照明&コンセント</div>
		<div class="num">0W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('5')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
	<div id="ojt_08" class="ojt">
		<div class="pic"><img src="ecoimages/zvp_p_ki_syokon.png" alt="" width="50" height="50" /></div>
		<div class="txt">洋室1父母部屋</div>
		<div class="txt2">照明&コンセント</div>
		<div class="num">405W</div>
		<div class="btn btn_58"><a href="javascript:moveCircuitChange('6')"><img src="ecoimages/zvp_b_graph_up.png" alt="" width="58" height="41" /></a></div>
	</div>
</div>
</BODY>
</HTML>

elecCheck_6000.cgi に対する multiscape の YAML

elecCheck_6000.cgi に対して、multiscrape コンポーネント を利用して、消費電力のみをエンティティの値として抽出する場合と、txt, txt2, num をエンティティの属性として取得できるようにする場合を試してみた。

まず、resource: http://192.168.1.220/elecCheck_6000.cgi?page=1&disp=2 で電気使用量確認の1ページを取得するようにしている。すべてのページの情報を取得するためには、異なるページに対しても同じような記述をする必要がある。

scan_interval: 600 で 600秒(10分)おきの更新にしている。ある時点の消費電力なので、もっと短い間隔で取得すべきと思うが、テストなので長めにした。

センサーとしては、Ecoマネシステムの画面で太陽光発電という名前のついている回路(ojt1)とキッチンという名前の回路(ojt2)を定義している。すべての回路の情報を得るためには、他も定義する必要がある。

ojt1は、属性として回路の情報や消費電力の値を取得するようにした。取得対象は select で選択している。ここでは、選択のためにパスを指定している。HTMLでは、消費電力が 123WのようにWが含まれているので、その部分で文字列を分割(split)し、数値部分のみを抽出している。

ojt2は、エンティティの値として消費電力の値を取得するようにした。この部分にもHTMLでは、消費電力にWが含まれているので、その部分で文字列を分割(split)し、数値部分のみを抽出している。測定単位として W を指定している(unit_of_measurement: "W")。

# Loads default set of integrations. Do not remove.
default_config:

# Load frontend themes from the themes folder
frontend:
  themes: !include_dir_merge_named themes

automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

multiscrape:
  - resource: http://192.168.1.220/elecCheck_6000.cgi?page=1&disp=2
    name: "Eco Mane Electricity Usage Page 1"
    log_response: True
    scan_interval: 600
    sensor:
      - name: "ojt1"
        select: "html > body > div > form > div#content > div#ojt_01 > div.num"
        attributes:
          - name: "txt"
            select: "html > body > div > form > div#content > div#ojt_01 > div.txt"
          - name: "txt2"
            select: "html > body > div > form > div#content > div#ojt_01 > div.txt2"
          - name: "num"
            select: "html > body > div > form > div#content > div#ojt_01 > div.num"
            value_template: '{{ value.split("W")[0] }}'
      - name: "ojt2"
        select: "html > body > div > form > div#content > div#ojt_02 > div.num"
        unit_of_measurement: "W"
        value_template: '{{ value.split("W")[0] }}'

elecCheck_6000.cgi から抽出したセンサーのオーバービューでの表示

オーバービューで、センサー ojt1、ojt2 からの情報を表示するように複数のエンティティを指定できるカードをダッシュボードに追加した。

また、センサー ojt1 からの情報を表示するようにエンティティカードを利用し、エンティティカードあたり1つの属性を表示するよう追加した。txt、txt2、num、friendly name の情報を表示するために3つのカード ojt1.txt、ojt1.txt2、ojt1.num、ojt1.friendly name を利用している。ここの例では、ojt1.txt の値は空である。

カードを追加した際の編集画面は次のようになるはず。

オーバービューでの表示は次のようになった。

課題

現状では、個別の回路の情報を取得するために、現状では回路ごとに別のセンサーにしようとするとセンサーの数だけojt1 と同様の記述を繰り返す必要があるようだ。スクレイピングして得たテキストをセンサー名として登録するようなことはできないようだ。しかし、かなりの回路があるので、それを手作業で設定するのは面倒である。独自のカスタムコンポーネントを作って、スクレイピングして得た情報を基にセンサーを設定し、その値が更新されるようにしたい。