hsBox1.3で、太陽光発電機器故障を検知して通知してみよう

2025年12月22日で太陽光発電 通知メールがなくなる件。 代替として自前で収集した情報をLINEに通知してみた」をさらに高度化を検討してみた。
 過去の実装(太陽光発電システムの故障判断アルゴリズム(その1))で、故障検知を高精度(6年間で5回検知・誤検知なし。1件はパワコンブレーカーダウン、4件はデータアップロードネットワークのダウン)で実装できていましたが、さきのソーラーフロンティアのサービス終了でこの検知システムが使えなくなりました。そこで、新たな検知システムの実装を、先に公開してシステムを強化して、検討してみます。

 ちなみに、公開済みの発電量収集代替実装では、従来のソーラーフロンティアの発電量との誤差は±2%程度なので、3か所ともに発電量収集代替実装を導入して、既存の検知システムにデータを流し込めば高精度での判定を継続できるようになります。しかし、通常、太陽光発電システムを複数保有しているほぼないので、この方式はほぼ使えません。そこで、ここでは1システムだけであっても判定することができる仕組みを検討してみます。

SolarAlert_
太陽光発電システム 故障通知

故障検知実現の設計方針

最終的には、人が判断するが、できるだけ判定精度を上げたい。過去の高精度判定システム構築での知見を反映して設計する。ポイントは次の通り。
・快晴時の発電量は安定しており、きれいなカーブを描く。
   →判定はこの部分のデータを使う。
・雲がある晴れの日のデータは変化が激しい。
・発電量の小さい日の発電量のばらつきが大きい。
   →判定には使用せず、参考値として通知する。
・四季の変化での発電量の補正が必要
→過去実装では、実績のピークデータをもとに関数化して組み込み
想定発電量と比較し、判定するかどうかを切り分けした。
・外部情報として最も近い気象庁観測点のデータを使用する。
・太陽光パネルの様々な設置場所ごとの違いによる想定発電量を単純な計算式ではなく学習方式を採用する。

使用するデータは、以前に公開したParquet形式で保存したものとする。

実装例 


import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import os
import sys
import argparse
import requests
from bs4 import BeautifulSoup

# ==================== 設定エリア ====================
NAS_PATH = r"/mnt/nas/PowerData" #★
MODEL_PATH = os.path.join(NAS_PATH, "learning_model.parquet") # 学習データ保存先
IFTTT_WEBHOOK_URL0 = "https://maker.ifttt.com/trigger/{event}/with/key/{your_key}"
IFTTT_EVENT_NAME = "message_to_myline" #★
your_key = "************" #★
IFTTT_WEBHOOK_URL = IFTTT_WEBHOOK_URL0.replace("{your_key}", your_key)

# 判定しきい値 (期待値の50%以下が2時間続いたら故障疑いなど)
THRESHOLD_RATIO = 0.5 #★
# ====================================================

def send_line_message(title: str, body: str, timestamp: str = "-"):
payload = {"value1": title, "value2": body, "value3": timestamp}
url = IFTTT_WEBHOOK_URL.replace("{event}", IFTTT_EVENT_NAME)
try:
response = requests.post(url, json=payload, timeout=10)
return response.status_code == 200
except Exception as e:
print(f"LINE通知失敗: {e}")
return False

def get_jma_sunshine_hourly(target_date: datetime):
"""気象庁HPから10分ごとの日照時間を取得し1時間単位(0.0-1.0)で返す"""
url = (f"https://www.data.jma.go.jp/stats/etrn/view/10min_s1.php?"
f"prec_no=**&block_no=****&year={target_date.year}&month={target_date.month}&day={target_date.day}&view=p2") #★
try:
res = requests.get(url, timeout=10)
res.encoding = res.apparent_encoding
soup = BeautifulSoup(res.text, 'html.parser')
rows = soup.find_all('tr', class_='mtx')
sunshine_10min = []
for row in rows:
cols = row.find_all('td')
if len(cols) > 10:
val = cols[9].text.strip() # 日照時間の列
sunshine_10min.append(float(val) if val else 0.0)
# 1時間(6個)ごとに合計(最大1.0)
return [sum(sunshine_10min[i:i+6]) for i in range(0, len(sunshine_10min), 6)]
except Exception as e:
print(f"気象庁データ取得失敗: {e}")
return [None] * 24

def update_learning_model(target_date: datetime, hourly_actual: list, hourly_sun: list):
"""日照が良い時のデータを『快晴時の正解』として学習モデルを更新"""
month = target_date.month
try:
if os.path.exists(MODEL_PATH):
model_df = pd.read_parquet(MODEL_PATH)
else:
# 初期モデル: 全月0で作成
model_df = pd.DataFrame(0.0, index=range(1, 13), columns=[f"h_{i}" for i in range(24)])

updated = False
for h in range(7, 18): # 発電時間帯のみ
# 日照率が0.9以上かつ、これまでの学習値より高い(または初データ)場合に更新
if hourly_sun[h] and hourly_sun[h] >= 0.9:
current_val = model_df.loc[month, f"h_{h}"]
if hourly_actual[h] > current_val:
model_df.loc[month, f"h_{h}"] = hourly_actual[h]
updated = True

if updated:
model_df.to_parquet(MODEL_PATH)
print("学習モデルを更新しました。")
except Exception as e:
print(f"学習モデル更新失敗: {e}")

def load_day_data(target_date: datetime) -> pd.DataFrame:
date_str = target_date.strftime("%Y%m%d")
file_path = fr"{NAS_PATH}/power_{date_str}.parquet"
if not os.path.exists(file_path):
raise FileNotFoundError(f"データが見つかりません: {file_path}")
return pd.read_parquet(file_path)

def main():
parser = argparse.ArgumentParser()
parser.add_argument("--da", type=int, default=0)
args = parser.parse_args()

base_date = datetime.now() + timedelta(days=args.da)
date_label = base_date.strftime("%Y/%m/%d")

try:
df = load_day_data(base_date)
# 1時間ごとの発電量(value1)リスト作成
df['hour'] = pd.to_datetime(df['timestamp']).dt.hour # timestamp列があると想定
hourly_actual = df.groupby('hour')['value1'].mean().reindex(range(24), fill_value=0.0).tolist()
except Exception as e:
print(f"データ読み込みエラー: {e}")
return

# 気象庁データ取得
hourly_sun = get_jma_sunshine_hourly(base_date)

# 学習モデルの更新
update_learning_model(base_date, hourly_actual, hourly_sun)

# 故障検知
is_anomaly = False
anomaly_details = []
if os.path.exists(MODEL_PATH):
model_df = pd.read_parquet(MODEL_PATH)
month = base_date.month

for h in range(8, 17): # 影の影響が少ない時間帯を重点チェック
sun = hourly_sun[h]
if sun and sun > 0.3: # ある程度の日照がある場合
expected = model_df.loc[month, f"h_{h}"] * sun
actual = hourly_actual[h]
if actual < expected * THRESHOLD_RATIO:
is_anomaly = True
anomaly_details.append(f"{h}時(実測{actual:.1f}kW/期待{expected:.1f}kW)")

