# Cube J1 でスマートメーターの電力を Grafana に送る

2026-10-03

![Grafana に表示した直近 24 時間の瞬時電力。普段は 600〜900 W で推移し、朝と夕方に 2 kW を超える山がある](power.png)

家のスマートメーターから瞬時電力を取り出し、Grafana Cloud のダッシュボードで眺められるようにしました。
取り出しには、サービスが終了した NextDrive Cube J1 を使っています。
この記事では、Cube J1 が MQTT に送った値を Telegraf で受けて Prometheus 形式に直し、Grafana Cloud に remote write するまでの構成と設定を紹介します。

## Cube J1 と cube-j1-mqtt

Cube J1 は、スマートメーターの B ルート[^broute]から電力を読み取る Wi-SUN ゲートウェイです。
NextDrive のクラウドサービスは 2025 年 3 月 31 日に終了し、いまは本体だけが中古で出回っています。

この Cube J1 を Home Assistant 用の MQTT デバイスとして使うツールを、つよぽんさんが [cube-j1-mqtt](https://github.com/tsuyopon123/cube-j1-mqtt) として公開しています。
設定ファイルを書いた USB メモリを挿して電源を入れるだけで、本体内蔵の Wi-SUN モジュールからスマートメーターに問い合わせ、結果を MQTT で送信するプログラムが常駐します。
分解せずに root を取ってプログラムを常駐させる仕組みは、同じ作者の [Zenn の記事](https://zenn.dev/tsuyopon123/articles/cube-j1-root)に詳しく書かれています。

公開時のポストでは、Cube J1 がヤフオクで 2,500 円ほどで手に入ることにも触れられています。

https://x.com/tsuyopon_dev/status/2054137344824049913

我が家には、B ルートの電力値を読める Nature Remo E lite もあります。
ただ、Remo E lite の電力データを取れる API は、Nature のサーバーを経由する [Cloud API](https://support.nature.global/hc/ja/articles/50769988430489) だけです。
家の中から直接呼べる Local API は赤外線信号の送受信にしか対応しておらず、電力値は読めません。
Cloud API には、Nature アカウントごとに 5 分あたり 30 回までというリクエストの上限もあります。
クラウドを通さないと値を取り出せない点が、以前から不満でした。
値段も、現行の後継機 Nature Remo E2 lite が公式ストアで 19,800 円（2026 年 10 月時点）なので、気軽には勧めにくいと感じています。

cube-j1-mqtt は Home Assistant に送ることを想定していますが、MQTT のメッセージはほかのプログラムからも購読できます。
今回は Telegraf で受信し、Grafana Cloud に送信しました。

[^broute]: 家庭内の機器がスマートメーターに問い合わせて計測値を受け取る経路です。多くは 920 MHz 帯の Wi-SUN で通信し、ECHONET Lite で値を要求すると応答が返ります。電力会社の検針に使う経路（A ルート）とは別で、利用するには一般送配電事業者に申し込んで認証 ID とパスワードを受け取ります。

## 全体の構成

```
スマートメーター
   │ Wi-SUN (920 MHz) / ECHONET Lite
   ▼
Cube J1 ── mqtt_bridge.py
   │ MQTT (Wi-Fi 2.4 GHz)
   ▼
家の Linux サーバー (Docker Compose)
   ├─ Mosquitto ……… MQTT ブローカー
   ├─ Home Assistant … 同じトピックを購読（任意）
   └─ Telegraf ……… MQTT を購読して remote write
          │
          ├─▶ Grafana Cloud (Prometheus)
          └─▶ ローカルの Prometheus
```

Cube J1 は `cubej/<device_id>/<センサー名>` というトピックに、数値だけを含むペイロードを送信します。
cube-j1-mqtt が送るのは次の 5 つです。

| トピック | 内容 | 単位 |
|---|---|---|
| `cubej/cubej1/power` | 瞬時電力 | W |
| `cubej/cubej1/energy_forward` | 積算電力量（買電） | kWh |
| `cubej/cubej1/energy_reverse` | 積算電力量（売電） | kWh |
| `cubej/cubej1/current_r` | 瞬時電流 R 相 | A |
| `cubej/cubej1/current_t` | 瞬時電流 T 相 | A |

我が家は単相 2 線式なので T 相がなく、スマートメーターは T 相の電流を ECHONET Lite の「計測なし」を表す値（`0x7FFE`）で返します。
cube-j1-mqtt のコードはこの値も区別せずに換算するので、このメーターでは T 相が 3276.6 A という値で送られるはずです。
手元で使っているブリッジはこの値を捨てるようにしてあり、T 相の値は送っていません。

Home Assistant と Telegraf は、どちらも Mosquitto から同じトピックを購読します。
Cube J1 から見ると送り先はブローカー 1 つだけなので、Grafana 向けの経路を足しても Cube J1 側の設定は変わりません。

## Cube J1 の準備

手順は cube-j1-mqtt の README のとおりです（この記事は 2026 年 5 月時点の版で確かめています）。
`production_tool/config.json` に B ルートの認証情報と MQTT ブローカーの接続先を書き、`production_tool/wpa_supplicant.conf` に Wi-Fi の SSID とパスワードを書きます。

```json
{
    "br_id": "（B ルート認証 ID、32 文字）",
    "br_pwd": "（B ルートパスワード、12 文字）",
    "mqtt_host": "192.168.1.10",
    "mqtt_port": 1883,
    "mqtt_user": "cubej1",
    "mqtt_pass": "（MQTT のパスワード）",
    "device_id": "cubej1",
    "serial_port": "/dev/ttyS1",
    "poll_interval": 60
}
```

`mqtt_host` には、Mosquitto を動かすサーバーの IP アドレスを書きます。
FAT32 でフォーマットした USB メモリのルートに、リポジトリ直下の空ファイル `CubeJMTS.txt` と `production_tool/` ディレクトリをコピーします。
この USB メモリを Cube J1 に挿して電源を入れると、セットアップが始まります。
終わると LED が白く 10 回点滅します。

ここで一度つまずきました。
Cube J1 の Wi-Fi は 2.4 GHz にしか対応していません。
我が家のアクセスポイントは 2.4 GHz と 5 GHz で同じ SSID を出しており、この状態では Cube J1 が Wi-Fi に繋がりませんでした。
原因までは切り分けていませんが、`wpa_supplicant.conf` の `network` ブロックに `freq_list` を書き、2.4 GHz の周波数だけを候補にしたところ繋がりました。

```
network={
    ssid="（SSID）"
    psk="（パスワード）"
    freq_list=2412 2417 2422 2427 2432 2437 2442 2447 2452 2457 2462 2467 2472
}
```

## Mosquitto を立てる

ブローカーには Eclipse Mosquitto 2 を使い、後で立てる Telegraf と同じ `compose.yaml` で動かします。
同じ Compose プロジェクトのサービスは同じネットワークに入るので、Telegraf からはサービス名の `mosquitto` で接続できます。
Cube J1 は別の機器から接続してくるので、1883 番ポートをサーバーの LAN 側のアドレスに公開します。
この例では LAN 内だけで使う前提で、TLS は設定していません。
ルーターでインターネット側に公開せず、必要に応じてサーバーのファイアウォールでも接続元を制限してください。

```yaml
services:
  mosquitto:
    image: eclipse-mosquitto:2
    restart: unless-stopped
    ports:
      - "192.168.1.10:1883:1883"
    volumes:
      - ./mosquitto/config:/mosquitto/config:ro
      - ./mosquitto/data:/mosquitto/data
      - ./mosquitto/log:/mosquitto/log
```

`mosquitto/config/mosquitto.conf` では匿名接続を禁止し、パスワードファイルで認証します。

```
persistence true
persistence_location /mosquitto/data/

log_dest file /mosquitto/log/mosquitto.log
log_dest stdout

listener 1883
protocol mqtt

allow_anonymous false
password_file /mosquitto/config/passwd
```

パスワードファイルは `mosquitto_passwd` で作ります。
Cube J1 用、Home Assistant 用、Telegraf 用にユーザーを分けておきます。
分けているのは認証情報を機器ごとに分けるためで、ACL は設定していません。
認証されたユーザーは、すべてのトピックへの送信と購読ができます。
Cube J1 の設定ファイルには MQTT のパスワードが平文で入るので、ACL で送信できるトピックを絞ることも検討してください。

```sh
docker run --rm -it -v "$PWD/mosquitto/config:/mosquitto/config" eclipse-mosquitto:2 \
  mosquitto_passwd -c /mosquitto/config/passwd cubej1
```

2 つ目以降のユーザーは `-c` を外して追加します。
Cube J1 が送り始めたら、`mosquitto_sub` で値が届いているかを確かめられます。

```sh
mosquitto_sub -h 192.168.1.10 -u telegraf -P '（パスワード）' -t 'cubej/#' -v
```

```
cubej/cubej1/power 821
cubej/cubej1/current_r 9.0
```

## Telegraf で受けて remote write する

Telegraf は `inputs.mqtt_consumer` でトピックを購読し、`outputs.http` の `prometheusremotewrite` 形式で Prometheus に書き込みます。
設定の全体は次のとおりです。

```toml
[agent]
  omit_hostname = true

[[inputs.mqtt_consumer]]
  servers = ["tcp://mosquitto:1883"]
  topics = [
    "cubej/+/power",
    "cubej/+/energy_forward",
    "cubej/+/energy_reverse",
    "cubej/+/current_r",
    "cubej/+/current_t",
  ]
  username = "telegraf"
  password = "${MOSQ_PASSWORD}"
  client_id = "telegraf-cubej"
  data_format = "value"
  data_type = "float"
  name_override = "mqtt_measure"

  [[inputs.mqtt_consumer.topic_parsing]]
    topic = "cubej/+/+"
    tags = "_/device_id/sensor"

[[processors.starlark]]
  namepass = ["mqtt_measure"]
  source = '''
SUFFIX = {
    "power": "power_watts",
    "energy_forward": "energy_forward_kwh_total",
    "energy_reverse": "energy_reverse_kwh_total",
    "current_r": "current_r_amperes",
    "current_t": "current_t_amperes",
}

def apply(metric):
    suffix = SUFFIX.get(metric.tags.get("sensor"))
    v = metric.fields.pop("value", None)
    if suffix == None or v == None:
        return None
    metric.name = "cube_j1_smart_meter"
    metric.fields[suffix] = v
    metric.tags.pop("topic", None)
    metric.tags.pop("sensor", None)
    return metric
'''

[[outputs.http]]
  url = "${GRAFANA_PROM_URL}"
  method = "POST"
  data_format = "prometheusremotewrite"
  username = "${GRAFANA_PROM_USER}"
  password = "${GRAFANA_PROM_KEY}"

  [outputs.http.headers]
    Content-Type = "application/x-protobuf"
    Content-Encoding = "snappy"
    X-Prometheus-Remote-Write-Version = "0.1.0"
```

ペイロードは数値 1 つだけなので、`data_format = "value"` でそのまま読めます。
デバイス ID とセンサー名はトピックに入っています。
そこで `topic_parsing` でトピックを `/` で区切り、2 番目を `device_id` タグに、3 番目を `sensor` タグにしています。

Starlark のプロセッサーは、メトリクス名を整えるために入れました。
`prometheusremotewrite` 形式では、Prometheus 上のメトリクス名が「measurement 名 + `_` + field 名」になります。
何もしないと、電力も電流も積算電力量も `mqtt_measure_value` という 1 つのメトリクス名になり、`sensor` ラベルの違いだけで区別されます。
Prometheus では、同じメトリクス名を持つ系列には同じ意味と単位を持たせるのが慣例です。
そこで measurement 名を `cube_j1_smart_meter` に揃え、field 名にセンサーごとの接尾辞を入れて、`cube_j1_smart_meter_power_watts` や `cube_j1_smart_meter_current_r_amperes` という名前で届くようにしました。
接尾辞には単位名を入れ、増え続ける積算値には `_total` を付けています。
積算電力量はスマートメーターが返す kWh のまま扱っているので、Prometheus が推奨する基本単位（エネルギーならジュール）には合わせていません。

`mqtt_consumer` はメッセージを受け取るたびにメトリクスを作る入力プラグインなので、`[agent]` の `interval` はサンプルの間隔に影響しません。
Prometheus に入るサンプルの間隔は、Cube J1 の `poll_interval` で決まります。

## Grafana Cloud の接続情報と Telegraf の起動

remote write の宛先は、Grafana Cloud のポータルで Prometheus の「Details」を開くと確認できます。
URL は `https://prometheus-prod-XX-prod-<リージョン>.grafana.net/api/prom/push` の形で、ユーザー名は数字のインスタンス ID です。
Telegraf はこれを HTTP の Basic 認証として送り、パスワードにはメトリクスの書き込み権限（`metrics:write`）を付けたアクセストークンを使います。
画面上の項目名やトークンの発行手順は変わることがあるので、Grafana Cloud の接続画面に表示される値を確かめてください。

これらは Telegraf の設定ファイルに直接書かず、`telegraf.env` に書いて環境変数として渡しています。
Telegraf は設定ファイル中の `${VAR}` を環境変数で置き換えます。

```
MOSQ_PASSWORD=（Mosquitto の telegraf ユーザーのパスワード）
GRAFANA_PROM_URL=https://prometheus-prod-XX-prod-<リージョン>.grafana.net/api/prom/push
GRAFANA_PROM_USER=（インスタンス ID）
GRAFANA_PROM_KEY=（アクセストークン）
```

このファイルはリポジトリに入れず、権限を `600` にしておきます。
Telegraf のサービスは、Mosquitto と同じ `compose.yaml` の `services` に追加します。
手元では Telegraf 1.32 で動かしています。

```yaml
  telegraf:
    image: telegraf:1.32
    restart: unless-stopped
    volumes:
      - ./telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
    env_file:
      - ./telegraf/telegraf.env
```

起動したら、`docker compose logs telegraf` に設定エラーや remote write の HTTP エラーが出ていないかを確かめます。
認証情報を間違えていると、ここに `401` や `403` が出ます。

## Grafana で見る

値が届いたら、Grafana Cloud の Explore で Prometheus のデータソースを開き、次の PromQL を実行するとグラフを確認できます。
冒頭の画像は、このクエリをそのまま時系列パネルにしたものです。

```
cube_j1_smart_meter_power_watts{device_id="cubej1"}
```

ダッシュボードには、ほかに次のようなパネルを並べています。

- 直近 1 時間の平均電力: `avg_over_time(cube_j1_smart_meter_power_watts[1h])`
- 直近 24 時間の最大電力: `max_over_time(cube_j1_smart_meter_power_watts[24h])`
- 相ごとの電流: `cube_j1_smart_meter_current_r_amperes` と `cube_j1_smart_meter_current_t_amperes`

![手元の Grafana のダッシュボード。上段に直近 6 時間の瞬時電力のグラフ、下段に現在の電力（933 W）、直近 1 時間の平均（835 W）、直近 24 時間の最大（2.67 kW）を並べている](dashboard.png)

![R 相の電流の 24 時間の推移。普段は 5〜10 A で、朝と夕方に 20 A を超える。単相 2 線式で T 相がないため、T 相の線はない](currents.png)

我が家は単相 2 線式なので、電流のパネルには R 相の線だけが出ています。

我が家では夜間に 600〜800 W で推移し、朝と夕方に電子レンジやドライヤーを使うと 2 kW を超えます。
冒頭の画像の 24 時間では、平均が約 770 W でした。

## Grafana Cloud Free の保持期間と長期保存

しばらく運用してから、私が使っている Grafana Cloud の Free プランでは、14 日より前のメトリクスを参照できないことに気付きました（2026 年 9 月に確認）。
長期の履歴を溜めているつもりでしたが、残っていたのは直近 14 日分だけでした。
保持期間は変わることがあるので、最新の値は Grafana Cloud の料金ページで確かめてください。

長期の推移も見たかったので、同じサーバーで動かしている Prometheus にも二重に書き込むことにしました。
Telegraf に、ローカルの Prometheus 用の `outputs.http` を追加しています。
手元の Prometheus は Grafana と一緒に別の Compose プロジェクトで動かしており、Telegraf と共通の外部ネットワーク（`networks` に `external: true` を付けたネットワーク）に参加させています。
そのため、Telegraf からサービス名の `prometheus` で接続できます。

```toml
[[outputs.http]]
  url = "http://prometheus:9090/api/v1/write"
  method = "POST"
  data_format = "prometheusremotewrite"

  [outputs.http.headers]
    Content-Type = "application/x-protobuf"
    Content-Encoding = "snappy"
    X-Prometheus-Remote-Write-Version = "0.1.0"
```

受け取る Prometheus 側は、`--web.enable-remote-write-receiver` を付けて起動しないと書き込みを受け付けません。
データの保持上限は、期間を指定する `--storage.tsdb.retention.time` と、容量を指定する `--storage.tsdb.retention.size` で設定できます。
両方を指定すると、先に達したほうの条件で古いデータが消えます。
私の環境ではディスクの空きに合わせて、2 年（`2y`）と 120 GB（`120GB`）にしました。
ローカルの Grafana にも、同じ PromQL のパネルを並べたダッシュボードを作っています。

## グラフが途切れる問題

この構成で動かし始めた直後は、グラフが数十分単位で途切れていました。
Mosquitto のログを見ると、Cube J1 の接続が約 12 分おきに `exceeded timeout` で切られていました。
ブリッジは 1 本のループで、スマートメーターへの問い合わせ、応答待ち、MQTT の ping 送信、次の問い合わせまでの待機を順に回しています。
MQTT の keepalive は 60 秒で、ブローカーはその 1.5 倍の 90 秒間、クライアントから MQTT の制御パケットを受信できないと接続を切ります。
メーターの応答が返らず値を送れない周回が続いたり、Wi-SUN の再接続が長引いたりすると、この間に ping も測定値も送られずに切断されます。
この処理の停止から MQTT の再接続までの間は新しい測定値がブローカーに届かず、グラフが欠けていました。

その後、手元でブリッジを改造して途切れをかなり減らしました。
原因の調べ方と対策は、[続編](/articles/cube-j1-grafana-gaps)に書きました。
