2026年10月14日作成
遠隔地の電力情報を監視してみる
1.はじめに
2.全体の構成について
3.keilogのセットアップ
4.データ受信
5.InfluxDBの導入とtelegrafの設定
6.リソース節約術
1.はじめに
今回は補足的な記事です。
以前、Bルートで電力を取得してGrafanaで監視するという記事を書きましたが、
実家等の遠隔地の拠点の電力情報を取得しようとしたところ思い通りに動かない部分があり
Geminiの力を借りながら構築したものを備忘録的に書いていきます。
ベースは以前の記事で書いているので端折りながら書いていこうと思います。
以前の記事:Bルートで電力を取得してGrafanaで監視する
2.全体の構成について
全体の構成としては以下のように電力情報の取得を遠隔地に設置した機器で行い、
telegrafによるDBへの取り込みやInfluxDBでの管理は手元の環境で行います。
<遠隔地>
・Wi-SUNデバイスでのスマートメータとの通信
・keilogによる取得データのtxt出力及びデータ送信
※要求機器スペックはラズパイレベルでOK。RaspberryPi 3B+でも安定して稼働できます。
<手元の環境>
・telegrafでのInfluxDBへの取り込み
・InfluxDBによる時系列データとしての管理
※要求機器スペックは場合によっては高くなります。後述のリソース節約術で負荷はだいぶ軽減できます。
3.keilogのセットアップ
Wi-SUNデバイスのセットアップなどについては特に変更点はないため省略します
keilogの導入については以前の記事を参照してください。
外部送信機能を利用する場合はkeiconf.pyに記述する通常の設定内容に加えて以下の部分を設定する必要があります。
#必須ではありませんが、長期的な運用を想定する場合は瞬断などに備えてアップロードキューを多めに設定しておきます
-upload_que = queue.Queue(50)
+upload_que = queue.Queue(200)
#アップロード先を設定します
-target_url = 'https://example.com/upload.php'
-upload_key = 'xxxxxxxxxxxxxxxx'
+target_url = 'http://[[送信先ホストIP]]/upload.php'
+upload_key = '[[任意の文字列]]'
アップロードキーについては任意の文字列を設定します。特に希望がない場合などは、
以下のコマンドで自動生成してもよいかと思います。
$ openssl rand -base64 16
続いてkeilibディレクトリ配下のupload.pyの以下の部分を修正し動作状況がログに出力されるように改変します。
~~略~~
def run ( self ):
logger.info('[START]')
# self.upload_que.put(['test.txt', 'This is test data\n'])
while not self.stopEvent.is_set():
# get data from upload_que(queueからデータの取得)
try:
filename, data = self.upload_que.get(timeout=3)
except:
# logger.debug('upload que is empty')
continue
payload = {
'type' : 'text',
'key' : self.upload_key,
'fname': filename,
'data' : data
}
- logger.debug(payload)
+ logger.debug(f"Sending payload: {payload}")
# POST execution(POST実行)
try:
- response = requests.post(self.target_url, payload)
- except:
- logger.error('requests post error')
+ #timeout=10 を設定して応答待ちの無限停止を防止
+ response = requests.post(self.target_url, data=payload, timeout=10)
+ #サーバーからのレスポンス結果をログに出力
+ res_text = response.text.strip()
+ #以下のコメントアウトを外すと送信成功時のレスポンスをログに記録する。
+ #通常運用時はログが埋まるためコメントアウト推奨
+ #logger.info(f"Server Response ({response.status_code}): {res_text}")
+ if res_text != "OK.":
+ logger.warning(f"Upload was rejected by server: {res_text}")
+ except Exception as e:
+ logger.error(f"requests post error: {e}")
continue
logger.info('[STOP]')
また、もともとのプログラムでは10分ごとの平均値(sumyyyymmdd-smartmeter-log.txtに出力される値)
しか送信しないようになっています。
そこで10秒ごとの瞬時電力値も送信したい場合は
keilibディレクトリ配下のrecorder.pyの以下の部分を追記します。
~~略~~
# ファイルへの書き出し(1行)
linedata = self.date+' '+self.mytime+','+unit+','+sensor+','+str(round(value,4))+','+id+'\n'
filename = self.key01m[:8]+'-'+self.fileNameBase+'.txt'
with open(filename, 'a') as f:
f.write(linedata)
self._send_disp( unit, sensor, value )
+ # ==============================================================
+ # ★ リアルタイム送信用の追加コード
+ # ==============================================================
+ if self.upload_que is not None:
+ try:
+ # リアルタイム保存用のファイル名(例: real20261009-smartmeter-log.txt)
+ real_filename = self.key01m[:8] + '-' + self.fileNameBase + '.txt'
+ self.upload_que.put([real_filename, linedata], block=False)
+ except:
+ # キューが満杯の場合はスキップ
+ pass
def _send_disp( self, unit, sensor, value ):
"""データが disp_def に一致するとき disp_queue に送信する
"""
~~略~~
あとはスクリプトの設定、自動起動を設定すれば遠隔地側の設定は完了です。
4.データ受信
データの送信はHTTPプロトコルで行われます。
また受信側のPHPプログラムをkeilogの中に格納してくれているのでこれを利用します。
まずはWebサーバ及びPHP環境を導入します。
$ sudo apt install apache2 php libapache2-mod-php
導入にあたっての詳細についてはこちらの記事を参照して貰えればと
apacheを導入するとWebサーバのルートディレクトリが/var/www/html/にあるため直下にupload.phpを配置します。
このupload.phpは前述のkeilogのPHPディレクトリ配下に格納されているものを利用します。
なお、PHPのバージョンアップに伴い一部設定を以下のように変更します。(執筆時のPHP8.5で動作します)
<?php
// POSTされてきた内容をチェックし保存
// 不正なアクセスはリジェクト
+// JITメモリ割り当て警告の防止
+ini_set('pcre.jit', 0);
// アップロードキーのチェック
-if ($_POST["key"] !== "xxxxxxxxxxxxxxxxxxxxxxxx") {
- exit("Error: Invalid key is specified.");
-}
#LAN内で完結する場合などで認証が不要な場合はコメントアウトしてもよいかと思います
+if ($_POST["key"] !== "[[keiconf.pyで設定したアップロードキー]]") {
+ exit("Error: Invalid key is specified.");
+}
// アップロード間隔による制限。頻繁なPOSTによる処理を制限
// 以下の例は前回のアップロードから100秒以上
try {
- $ptime = file_get_contents('ptime.txt');
+ $ptime = (int)trim(file_get_contents('ptime.txt'));
} catch (Exception $e) {
$ptime = 0;
}
#瞬時電力を取得する場合は10秒未満に設定。場合によっては以下のようにコメントアウトしても良いかも
-$ctime = time();
-if ($ctime - $ptime < 100) {
- exit("Error: Upload interval is too short. Try later.");
-}
+$ctime = time();
+//if ($ctime - $ptime < 100) {
+// exit("Error: Upload interval is too short. Try later.");
+//}
// データサイズによる制限
// 以下の例は 1000 Byte 未満
$data = $_POST["data"];
$length = strlen($data);
if ($length > 1000) {
exit("Error: Too big data.");
}
// ファイル名長さチェック
// 以下の例は 20 Byte 未満
$fname = $_POST["fname"];
$length = strlen($fname);
#デフォルトのファイル名が20Byte以上となるため50Byte未満に緩和
-if ($length > 20) {
- exit("Error: Too long file name.");
-}
+if ($length > 50) {
+ exit("Error: Too long file name.");
+}
// ファイル名チェック、上書きして良いファルだけ
// 以下の例は、パス区切り文字/(スラッシュ)を含めない。想定外のディレクトリに書き込まれないように。
#デフォルトのファイル名に-が入ってるものの許可されていないため、許容する文字種を緩和
-if ( ! preg_match("/^[A-Za-z0-9_]+(\.[A-Za-z0-0_]+)*$/", $fname)) {
- exit("Error: Invalid file name.");
-}
+if ( ! preg_match("/^[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*$/", $fname)) {
+ exit("Error: Invalid file name.");
+}
// その他のチェックを行う
// 大切なファイルを破壊されないように
// 以下の例は update_ok.txt というファイルだけ通す
# if ( ! preg_match("/^update_ok\.txt$/", $fname)) {
# exit("Error: The file name is not allowd.");
# }
// 指定されたファイルにデータを追記
$fp = fopen("data/" . $fname, 'a');
fwrite($fp, $data);
fclose($fp);
file_put_contents('ptime.txt', $ctime);
print "OK.";
?>
このPHPファイル以外に、受信したデータを保存するためのdataディレクトリを作成します。
また、これらを配置したあとにすべてのディレクトリ・ファイルの所有者権限を"www-data"に変更します。
ここまでで以下のようなファイル構成となればOKです。
$ sudo ls -la /var/www/html/
drwxr-xr-x 4 www-data www-data 4096 Oct 13 08:51 .
drwxr-xr-x 3 root root 4096 Oct 10 21:30 ..
drwxr-xr-x 2 www-data www-data 69632 Oct 14 02:51 data
-rw-r--r-- 1 www-data www-data 0 Oct 10 23:50 index.html
-rw-r--r-- 1 www-data www-data 1921 Oct 13 01:21 upload.php
$
ここまでの設定がうまく行っていれば、電力データの取得と同時にこのdataディレクトリに電力データが保存されます。
5.InfluxDBの導入とtelegrafの設定
InfluxDBとtelegrafの導入については変わりないため割愛します。
telegrafの設定についてもほとんど変わらないですが、
データソースの指定部分だけ変わるため、以下のように変更しておきます。
/etc/telegraf/telegraf.conf
~~略~~
# Parse a complete file each interval
[[inputs.file]]
name_override = "BRoute"
## Files to parse each interval. Accept standard unix glob matching rules,
## as well as ** to match recursive files and directories.
- files = ["/script/keilog/????????-smartmeter-log.txt"]
+ files = ["/var/www/html/data/????????-smartmeter-log.txt"]
## Character encoding to use when interpreting the file contents. Invalid
## characters are replaced using the unicode replacement character. When set
## to the empty string the data is not decoded to text.
## ex: character_encoding = "utf-8"
## character_encoding = "utf-16le"
## character_encoding = "utf-16be"
## character_encoding = ""
# character_encoding = ""
~~略~~
あとはInfluxDBでの取り込み結果を確認して問題なければ、Grafanaで表示させて完了となります。
6.リソース節約術
telegrafでのデータ取り込みを1年とか続けていると、
取り込むべきテキストファイルとデータ項目が膨大な物量となってきます。
DBに取り込んでしまえば元のテキストファルを読み込む必要はないため
定期的にデータソースから退避させることで、telegrafの負荷を下げます。
以下のようなスクリプトを作成し、スクリプト実行当日と前日分以外の受信データをファイル名から判断し退避させます。
スクリプト例では、新たに用意した/var/www/html/old-data/ディレクトリに対比させtelegrafの参照元から退避させています。
これを夜間にcronで定期実行することで、telegrafが読み込むデータは最大2日分に抑えることができます。
/var/www/html/move.sh
#!/bin/bash
# ディレクトリの定義
SRC_DIR="/var/www/html/data/"
DEST_DIR="/var/www/html/old-data/"
# 移動先フォルダが存在しない場合は作成
mkdir -p "$DEST_DIR"
# 今日と昨日の日付文字列を取得
TODAY=$(date +%Y%m%d)
YESTERDAY=$(date -d "1 day ago" +%Y%m%d 2>/dev/null)
# 日付の取得チェック
if [ -z "$TODAY" ] || [ -z "$YESTERDAY" ]; then
echo "エラー: 日付の取得に失敗しました。" >&2
exit 1
fi
# フォルダA内のファイルを1つずつ確認
for file in "$SRC_DIR"/*; do
[ -f "$file" ] || continue
filename=$(basename "$file")
# ファイル名に今日または昨日が含まれているか判定 (sh互換のcase文)
case "$filename" in
*"$TODAY"*|*"$YESTERDAY"*)
# 今日・昨日のファイルはそのまま残す
continue
;;
*)
# それ以外のファイルを移動
mv "$file" "$DEST_DIR"
echo "Moved: $filename"
;;
esac
done
続いてcronの定義を設定し、設定を適用します。
$ sudo crontab -u root -e
~~略~~
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
0 1 * * * sh /var/www/html//move.sh
$sudo systemctl restart cron
上記の設定例では毎日午前1時にスクリプトを実行します。
<前へ戻る
《参考ページ》
・Hatena Blog - スマートメーターから消費電力データを取得してグラフ化する ①概要編https://suzu-ha.com/entry/2024/01/21/151655
・Github - スマートメーター&計測ロガー
https://github.com/kjmat/keilog
・InfluxDB
https://www.influxdata.com/products/influxdb/
・Grafana
https://grafana.com/oss/grafana/
・Github - ホームアシスタント アドオン BRoute-Mqtt
https://github.com/hsakoh/broute-mqtt
・ZENN - 適当なキー文字列を生成する
https://zenn.dev/takaha4k/articles/8452cad6c4c392