# メッセージ作成
total_gen = sum(hourly_actual) * (10/60) # 10分データの場合の概算
status_msg = "✅ 正常" if not is_anomaly else "⚠️ 故障の疑い"

message = f"⚡️ {date_label} 発電実績 ({status_msg})\n" \
f"総発電量:{total_gen:.2f} kWh\n"

if is_anomaly:
message += "\n【異常検知】\n" + "\n".join(anomaly_details)
send_line_message("太陽光発電アラート", message, "ALERT")
else:
send_line_message("太陽光発電データ", message, "OK")

print(message)

if __name__ == "__main__":
main()

上の実装は、あくまでもサンプルです。 特に”★”印のある行は環境に合わせて修正してください。

このコードで検証していますが、かなり過敏で誤検知が多いです。
閾値調整で、精度を上げることになると思いますが、過去実装での故障判定ロジックを超える精度にはならなさそうです。 誤検知をゼロにするには、十分に学習させたうえで、1日中快晴だった日のデータでのみで判定する必要がありそうです。

他に改善案などあればコメントをお願いします。

関連記事

 

2025年12月22日で太陽光発電 通知メールがなくなる件。 代替として自前で収集した情報をLINEに通知してみた

いよいよ「太陽光発電、 通知メールがなくなる」。さてどうしようか、2025/12/07時点で、一番下に添付したメールが朝9:00ころに届いている。これを、代替できるLINE通知する仕組みを作ってみました。 12/14に取得した結果は次の通り、ほぼ同じ結果で十分な結果と言えるでしょう。

自作したLINEでの表示例 と【フロンティアモニター】のレポート
下記の通り、2025年12月13日の発電量をお知らせいたします。

発電量:7.52kWh
下記の通り、2025年12月13日の電力量をお知らせいたします。

発電量:7.52kWh
売電量:0.28kWh
買電量:53.99kWh
今月の目標売電量(223kWh)に対して、28%達成しました。
今月の目標消費電力量(1,106kWh)に対して、641kWh消費しました。
下記の通り、2025年12月07日-2025年12月13日の電力量をお知らせいたします。

発電量:96.83kWh
売電量:31.50kWh
買電量:281.26kWh
今月の目標売電量(223kWh)に対して、28%達成しました。
今月の目標消費電力量(1,106kWh)に対して、641kWh消費しました。


実装方針

・従来の通知メールに含まれる発電量等の計測値データはすべて含める
・通知メールの種類はいくつかあるが、元データは1つなので、引数で送信メッセージの内容を切り替える
・CRONで起動指定する

この方針で、ここまでで作成した実装をベースに集計+メッセージ送信するスクリプトを作成します。まずは、iPython上で動作確認して、hsBox上に移植しました。
以下のスクリプトを /home/hsbox/pyd/power_report.py に配置し、後述の設定を行いました。

スクリプト実装(例 ★印の箇所は要更新)

# power_report.py
import pandas as pd
from datetime import datetime, timedelta
import os
import sys
import argparse
import requests

# ==================== 設定エリア ====================
NAS_PATH = r"/mnt/nas<★NASマウント+パス>" #★
IFTTT_WEBHOOK_URL0 = "https://maker.ifttt.com/trigger/{event}/with/key/{your_key}"
IFTTT_EVENT_NAME = "******" # IFTTTで作ったイベント名★
your_key="********" # ←ここを自分のものに変更★
IFTTT_WEBHOOK_URL = IFTTT_WEBHOOK_URL0.replace("{your_key}", your_key)
# ====================================================


def send_line_message(title: str, body: str, timestamp: str):
payload = {
"value1": title,
"value2": body,
"value3": timestamp
}
url = IFTTT_WEBHOOK_URL.replace("{event}", IFTTT_EVENT_NAME)
try:
response = requests.post(url, headers={"Content-Type": "application/json"}, json=payload, timeout=10)
if response.status_code == 200:
print("LINE通知成功")
else:
print(f"エラー: {response.status_code}")
except Exception as e:
print(f"LINE通知失敗: {e}")

def load_day_data(target_date: datetime) -> pd.DataFrame:
date_str = target_date.strftime("%Y%m%d")
file_path = fr"{NAS_PATH}/power_{date_str}.parquet"
if not os.path.exists(file_path):
raise FileNotFoundError(f"データが見つかりません: {file_path}")
return pd.read_parquet(file_path)

def calc_daily_summary(df: pd.DataFrame, date_label: str):
# 数値化
df["value1"] = pd.to_numeric(df["value1"], errors="coerce") # 発電 kW
df["value2"] = pd.to_numeric(df["value2"], errors="coerce") # 買電 kW
df["value3"] = pd.to_numeric(df["value3"], errors="coerce") # 売電 kW
df["value4"] = pd.to_numeric(df["value4"], errors="coerce") / 6 # 消費 kWh(既に1時間積算値)

# 10分間隔と仮定して正確にkWh計算
interval_min = 10
hours_per_record = interval_min / 60.0

total_gen_kwh = df["value1"].mean() * len(df) * hours_per_record
total_buy_kwh = df["value2"].mean() * len(df) * hours_per_record
total_sell_kwh = df["value3"].mean() * len(df) * hours_per_record
total_use_kwh = total_buy_kwh + total_gen_kwh - total_sell_kwh # 電力収支で算出(最も正確)

return {
"date": date_label,
"gen": round(total_gen_kwh, 2),
"buy": round(total_buy_kwh, 2),
"sell": round(total_sell_kwh, 2),
"use": round(total_use_kwh, 2)
}

def calc_week_summary(days=7, da=-8 ):
"""過去days日分の集計(今日を除く昨日まで)"""
base_day = datetime.now().date()
results = []
for i in range(1+8+da, days + 1+8+da):
target_date = base_day - timedelta(days=i)
try:
df = load_day_data(target_date)
date_label = target_date.strftime("%m/%d")
summary = calc_daily_summary(df, date_label)
results.append(summary)
except Exception as e:
print(f"{target_date} のデータ読み込み失敗: {e}")

if not results:
return None

total_gen = sum(r["gen"] for r in results)
total_buy = sum(r["buy"] for r in results)
total_sell = sum(r["sell"] for r in results)
total_use = sum(r["use"] for r in results)

lines = [f"【過去{days}日間実績】"]
for r in reversed(results): # 古い順→新しい順
lines.append(f"{r['date']}: 発電{r['gen']} kWh")
lines.append("")
lines.append(f"合計発電量: {total_gen:.1f} kWh")
lines.append(f"合計買電量: {total_buy:.1f} kWh")
lines.append(f"合計売電量: {total_sell:.1f} kWh")
lines.append(f"合計消費量: {total_use:.1f} kWh")

return "\n".join(lines)

def main():
parser = argparse.ArgumentParser(description="電力データ集計&LINE通知")
parser.add_argument("--da", type=int, default=0, help="日付加算。-1=昨日, 0=今日, 1=明日…")
parser.add_argument("--ptn", type=str, default="f", choices=["p", "f", "w"],
help="p=発電量のみ, f=全項目, w=週間集計")
args = parser.parse_args()

