【HomeAssistant】Nature Remo E Liteから電力情報を取得する

## はじめに

これまで利用していたNature Remo用のカスタムインテグレーションが、HomeAssistantのバージョンアップに伴い不安定になってしまったため、この機会に自作でのカスタムインテグレーション開発に挑戦することにしました。

この記事では、Nature Remo E Liteを用いて、**スマートメーターの電力情報（買電・売電・瞬時電力）をセンサーとして取得し、HomeAssistantで表示できるようにした内容**をメモとしてまとめています。


## 環境

- Ubuntu 24.04
- HomeAssistant Core（開発環境）
- Nature Remo E Lite（スマートメーター接続済み）
- Python 3.13（venvによる仮想環境）
- カスタムコンポーネント構成（`custom_components/nature_remo/`）


## 使用したAPI：`GET /1/appliances`

Nature Remo Cloud APIの `/1/appliances` から、以下の電力情報が取得できます。
[電力データ算出方法 | Nature Developer Page](https://developer.nature.global/docs/how-to-calculate-energy-data-from-smart-meter-values/)


| name | epc | 内容 |
|------|-----|------|
| coefficient | 211 | 倍率 |
| cumulative_electric_energy_unit | 225 | 単位補正（指数） |
| normal_direction_cumulative_electric_energy | 224 | 買電（積算kWh） |
| reverse_direction_cumulative_electric_energy | 227 | 売電（積算kWh） |
| measured_instantaneous | 231 | 瞬時電力（W） |


## ディレクトリ構成（必要なファイルのみ抜粋）

```text
custom_components/nature_remo/
├── __init__.py
├── api.py
├── sensor.py
└── coordinator.py
```

## センサー処理の流れ
※今回の作業で追加した部分のみ抽出しています。

```python:__init__.py
PLATFORMS = ["climate", "light", "sensor"] 

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """YAML設定をサポートするためのセットアップ処理."""
    hass.data.setdefault(DOMAIN, {})

    # YAMLの設定を保存
    if DOMAIN in config:
        _LOGGER.info("load config!!!")
        hass.data[DOMAIN] = config[DOMAIN]

    # APIクラスのインスタンスを作成して保存
    api = await create_api_client(token)
    hass.data[DOMAIN]["api"] = api

    # コーディネーターをHomeAssistantのデータ領域に保存
    coordinator = NatureRemoCoordinator(hass, api)
    hass.data[DOMAIN]["coordinator"] = coordinator

    # 初回更新
    await coordinator.async_refresh()

    for platform in PLATFORMS:
        hass.async_create_task(
            async_load_platform(hass, platform, DOMAIN, {}, hass.data[DOMAIN])
        )

    return True
```

```python:api.py
def parse_smart_meter_properties(properties: list[dict]) -> dict:
    """
    Nature Remo E Liteのechonetlite_propertiesを元に
    買電・売電・瞬時電力を変換して返却する
    """
    # 値が欠損している場合にも備え、各種初期値を定義
    coefficient = 1
    unit_power = 0
    buy_power_raw = 0
    sold_power_raw = 0
    instant_power = 0

    for prop in properties:
        epc = int(prop.get("epc"))
        val_str = prop.get("val", "0")
        try:
            val = int(val_str)
        except ValueError:
            val = 0

        # epc（Echonet Lite Property）に応じて各値を格納
        if epc == 211:  # 係数
            coefficient = val
        elif epc == 215:  # 有効桁数（今回は使用せず）
            continue
        elif epc == 224:  # 積算買電量
            buy_power_raw = val
        elif epc == 225:  # 単位（指数）
            unit_power = val
        elif epc == 227:  # 積算売電量
            sold_power_raw = val
        elif epc == 231:  # 瞬時電力（W）
            instant_power = val

    # 単位変換テーブルを定義
    unit_table = {
        0x00: 1,
        0x01: 0.1,
        0x02: 0.01,
        0x03: 0.001,
        0x04: 0.0001,
        0x0A: 10,
        0x0B: 100,
        0x0C: 1000,
        0x0D: 10000
    }

    # 取得した値を変換
    factor = coefficient * unit_table.get(unit_power, 1)
    buy_power = buy_power_raw * factor
    sold_power = sold_power_raw * factor

    # センサー用データとして返却
    return {
        "buy_power": buy_power,
        "sold_power": sold_power,
        "instant_power": instant_power
    }
```

```python:sensor.py
from homeassistant.components.sensor import SensorEntity
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .coordinator import NatureRemoCoordinator

DOMAIN = "nature_remo"

SENSOR_TYPES = {
    # センサー種別ごとの属性定義（表示名、単位、分類など）
    "buy_power": {
        "name": "Buy Power",
        "unit": "kWh",
        "device_class": "energy",    # energy device_class によりエネルギー統計に対応
        "state_class": "total_increasing",  # 積算データ扱いにすることでグラフ化などが可能に
    },
    "sold_power": {
        "name": "Sold Power",
        "unit": "kWh",
        "device_class": "energy",
        "state_class": "total_increasing",
    },
    "current_power": {
        "name": "Current Power",
        "unit": "W",
        "device_class": "power",
        "state_class": "measurement",
    },
}

async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    coordinator = hass.data[DOMAIN]["coordinator"]
    entities = []

    # スマートメーターごとに、センサー（buy/sold/current）を生成
    for appliance_id, data in coordinator.smart_meters.items():
        for key, desc in SENSOR_TYPES.items():
            entities.append(
                NatureRemoSensor(
                    coordinator,
                    appliance_id,
                    data["name"],  # Remoの表示名
                    key,           # センサーの種類（buy/sold/current）
                    desc           # 属性（unit, device_classなど）
                )
            )
    # エンティティとしてHome Assistantに追加
    async_add_entities(entities)

class NatureRemoSensor(CoordinatorEntity, SensorEntity):
    def __init__(self, coordinator, appliance_id, name, key, description):
        super().__init__(coordinator)
        
        # センサーの基本情報をセット（Home Assistantが認識できる形式）
        self._appliance_id = appliance_id
        self._attr_name = f"{name} {description['name']}"
        self._attr_unique_id = f"{appliance_id}_{key}"  # 複数センサーが被らないように一意に設定
        self._attr_native_unit_of_measurement = description["unit"]
        self._attr_device_class = description["device_class"]
        self._attr_state_class = description["state_class"]
        self._key = key

    @property
    def native_value(self):
        # 現在の値をcoordinatorから取得して返す
        return self.coordinator.smart_meters[self._appliance_id][self._key]
```

```python:coordinator.py
    async def _async_update_data(self):
        """APIを1回だけ呼び、appliancesの情報を取得."""
        try:
            self.smart_meters = {}
            appliances = await self.api.get_appliances()

            # 取得したappliancesからスマートメータの情報を取得
            for appliance in appliances:
                appliance_type = appliance.get("type")
                appliance_id = appliance.get("id")
                nickname = appliance.get("nickname", "Smart Meter")

                if appliance_type == "EL_SMART_METER":
                    properties = appliance.get("smart_meter", {}).get("echonetlite_properties", [])
                    parsed = parse_smart_meter_properties(properties)

                    self.smart_meters[appliance_id] = {
                        "name": nickname,
                        "buy_power": parsed["buy_power"],
                        "sold_power": parsed["sold_power"],
                        "current_power": parsed["instant_power"],
                    }

            return {ac["id"]: ac for ac in appliances}
        except ClientError as err:
            raise UpdateFailed(f"通信エラー: {err}") from err  # ネットワーク系のエラー
```




## センサー表示例

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4016885/66756118-73b6-4344-b924-54076951ade4.png">


|エンティティ|値|単位|属性|
|----------|--|---|---|
|sensor.sumatometa_buy_power|2647.52|kWh|total_increasing, energy|
|sensor.sumatometa_sold_power|736.66|kWh|total_increasing, energy|
|sensor.sumatometa_current_power|529|W|measurement, power|

## 今後の展望

- エネルギーダッシュボードへの対応・視覚化
- オートメーション連携（ピーク時通知、機器制御など）

## まとめ

今回は、Nature Remo E Liteの電力情報を取得し、HomeAssistant上でセンサー化することで、エネルギーの見える化を実現しました。

同じようにスマートメーター連携を試してみたい方の参考になればと思います。

## 関連リンク

https://note.com/nanomana/n/ndf64f5145ace?sub_rt=share_pw