# --- 対象日の決定 ---
base_date = datetime.now()
if args.da != 0:
base_date += timedelta(days=args.da)

# 今日の場合、現在時刻までのデータのみ読み込む(ファイルは当日分全部ある前提)
target_date = base_date.date()
date_label = base_date.strftime("%Y/%m/%d")

if args.ptn == "w":
message = calc_week_summary(7,args.da)
if message:
send_line_message("太陽光発電1週間データ",message,"-")
message=f"{message}\n"
else:
send_line_message("太陽光発電","週間集計データが取得できませんでした","-")
message="週間集計データが取得できませんでした"
print(message)
return
else:
try:
df = load_day_data(base_date)
except Exception as e:
send_line_message("太陽光発電",f"⚠️ {date_label} のデータがありません\n{e}","-")
message = f"⚠️ {date_label} のデータがありません\n{e}"
print(message)
return

summary = calc_daily_summary(df, date_label)

# --- メッセージ作成 ---
if args.ptn == "p":
message = f"☀️ {base_date.date()} 発電量\n{summary['gen']} kWh"
else: # f
message = f"⚡️ {base_date.date()} 電力実績\n" \
f"発電量 :{summary['gen']} kWh\n" \
f"買電量 :{summary['buy']} kWh\n" \
f"売電量 :{summary['sell']} kWh\n" \
f"消費電力量:{summary['use']} kWh"

send_line_message("太陽光発電データ",message,"-OK-")
print(message)

import sys
if "ipykernel_launcher.py" in sys.argv[0]:
# ここに実行したい引数を書く(好きな組み合わせでOK)
sys.argv = ["power_report.py"] # ← ptnなし → f 扱い(今日分フル)
#sys.argv = ["power_report.py", "--da", "-1"] # 昨日分フル
#sys.argv = ["power_report.py", "--ptn", "p"] # 今日の発電量だけ
# sys.argv = ["power_report.py", "--ptn", "w"] # 週間集計

if __name__ == "__main__":
main()
使用方法・CRON設定方法(CRON起動設定 例)
30 19 * * * /usr/bin/python3 /home/hsbox/pyd/power_report.py --da 0 --ptn p
19:30に当日の発電量を通知

0 9 * * * /usr/bin/python3 /home/hsbox/pyd/power_report.py --da -1 --ptn f
AM9:00に前日の発電量・売電量・買電量・消費電力を通知

1 9 * * 7 /usr/bin/python3 /home/hsbox/pyd/power_report.py --da -8 --ptn w
日曜日 AM9:00に前日までの7日間の発電量・売電量・買電量・消費電力を通知

IFTTTの設定

IFTTTでの設定は、随時更新されるため参考程度で見てください。詳細はWebhooksやLINEの項目を確認してください。 ※これは、2025/12/10時点の情報です。

Webhooksの「Your key」の確認方法

1. IFTTT にログイン

https://ifttt.com
にアクセスし、ログインします。

2. Webhooks サービスページへ移動

以下の公式ページを開く
👉 https://ifttt.com/maker_webhooks

3. 右上の「Settings」をクリック

画面右上に Settingsという青いボタンがあります。

4. “Webhooks Settings” の欄を確認

開いたページに以下のような記載があります:

URL
https://maker.ifttt.com/use/**********


この key(*******の部分) が個人専用の Webhooks Key です。
このkeyをスクリプト(power_report.py)のyour_keyに設定してください
IFTTTの Applet 作成例(※画面は2025年12月現在のものです)

Appletの名称は自分にわかりやすいように任意に設定してください。

イベント(THEN)に「Receive a web request」を選択し、Event Nameを設定します。このEvent Nameをスクリプト(power_report.py)内のIFTTT_EVENT_NAMEに設定します。

THAT(実行内容)に LINEの「Send message to self」を選択し、自分のLINE accountを設定します。 Messageは、上のように設定すれば、設定完了です。

この設定は、自分あてのメッセージ送信なので、このスクリプトに限らず、他のメッセージ送信にも応用できます。

以下、2025/12/07に届いた通知メール
件名:【フロンティアモニター】 12月06日 電力量レポート


内容:
**** 様

日頃より【フロンティアモニター】ホームエネルギーモニタリングサービスをご利用いただき、誠にありがとうございます。

【システム終了のお知らせ】
2025年12月22日(月)をもって本計測装置のサービスを終了いたします。
なお、システムの都合により、一部サービス終了のタイミングについては前後する可能性がございますので、ご承知おきください。
詳しくはお客様ご利用サイトのお知らせ欄をご覧ください。

本メール発信は、メールシステムメンテナンスにより、1日遅延する場合があります。メンテナンスの日程は、お客様ログイン画面の「お知らせ」欄に随時記載いたします。
メンテナンス時はご不便をおかけしますが、何卒ご承知おきくださいますようお願いいたします。

下記の通り、2025年12月06日の電力量をお知らせいたします。

発電量:19.98kWh
売電量:9.56kWh
買電量:37.58kWh

今月の目標売電量(223kWh)に対して、14%達成しました。
今月の目標消費電力量(1,106kWh)に対して、293kWh消費しました。

省エネ目標が01月01日から変更されておりません。

発電・消費電力量は季節ごとにかわりますので、目標も毎月更新されることをおすすめします。

日没後に送られてくる当日分発電量
**** 様

日頃より【フロンティアモニター】ホームエネルギーモニタリングサービスをご利用いただき、誠にありがとうございます。

【システム終了のお知らせ】
2025年12月22日(月)をもって本計測装置のサービスを終了いたします。
なお、システムの都合により、一部サービス終了のタイミングについては前後する可能性がございますので、ご承知おきください。
詳しくはお客様ご利用サイトのお知らせ欄をご覧ください。

本メール発信は、メールシステムメンテナンスにより、1日遅延する場合があります。メンテナンスの日程は、お客様ログイン画面の「お知らせ」欄に随時記載いたします。
メンテナンス時はご不便をおかけしますが、何卒ご承知おきくださいますようお願いいたします。

下記の通り、2025年12月06日の発電量をお知らせいたします。

発電量:19.98kWh















今後ともフロンティアモニターをよろしくお願いいたします。
★なお、お心当たりのない方は、お手数ではございますが、下記メールアドレスまでご連絡頂きますようお願いいたします。
★このメールは送信専用メールアドレスから配信しています。このまま返信いただいてもお答えできませんのでご了承ください。
-----------------------------------------------
ソーラーフロンティア株式会社
【フロンティアモニター】お客様サービスセンター
電話:0570-053115(受付時間:9:00-17:00)※日曜、祝祭日、メーデー、年末年始を除く
メール:information@solar-frontier.com
-----------------------------------------------

上の内容のうち、太字部分に相当する情報を、LINEで通知するようにしてみました。
参考にしてみてください。

※追記情報

LINEへの通知はIFTTTをPROアカウントで利用している場合でも件数制限があるようです。たぶん、月間50件くらいの制限だと思われます。通常はE-mailでの通知にして、頻度が少ない異常の検知をした場合にだけ使うようにするのがよさそうです。

関連記事

ソーラーフロンティア太陽光発電・発電量独自集積データ解析をiPythonで書いてみた

モニタリングを視覚化と、異常検知するアプローチを進めていきましょう。 まずは、自動化に向けて太陽光発電・発電量独自集積データ解析をiPythonで書いてみます。

先の記事までにデータ収集に成功し、ソーラーフロンティア太陽光発電のモニタリングサービスで収集した結果とデータ比較しほとんど一致していることを確認できています。ここからは、モニタリングを視覚化と、異常検知するアプローチを進めていきましょう。 まずは、自動化に向けて太陽光発電・発電量独自集積データ解析をiPythonで書いてみます。 → LINE通知はこちら

システム構成
fmget
ソーラフロンティアモニタ代替

ここからは、前にNASに保存した形式のデータを扱う前提で書いていきます。

元のデータは、10分毎の瞬間値データでした。1時間ごとに平均してkWhに変換すると、ソーラーフロンティアモニタリングサービスに蓄積されたデータとほとんど同じデータになります。※取得した瞬間値の取得タイミングが異なるので、雲がまばらにあるような瞬間値の変動が激しい天候の場合はずれが大きくなるでしょう。
 1日分の発電量を集計・グラフ化するipythonコードは次の通りです。

import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
from datetime import datetime

dt1 = "2025/12/02" #★参照したい日付 を指定
nas_path = r"<NASのパス名>" #★NASのパス 環境に合わせて指定


date_obj = datetime.strptime(dt1, "%Y/%m/%d")
date_with_slash = date_obj.strftime("%Y/%m/%d")
date_str = date_obj.strftime("%Y%m%d")

# --- ① parquet 読み込み(あなたのコード) ---
# ファイルパスを組み立て
file_path = fr"{nas_path}\power_{date_str}.parquet"
df = pd.read_parquet(file_path)
ttl=f"発電量推移 {date_with_slash}"

# --- ② Value1 を数値化(エラーを NaN に) ---
df["value1"] = pd.to_numeric(df["value1"], errors="coerce")

# --- ③ timestamp を datetime に変換 ---
df["timestamp"] = pd.to_datetime(df["timestamp"])

# --- ④ 時刻(Hour)を抽出 ---
df["hour"] = df["timestamp"].dt.hour

# --- ⑤ 1時間ごとの平均値を計算 ---
hourly_mean = df.groupby("hour")["value1"].mean()

# --- ⑥ 結果表示 ---
print(hourly_mean)
total_sum = df["value1"].sum()/6
print(total_sum)

# timestamp を x 軸として、そのままプロット
plt.figure(figsize=(14,4))
plt.plot(hourly_mean, linewidth=1)
plt.title(ttl)
plt.grid(True)
plt.tight_layout()
plt.show()

★印の箇所は、任意に変更してください。NASのパスは iPythonが動作しているマシン上から見た、データ保存のパスです。
\\192.168.1.50\share など

表示結果例

SolarPower

つぎは、月単位、年単位の集計値を出すコード書いてみましょう。それから、hsBoxで、スマートディスプレイやGoogleTVに表示させる仕組みに着手です。

関連記事

hsbox1.3で屋内のソーラーフロンティアホームサーバから直接発電量データ取得、データ検証編 まず2日分データで検証

hsbox でのソーラーフロンティアホームサーバーからのデータ収集の続きをしましょう。ホームサーバから直接取得する方法で、ホームエネルギーモニタリングサービス とほぼ一致するデータが取れたことを確認できました。


今回は、情報採取した2日分のデータと、「フロンティアモニター – ホームエネルギーモニタリングサービス -」のデータがどの程度一致しているか検証してみます。

取得データ比較結果

12/1と12/2の発電量の比較デーは以下の通りです。10分間隔で取得して1時間ごとに平均化しています。ほぼ同じ取得方法ですが、取得タイミングが微妙に違うので少しずれます。それでも、1日の発電量の一致度は、12/1は98.4%、 12/2は100.5%でした。十分満足できる結果でした。昨日公開した実装方法で発電量などのデータを取得できることを確認できました。

先のValue1が発電量のデータです。さらに2週間ほど並行してソーラーフロンティアに上がっているデータと一致しているか、詳細確認をしてみます。

検証環境

今回、データ取得に使用した環境は次の通りです。
フロンティアモニターホームサーバー
カーネルVer. 3.22
システムVer. 3.22
AD変換ボードVer. 2.00

hsBox
Version: 1.03.01.01, Build: 324

fmget
ソーラフロンティアモニタ代替

関連記事

https://www.frontier-monitor.com/persite/top

hsbox1.3で、ソーラーフロンティアホームサーバから発電量データ取得 遂に成功!? (fm_dataget.py)

hsbox でのソーラーフロンティアホームサーバーからのデータ収集の続きをしましょう。PROXY方式は諦めて、ホームサーバから直接取得する方法で再検討です。
結論から言うと、どうもうまくいっていそうです。最初に構成図です、前に描いた図と同じですが、ホームサーバから受け取るのではなく、ホームサーバに取りに行くイメージです。

検証環境

今回、データ取得に使用した環境は次の通りです。
フロンティアモニターホームサーバー
カーネルVer. 3.22
システムVer. 3.22
AD変換ボードVer. 2.00

hsBox
Version: 1.03.01.01, Build: 324

データ取得実装例(/home/hsbox/pyd/fm_dataget.py)

フロンティアモニターホームサーバーのバージョンによってデータ取得方法に違いがあります。参考にしてみてください

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests
import pandas as pd
import os
import json
import logging
from datetime import datetime
from pathlib import Path
import platform

# ===== 設定 =====
URL = "http://<★フロンティアモニターホームサーバーIP>/getEpData.cgi"
if platform.system() == "Windows":
NAS_DIR = Path(r"\\<★NAS IP>\share\PowerData")
else:
NAS_DIR = Path("/mnt/nas/PowerData") ★

NAS_DIR.mkdir(parents=True, exist_ok=True)

# ===== ログ設定 =====
today = datetime.now().strftime("%Y%m%d")
logfile = NAS_DIR / f"powerD_{today}.log"

logging.basicConfig(
filename=str(logfile),
level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
encoding='utf-8'
)

# ===== データ取得 =====
try:
response = requests.post(URL, data={"ep_units": "KW"}, timeout=5)
response.raise_for_status()
raw_data = response.text.strip()
except Exception as e:
logging.error(f"データ取得エラー: {e}")
print(f"データ取得エラー: {e}")
exit(1)

# ===== データ整形 =====
values = raw_data.split('|')

now = datetime.now()
data_dict = {
"timestamp": now,
"value1": values[0],
"value2": values[1],
"value3": values[2],
"value4": values[3],
"value5": values[4],
"value6": values[5],
"value7": values[6],
"value8": values[7] if len(values) > 7 else None,
"value9": values[8] if len(values) > 8 else None,
"value10": values[9] if len(values) > 9 else None,
"value11": values[10] if len(values) > 10 else None,
}

df = pd.DataFrame([data_dict]) # ← 1行 DataFrame

# ===== daily Parquet 追記 =====
daily_file = NAS_DIR / f"power_{today}.parquet"

try:
if daily_file.exists():
df_existing = pd.read_parquet(daily_file)
df = pd.concat([df_existing, df], ignore_index=True)

df.to_parquet(daily_file, index=False)
print(f"{daily_file} にデータを保存しました。")

# JSON ログ用に datetime を文字列化
log_dict = data_dict.copy()
log_dict["timestamp"] = log_dict["timestamp"].isoformat()
logging.info(f"データ保存: {json.dumps(log_dict, ensure_ascii=False)}")

except Exception as e:
logging.error(f"Parquet 保存エラー: {e}")
print(f"Parquet 保存エラー: {e}")

★印の箇所は、環境に合わせて、書き換えてください。
NAS設定はこちらのページを参考してください

cron設定

*/10 * * * *  /usr/bin/python3 /home/hsbox/pyd/fm_dataget.py

CRON設定で、10分おきに実行するように設定します。
CRON設定の方法は、hsbox本家サイトのページを参考にしてください。

このような感じでデータを取得できました


読み込み完了! → 143 行 × 12 列
timestamp value1 value2 value3 value4 value5 value6 value7 value8 value9 value10 value11
0 2025-12-01 00:00:03.338238 0.00 1.15 0.00 6.39 99.59 6.64 99.35 --/-- --:-- -.-- -.-- --/-- --:--
1 2025-12-01 00:10:03.199163 0.00 1.84 0.00 11.67 99.55 8.01 99.48 --/-- --:-- -.-- -.-- --/-- --:--
2 2025-12-01 00:20:03.231549 0.00 1.19 0.00 5.79 100.05 7.47 99.64 --/-- --:-- -.-- -.-- --/-- --:--
3 2025-12-01 00:30:02.432434 0.00 1.66 0.00 10.68 99.80 7.15 99.72 --/-- --:-- -.-- -.-- --/-- --:--
4 2025-12-01 00:40:03.512127 0.00 1.12 0.00 5.50 100.37 7.18 99.92 --/-- --:-- -.-- -.-- --/-- --:--
5 2025-12-01 00:50:02.524990 0.00 1.66 0.00 10.88 99.81 7.06 99.78 --/-- --:-- -.-- -.-- --/-- --:--
6 2025-12-01 01:00:03.315994 0.00 1.67 0.00 11.06 100.06 7.03 100.07 --/-- --:-- -.-- -.-- --/-- --:--
7 2025-12-01 01:10:02.494529 0.00 1.13 0.00 5.94 100.36 6.77 100.07 --/-- --:-- -.-- -.-- --/-- --:--
8 2025-12-01 01:20:03.026167 0.00 1.08 0.00 5.49 100.05 6.85 99.70 --/-- --:-- -.-- -.-- --/-- --:--
9 2025-12-01 01:30:03.163444 0.00 1.03 0.00 5.21 100.12 6.67 99.77 --/-- --:-- -.-- -.-- --/-- --:--
10 2025-12-01 01:40:02.385593 0.00 0.99 0.00 5.46 100.18 6.18 99.92 --/-- --:-- -.-- -.-- --/-- --:--
11 2025-12-01 01:50:03.190017 0.00 0.91 0.00 5.44 100.25 5.43 100.11 --/-- --:-- -.-- -.-- --/-- --:--

それぞれの項目のデータの意味は次のようになっているようです。

保存されたデータを確認

とりあえず一部のみです。

発電量のデータです。 多分取れているようです。2週間ほど並行してそらーフロンティアに上がっているデータと一致しているか、詳細確認をしてみます。

関連記事

本番、仕切り直し。(proxy設定 httpからhttpsに変換、 ポストデータ取得を検証)この方法は断念…   

「フロンティアモニターホームサーバー」のプロキシ設定を変更して、プロキシ経由でのデータ送信を検証してみます。hsBoxのIPとプロキシのポート番号8080を設定しました。すると、フロンティアモニター – ホームエネルギーモニタリングサービス – https://www.frontier-monitor.com/persite/top へのデータ反映が止まりました。当然過去分は見えますが、プロキシ設定変更後のデータが反映されません。 先のポストデータの取得のスクリプトでは、データが取れないどころか、「フロンティアモニターホームサーバー」から何か届いているのかさえも確認できません。スプリプトを改造してスタブ実装で200応答するように改造しましたが、コネクションまでは確認でき接続先サーバーを記録できることまではできましたが、TLS接続してくるのを疑似CAで応答できなさそうであることを確認しました。

solar
solar

PROXY方式についての結論

ユーザー名、パスワードを設定してもhttpsで接続し、httpで接続してくることはない。疑似的接続させることもほぼ不可能である。
 ということで、PROXY方式での情報採取はあきらめました。


再び、内部APIの調査、CGIでデータを採取

次回は、内部CGIでデータをとれるかを検証してみます。 どうもこっちが本命になりそう。


関連記事

さて本番だ、切り替えてみよう。(proxy設定 httpからhttpsに変換、 ポストデータ取得を検証)あと1歩に見えたが…

「フロンティアモニターホームサーバー」のプロキシ設定を変更して、プロキシ経由でのデータ送信を検証してみます。hsBoxのIPとプロキシのポート番号8080を設定しました。すると、フロンティアモニター – ホームエネルギーモニタリングサービス – https://www.frontier-monitor.com/persite/top へのデータ反映が止まりました。当然過去分は見えますが、プロキシ設定変更後のデータが反映されません。 先のポストデータの取得のスクリプトでは、データが取れないどころか、「フロンティアモニターホームサーバー」から何か届いているのかさえも確認できません。横から、テスト用のポストをするとデータは記録されるので、構築した環境は動いているようです。

今回の結果を先に書くと、www.frontier-monitor.comの仕様の古さのために、当初の第一階目のゴールにはたどり着けないということが判明した。そして、いきなり最終ゴールにむけた実装が必要ということがわかった。調査結果を以下に書く。ゴールだけを見たいという人はこの記事は読み飛ばしてもらって構わない。

再びデバック開始

よく見たら、ジャーナルにたくさん「フロンティアモニターホームサーバー」接続記録が出ていました。

journalctl -u fm-mitmproxy.service -f


11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:04.599][192.168.x.xx:57372] server connect www.frontier-monitor.com:443 (150.31.252.104:443)
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:05.235][192.168.x.xx:57372] Client TLS handshake failed. Client and mitmproxy cannot agree on a TLS version to use. You may need to adjust mitmproxy's tls_version_client_min option.
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:05.240][192.168.x.xx:57372] client disconnect
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:05.245][192.168.x.xx:57372] server disconnect www.frontier-monitor.com:443 (150.31.252.104:443)
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:11.362][192.168.x.xx:43760] client connect
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:11.469][192.168.x.xx:43760] server connect www.frontier-monitor.com:443 (150.31.252.104:443)
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:12.019][192.168.x.xx:43760] Client TLS handshake failed. Client and mitmproxy cannot agree on a TLS version to use. You may need to adjust mitmproxy's tls_version_client_min option.
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:12.023][192.168.x.xx:43760] client disconnect
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:12.028][192.168.x.xx:43760] server disconnect www.frontier-monitor.com:443 (150.31.252.104:443)
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:18.092][192.168.x.xx:40724] client connect
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:18.143][192.168.x.xx:40724] server connect www.frontier-monitor.com:443 (150.31.252.104:443)
11月 29 17:11:53 hsbox mitmdump[47809]: [17:10:18.670][192.168.x.xx:40724] Client TLS handshake failed. Client and mitmproxy cannot agree on a TLS version to use. You may need to adjust mitmproxy's tls_version_client_min option.
11月 29

「Client TLS handshake failed. Client and mitmproxy cannot agree on a TLS version to use. You may need to adjust」このログが大量に出ているが、これが問題だったようだ。 TLS1.0に下げるように要求されている。 hsBoxでも設定で下げれないことはないが、外部公開している入り口が怪しくなるので無理にTLS1.0にさげないことにした。

太陽光機器(192.168.*.**)  
      ↓ CONNECT www.frontier-monitor.com:443 HTTP/1.1    プロキシ宛
mitmproxy(192.168.*.*:8080) ←ここで TLS 開始(クライアント側 TLS)  
      ↓ TLS ハンドシェイク開始  
      × 失敗 → Client TLS handshake failed  
      (mitmproxy → 150.31.252.104:443 にはまだ接続すらしていない)


ということで、データのキャプチャにも失敗し、プロキシ経由でのサーバへのアップロードもできていない。 プロキシ設定してから、 www.frontier-monitor.comへのデータアップロードも止まったままである。
  この記事での成果は、「フロンティアモニターホームサーバー」の送信先がwww.frontier-monitor.comであると確認できたことだ。

一旦、切り戻しして、仕切り直しましょう。そして、最終型にむけて再検討します。


関連記事

proxy設定のその2 httpからhttpsに変換、 ポストデータ取得を検証

hsbox の proxy実装の続きをしましょう。 httpは通りました https対応に挑戦です。
最初に検証方法を確認しておきましょう。 httpサービスをしていないhttpsのみのサイトを探しましょう そのサイトを使って、送信データを取得できるか検証しましょう。

確認方法の検討

$ curl -I http://github.com
HTTP/1.1 301 Moved Permanently
Content-Length: 0
Location: https://github.com/


$ curl -I https://github.com
HTTP/2 200
date: Fri, 28 Nov 2025 02:58:12 GMT
content-type: text/html; charset=utf-8
vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, X-Requested-With, Accept-Language,Accept-Encoding, Accept, X-Requested-With
content-language: en-US
etag: W/"06826aee56dafc29be870ab3e992ec77"
cache-control: max-age=0, private, must-revalidate
strict-transport-security: max-age=31536000; includeSubdomains; preload
---以下省略

guithub.comのトップでhttpsへのプロキシが効くが確認することにします。

最初の状態でのProxy動作を確認してみます

$ curl -x http://192.168.2.45:8080 http://github.com

何も応答がありません。
まだ、Proxyが自動的にhttpsに変換していないようです。

プロキシをとおしてプロキシでポストデータを取得するのが目的です。 この場合、POSTはhttpsではなくhttpで送られる必要があるでしょう。そして、プロキシでhttpsに変換する。 そのような使い方をしたいので、 mitmproxy の 設定方法を変更します。

 mitmproxy 用解析・保存スクリプトを更新配置(仮2)

■/home/hsbox/pyd/fm_capture.py  を更新配置  (内容は以下)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File: ~/fm_capture.py

import json
import os
from datetime import datetime
from mitmproxy import http
from mitmproxy import ctx

DATA_DIR = "/home/hsbox/fm_data" # ← 自分のホームに合わせて変更
os.makedirs(DATA_DIR, exist_ok=True)

# fm_capture.py の先頭に追加
force_https_domains = {
"www.frontier-monitor.com",
"github.com",
# ここに対象ドメインを全部書く(または全部強制したいなら条件を緩く)
}

def request(flow):
host = flow.request.pretty_host
if host in force_https_domains or host.endswith(".example.com"):
if flow.request.scheme == "http":
flow.request.scheme = "https"
flow.request.port = 443

def response(flow: http.HTTPFlow):
# フロンティアモニターの送信先だけを対象にする
if "frontier-monitor.com" not in flow.request.pretty_host:
return

if flow.request.path.startswith("/upload/data.php"): # 実際のURLに合わせて調整可
try:
# POSTされたJSONを取得
raw = flow.request.get_text()
data = json.loads(raw)

# タイムスタンプを付与(モニターの時刻を優先)
timestamp = data.get("timestamp", datetime.now().isoformat())

# 1. 生JSONを保存(デバッグ用)
raw_file = f"{DATA_DIR}/raw_{timestamp.replace(':', '-')}.json"
with open(raw_file, "w") as f:
f.write(raw)

# 2. 最新データを上書き保存
latest_file = f"{DATA_DIR}/latest.json"
with open(latest_file, "w") as f:
json.dump(data, f, indent=2)

# 3. SQLiteに挿入(初回はテーブル自動作成)
import sqlite3
db_path = f"{DATA_DIR}/fm_data.db"
conn = sqlite3.connect(db_path)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS power (
ts TEXT PRIMARY KEY,
generation INTEGER,
consumption INTEGER,
grid_buy INTEGER,
grid_sell INTEGER,
temperature REAL,
status INTEGER
)
""")
cur.execute("""
INSERT OR REPLACE INTO power VALUES (?, ?, ?, ?, ?, ?, ?)
""", (
timestamp,
data.get("generation"),
data.get("consumption"),
data.get("grid_buy"),
data.get("grid_sell"),
data.get("temperature"),
data.get("status")
))
conn.commit()
conn.close()

ctx.log.info(f"[FM] データ保存成功 → {timestamp}")
except Exception as e:
ctx.log.error(f"[FM] エラー: {e}")

systemd サービスファイルの更新

[Unit]
Description=Frontier Monitor Transparent Proxy
After=network.target
Wants=network.target

[Service]
Type=simple
User=hsbox
Environment="PATH=/home/hsbox/.local/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/home/hsbox/.local/bin/mitmdump --mode regular --listen-host 0.0.0.0 --listen-port 8080 --set upstream_cert=false --showhost --proxyauth ユーザー名:パスワード@ --script /home/hsbox/pyd/fm_capture.py --quiet
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

ユーザー名とパスワードを設定してください。 使用しない場合、”–proxyauth”の設定は不要です。
上の設定をしたら、設定反映と起動、起動確認を行います。

動作確認

■curlで、 動作検証します。
curl -x http://<プロキシが動作するhsboxのIP>:8080 http://github.com/

実行結果例:
curl -x http://192.168.1.10:8080 http://github.com








<!DOCTYPE html>
<html
lang="en"
data-color-mode="dark" data-dark-theme="dark"
data-color-mode="light" data-light-theme="light" data-dark-theme="dark"
data-a11y-animated-images="system" data-a11y-link-underlines="true"

>




<head>
<meta charset="utf-8">
<link rel="dns-prefetch" href="https://github.githubassets.com">
<link rel="dns-prefetch" href="https://avatars.githubusercontent.com">
<link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
<link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
<link rel="preconnect" href="https://github.githubassets.com" crossorigin>
<link rel="preconnect" href="https://avatars.githubusercontent.com">


<link crossorigin="anonymous" rel="preload" as="script" href="https://github.githubassets.com/assets/global-banner-disable-54e442fb573b.js" />

<link rel="preload" href="https://github.githubassets.com/assets/mona-sans-14595085164a.woff2" as="font" type="font/woff2" crossorigin>



※これで、proxyで、httpをhttpsに変換してアクセスできていそうです。

NAS設定の修正

11月 28 23:03:07 hsbox systemd[1]: Started Frontier Monitor Transparent Proxy.
11月 28 23:03:45 hsbox mitmproxy[1050039]: POST CAPTURE FAILED: [Errno 13] Permission denied: ‘/mnt/nas/solar_data/capture_20251128.log’

NASの書き込み権限がないため書き込めません、mitmproxyは、hsbox権限で起動しているので、権限を777に設定します。

しかし、smbマウントしていると、chmodでは、権限を設定できません。NAS側のGUI等で、ログインユーザの権限等でフルアクセスできるように設定しておきます。
また、暫定対処ですが、起動時に自動マウントするように以下のマウントコマンドを仕込んでおきました。※事前に手動実行で操作確認しておいてください

# mitmproxy 用 NAS マウント
mount -t cifs //<NASのIP>/share /mnt/<マウントポイント> -o username=user,password=pass,vers=3.0,iocharset=utf8,uid=1000,gid=1000,nounix,cache=none,nolease && logger "NAS mounted for mitmproxy by startup script"

 mitmproxy 用解析・保存スクリプトを更新配置(仮3)

キャプチャデータをローカルおよびNASに保存するスプリプとに更新します。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
import os
from datetime import datetime
from mitmproxy import http
from urllib.parse import urlencode

LOG_DIR = "/home/hsbox/fm_data" # まずローカルで確認
#LOG_DIR = "/mnt/nas/solar_data"

os.makedirs(LOG_DIR, exist_ok=True, mode=0o777)

def request(flow):
host = flow.request.pretty_host
if host in {"www.frontier-monitor.com", "github.com"}:
if flow.request.scheme == "http":
flow.request.scheme = "https"
flow.request.port = 443

def response(flow: http.HTTPFlow):
# POSTじゃなければ完全スルー(無駄な書き込みゼロ)
#if flow.request.method != "POST":
# return

now = datetime.now().strftime("%Y%m%d")
logfile = f"{LOG_DIR}/capture_{now}.log"

post_data = ""
if flow.request.urlencoded_form:
post_data = urlencode(flow.request.urlencoded_form)
elif flow.request.multipart_form:
post_data = urlencode(flow.request.multipart_form)
elif flow.request.text:
post_data = flow.request.text

# 空のPOSTは記録しない(必要なら残す)
if not post_data.strip():
return

entry = {
"ts": datetime.now().isoformat(),
"host": flow.request.pretty_host,
"url": flow.request.pretty_url,
"post": post_data
}

try:
with open(logfile, "a", encoding="utf-8", buffering=1) as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
f.flush()
os.fsync(f.fileno())
except Exception as e:
os.system(f'logger -t mitmproxy "POST CAPTURE FAILED: {e}"')

手動でポストをシミュレーションして動作確認

httpsのサイトに手動でポストしてみたデータを保存できるか検証します

~$ curl -x http://<hsBoxのIP>:8080 --insecure -X POST -d "test=2111
1&name=フロンティア
" http://github.com

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; base-uri 'self'; connect-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self'; style-src 'unsafe-inline'">
<meta content="origin" name="referrer">
<title>Page not found &middot; GitHub</title>
<style type="text/css" media="screen">

保存されたデータを確認

{“ts”: “2025-11-29T10:13:24.269169”, “host”: “github.com”, “url”: “https://github.com/”, “post”: “test=11111&name=%C3%A3%C2%83%C2%95%C3%A3%C2%83%C2%AD%C3%A3%C2%83%C2%B3%C3%A3%C2%83%C2%86%C3%A3%C2%82%C2%A3%C3%A3%C2%82%C2%A2”}
{“ts”: “2025-11-29T10:52:06.845328”, “host”: “github.com”, “url”: “https://github.com/”, “post”: “test=21111&name=%C3%A3%C2%83%C2%95%C3%A3%C2%83%C2%AD%C3%A3%C2%83%C2%B3%C3%A3%C2%83%C2%86%C3%A3%C2%82%C2%A3%C3%A3%C2%82%C2%A2“}

1回のポストで1行追加されました。

ポストしたデータが丸ごと入っていることを確認できました。
これでキャプチャ成功です。 
NASへの保存も成功です。

ハードルが複数あるので、着実に1つづつクリアしていくのが、近道でしょう。

・–quiet にしないとサービス起動できない
・サービス起動は通常root相当だが、mitmproxyの起動ユーザはrootではうまく動かない
・書き込みタイミングの課題
・NASの書き込み権限
・hsBox独特?の自動マウントの手法

簡単にまとめると権限問題とタイミング問題ですね。開発者あるあるですね。。

関連記事

hsbox1.3上にproxyを構築する手順

太陽光発電のモニタリングサービスが終了するため、データ取得を検討中です。このデータ取得のために、proxyを構築します。 誰でも簡単に導入できるようにするためにここでは、hsbox(無料版:freebox)上に構築してみます。

どのような構成にするのかは、過去の記事を参考にしてください。ここでは、hsboxに構築する手順に特化して記載します。

0.前準備

hsboxを構築する手順はここでは省きます。本家サイトの記事(リンク先)か、Vectorサイトのドキュメント入りアーカイブを参照してください。
有償版は、GUIから操作できるなど操作性が上がりますが、ここでは無償版でも使える機能をベースに記載します。

1.プロキシのインストール

hsbox1.3は、python3環境を構築済みなので、プロキシのインストールからはじめます。

■1. hsboxに、sshでログインします。  *参考:本家サイト
  ホームディレクトリ /home/hsbox に移動。

■2. mitmproxy をインストール
pip3 install --user mitmproxy

■3. 実行パスを通す
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

■4. スクリプト等配置用のディレクトリ作成
mkdir /home/hsbox/pyd

2. mitmproxy 用解析・保存スクリプトを配置(仮版)

■/home/hsbox/pyd/fm_capture.py  を配置  (内容は以下)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File: ~/fm_capture.py

import json
import os
from datetime import datetime
from mitmproxy import http
from mitmproxy import ctx

DATA_DIR = "/home/hsbox/fm_data"   # ← 自分のホームに合わせて変更
os.makedirs(DATA_DIR, exist_ok=True)

def response(flow: http.HTTPFlow):
    # フロンティアモニターの送信先だけを対象にする
    if "frontier-monitor.com" not in flow.request.pretty_host:
        return

    if flow.request.path.startswith("/upload/data.php"):  # 実際のURLに合わせて調整可
        try:
            # POSTされたJSONを取得
            raw = flow.request.get_text()
            data = json.loads(raw)

            # タイムスタンプを付与(モニターの時刻を優先)
            timestamp = data.get("timestamp", datetime.now().isoformat())

            # 1. 生JSONを保存(デバッグ用)
            raw_file = f"{DATA_DIR}/raw_{timestamp.replace(':', '-')}.json"
            with open(raw_file, "w") as f:
                f.write(raw)

            # 2. 最新データを上書き保存
            latest_file = f"{DATA_DIR}/latest.json"
            with open(latest_file, "w") as f:
                json.dump(data, f, indent=2)

            # 3. SQLiteに挿入(初回はテーブル自動作成)
            import sqlite3
            db_path = f"{DATA_DIR}/fm_data.db"
            conn = sqlite3.connect(db_path)
            cur = conn.cursor()
            cur.execute("""
                CREATE TABLE IF NOT EXISTS power (
                    ts TEXT PRIMARY KEY,
                    generation INTEGER,
                    consumption INTEGER,
                    grid_buy INTEGER,
                    grid_sell INTEGER,
                    temperature REAL,
                    status INTEGER
                )
            """)
            cur.execute("""
                INSERT OR REPLACE INTO power VALUES (?, ?, ?, ?, ?, ?, ?)
            """, (
                timestamp,
                data.get("generation"),
                data.get("consumption"),
                data.get("grid_buy"),
                data.get("grid_sell"),
                data.get("temperature"),
                data.get("status")
            ))
            conn.commit()
            conn.close()

            ctx.log.info(f"[FM] データ保存成功 → {timestamp}")
        except Exception as e:
            ctx.log.error(f"[FM] エラー: {e}")

3. systemd サービスファイル

[Unit]
Description=Frontier Monitor Transparent Proxy
After=network.target
Wants=network.target

[Service]
Type=simple
User=hsbox
Environment="PATH=/home/hsbox/.local/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/home/hsbox/.local/bin/mitmdump --mode regular --listen-host 0.0.0.0 --listen-port 8080 --script /home/hsbox/pyd/fm_capture.py --quiet
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

※ファイルの書き込みはいろいろありますが、ルート権限で上書きcat するのか簡単でしょう。

4. 設定反映と起動

# ファイルを反映
sudo systemctl daemon-reload

# 自動起動設定+今すぐ起動
sudo systemctl enable fm-mitmproxy.service
sudo systemctl start fm-mitmproxy.service

# 状態確認
sudo systemctl status fm-mitmproxy.service
journalctl -u fm-mitmproxy.service -f # リアルタイムログ

参考

statusでの確認で、起動していれば次のように”active (running)”が表示されます

root@hsbox:~# sudo systemctl status fm-mitmproxy.service
● fm-mitmproxy.service - Frontier Monitor Transparent Proxy
Loaded: loaded (/etc/systemd/system/fm-mitmproxy.service; enabled; vendor >
Active: active (running) since Sun 2025-11-23 15:29:08 JST; 1 day 7h ago
Main PID: 135951 (mitmdump)
Tasks: 2 (limit: 4378)
Memory: 46.0M
CPU: 1min 2.732s
CGroup: /system.slice/fm-mitmproxy.service
mq135951 /usr/bin/python3 /home/hsbox/.local/bin/mitmdump --mode r>

11月 23 15:29:08 hsbox systemd[1]: Started Frontier Monitor Transparent Proxy.

動作確認

■curlで、 動作検証します。
curl -x http://<プロキシが動作するhsboxのIP>:8080 http://mic.or.jp/

例:
curl -x http://192.168.1.10:8080 http://mic.or.jp/

※とりあえず、確認できるのはhttpのみ、 この設定だけではhttpsサイトへのproxy利用ができません。 httpsは次のステップです。

関連記事

ソーラーフロンティア ホームエネルギーモニタリングサービス終了! 2025年12月solar発電データ監視がストップ?の代替で継続を検討

ソーラーフロンティア ホームエネルギーモニタリングサービスが2025年12月に終了!hsboxでsolar発電データ監視を代替を検討

過去の関連記事

はじめに

次のようにフロンティアモニターのシステム終了のアナウンスがきています。
hsBoxを使って代替機能を実装していきましょう。

**** 様

日頃より【フロンティアモニター】ホームエネルギーモニタリングサービスをご利用いただき、誠にありがとうございます。

【システム終了のお知らせ】
2025年12月22日(月)をもって本計測装置のサービスを終了いたします。
なお、システムの都合により、一部サービス終了のタイミングについては前後する可能性がございますので、ご承知おきください。
詳しくはお客様ご利用サイトのお知らせ欄をご覧ください。

本メール発信は、メールシステムメンテナンスにより、1日遅延する場合があります。メンテナンスの日程は、お客様ログイン画面の「お知らせ」欄に随時記載いたします。
メンテナンス時はご不便をおかけしますが、何卒ご承知おきくださいますようお願いいたします。

下記の通り、2025年11月07日の発電量をお知らせいたします。

発電量:37.44kWh


今後ともフロンティアモニターをよろしくお願いいたします。
★なお、お心当たりのない方は、お手数ではございますが、下記メールアドレスまでご連絡頂きますようお願いいたします。
★このメールは送信専用メールアドレスから配信しています。このまま返信いただいてもお答えできませんのでご了承ください。
-----------------------------------------------
ソーラーフロンティア株式会社
【フロンティアモニター】お客様サービスセンター
電話:0570-053115(受付時間:9:00-17:00)※日曜、祝祭日、メーデー、年末年始を除く
メール:information@solar-frontier.com
-----------------------------------------------

順次、実装を進めていきます。 参考してみてください。 コメントなどあればお願いします。

まずは、次の情報を集めます

●パワーコンディショナー(メーカーと型番)
●フロンティアモニターの接続方式

次の図ような方式で、データ収集できそうな目途が立ちました。
※最初PROXY方式で検討しましたが、途中で直接採取する方式に変更してデータ取得できるようになりました。

順番に構築・検証していきます。以下に一部公開済みです。

hsBoxにproxyを構築 [公開済み]
hsBoxのproxyでデータ収集を検証[公開済み]
proxyで実データ収集に調整 pruxy方式は放棄、諦めて別方式へ[公開済み]
・hsBoxで、直接発電電力データの取得に成功!![公開済み]
・hsBoxで収集してデータを蓄積[公開済み、取得と同時にNASへ蓄積]
・hsBoxで取得したデータの妥当性を比較検証[公開済み]
・集積したデータでグラフを書かせてみた[公開済み]
・発電量通知メールを置き換え LINEで通知[公開済み]
・異常検知を検知して、メール、LINEで通知 [公開済み]

この後の作業予定です。[近日追加公開予定]
・蓄積したデータのhsBoxでTVやスマートディスプレイに表示

関連記事