♨️

ノーリツ製の給湯器リモコンをPCからNode.jsで操作する(ECHONET Lite)

2022/12/30に公開

こんにちは。
久しぶりに帰省したところ、実家の給湯器のリモコンがIoT対応の製品 RC-G001MW-2 (ノーリツ)になっていました。これはハックしなければならないと思っていじってみたところ、HEMS向けの機能を利用することでわりと容易に制御できたので記事にしてみた次第です。


https://www.noritz.co.jp/product/kyutou_bath/remocon/remocon_gw.html
(画像:ノーリツ公式サイトより)

成果物のコマンドラインツールはnpmに公開しているので、同じモデルや同一プロトコルに対応した給湯リモコンをお持ちの方は実験できるかもしれません(詳細は記事末尾参照)。

https://www.youtube.com/watch?v=NnpHsreI_QE

HEMS

HEMS (Home Energy Management System)とは宅内のエネルギー使用量を管理・可視化する装置です。昨今のIoT対応家電はHEMSとの連携が前提になっているものも増えているようです。

https://echonet.jp/about/hems/

ECHONET Lite

HEMSのために国内で策定された通信規格がECHONET Lite(エコーネットライト)です。2012年頃にはすでに策定されていたようで、資料やノウハウなども十分蓄積されています。

今回、実家に設置されていたRC-G001MW-2はこれに対応しており、設定画面からオンオフが切り替えできます。

仕様書などはECHONETのWebサイトで公開されています。各種言語でのライブラリも充実しているので、とりあえず触れるというレベルにはすぐ到達できるはずです。

Node.jsで触る

今回はNodeでの実装 echonet-lite を使用します。

$ npm init
$ npm install echonet-lite
$ npm install date-utils

給湯器側の設定

(RC-G001MW-2の場合)

無線LANに接続し、「エコーネットライト」をオンにします。
また、メニュー音・その他無線LAN設定情報リモコンアドレスからIPアドレスを確認します。

情報の取得と仕様の確認

チュートリアルを参考に、必要な情報を検索してみます。

ECHONET Liteで通信するには、PC自身もECHONET Liteオブジェクトとして振る舞う必要があります。ここでは05ff01(コントロールパネルオブジェクト)として振る舞い、EL.search()でネットワーク内の全機器を検索します。

注意点として、echonet-liteライブラリの仕様として、他のECHONET Liteを機器からブロードキャストされるメッセージのハンドリングは EL.initialize のコールバック関数として実装する必要があります。

search.js
const EL = require('echonet-lite');
EL.initialize(['05ff01'], (rinfo, els, err) => {
    if (err) {
        console.dir(err);
    } else {
        console.dir(rinfo);
        console.log(els)
        console.log('--------');
    }
});

EL.search();

以下のようにオブジェクトでレスポンスが得られます。(一部のみ抜粋)

{ address: '192.XXX.XXX.XXX', family: 'IPv4', port: 3610, size: 35 }
{
  EHD: '1081',
  TID: '0002',
  SEOJ: '001101',
  DEOJ: '0ef001',
  EDATA: '72039d04038081889e0201819f0b0a80818283888a9d9e9fe0',
  ESV: '72',
  OPC: '03',
  DETAIL: '9d04038081889e0201819f0b0a80818283888a9d9e9fe0',
  DETAILs: { '9d': '03808188', '9e': '0181', '9f': '0a80818283888a9d9e9fe0' }
}
----
{ address: '192.XXX.XXX.XXX', family: 'IPv4', port: 3610, size: 37 }
{
  EHD: '1081',
  TID: '0003',
  SEOJ: '000701',
  DEOJ: '0ef001',
  EDATA: '72039d0504808188b19e0201819f0c0b80818283888a9d9e9fb0b1',
  ESV: '72',
  OPC: '03',
  DETAIL: '9d0504808188b19e0201819f0c0b80818283888a9d9e9fb0b1',
  DETAILs: {
    '9d': '04808188b1',
    '9e': '0181',
    '9f': '0b80818283888a9d9e9fb0b1'
  }
}
----
...

宅内に他の ECHONET Lite 機器がない場合、 rinfo として得られる要素に表示されているものが給湯器のIPアドレスと一致しているはずです。
els として得られるものが、ブロードキャストされている電文の本文(をライブラリがオブジェクト化したもの)にあたります。ここではSEOJ(Sender ECHONET Lite Object)に注目しましょう。

1つの給湯リモコンは複数のオブジェクトで構成されています。たとえば000701は人感センサ、001101は湿度センサとなっています。どういったオブジェクトが存在しており、どういったコードを持っているかを検索から列挙すると大変なので、ECHONETコンソーシアムのWebサイトから製品を検索してみましょう。

https://echonet.jp/product/

個別ページから「搭載オブジェクト申告書」のPDFをダウンロードして確認します。
https://echonet.jp/introduce/gz-000646/

以下のような記載より、本機器に搭載されているオブジェクトが確認できます。今回操作したい「瞬間式給湯器」のコードは027201であることがわかりました。

個別ページから「搭載プロパティ申告書(0x027201)」もダウンロードしましょう。どのプロパティに対応しているか、どのプロパティがGet/Setに対応しているかを確認できます。

これらプロパティからどういった情報がGet/SetできるかはECHONET Liteの仕様レベルで「ECHONET 機器オブジェクト詳細規定」として策定・公開されています。ただ、これは600ページを超えるPDFで、必要な情報を参照するのが若干大変なので、Sony CSLが公開しているリポジトリに掲載されているCSVファイルを参考にしても良いと思います。

オンオフ制御

それでは、実際にECHONET Liteプロトコルによって信号を送信してみます。
雑に以下の2つのファイルを用意しましょう。内容はほぼ同一で、オン・オフによって最後の引数が異なるのみです。

gas_on.js
const EL = require('echonet-lite');
EL.sendOPC1('給湯器のIPアドレス',[0x05,0xff,0x01],[0x02,0x72,0x01],EL.SETC,0x80,0x30);
gas_off.js
const EL = require('echonet-lite');
EL.sendOPC1('給湯器のIPアドレス',[0x05,0xff,0x01],[0x02,0x72,0x01],EL.SETC,0x80,0x31);

SEOJとして、[0x05,0xff,0x01](コントローラオブジェクトのID)を設定します。
DEOJ (Destination ECHONET Object) として[0x02,0x72,0x01](瞬間給湯器オブジェクトのID)を設定します。

ESV (ECHONET Lite サービスコード) として、応答ありのプロパティ値書き込み EL.SETC を設定します。(今回は応答値をハンドリングするわけではないので、応答なしのプロパティ書き込みEL.SETIでも差し支えありません。)

EPCコードに0x80(動作状態)を設定し、0x30(ON)と0x31(OFF)を設定します。

これを実行してあげると、オン・オフが確認できるはずです。

$ node gas_on.js
$ node gas_off.js

実際のリモコンは以下のような挙動をします。
ECHONET Lite経由で実施された操作は電源オンオフであっても設定変更として扱われるようで、そのアナウンスが鳴るのはやや紛らわしいですね。ちなみに、公式で用意されているスマホ向けアプリ「わかすアプリ」からの操作でも同様のアナウンスが鳴ります。

https://twitter.com/geo_vitya/status/1607768968722526208

給湯温度の取得

続いて、設定温度の取得を行います。取得は設定に比べると若干厄介です。
なぜなら、EL.GETを叩いても直接戻り値が得られるわけではなく、対象の機器がブロードキャストするメッセージを監視して別途拾う必要があるからです。

そのため、先程のEL.initializeのコールバックとしてメッセージハンドリングの関数を設定しておき、GETを送信する関数をawaitで呼び出してやるのが良さそうです(こちらの実装例を参考にさせていただきました)。

fetch.js
#! /usr/bin/env node
const EL = require('echonet-lite');

(async () => {
    EL.initialize(['05ff01'], messageHandler);
    await sleep(500); //機器側の応答速度を考慮し、500ms待機する
    await ofuroGet(0xD1); //0xD1は給湯温度
    // https://github.com/SonyCSL/ECHONETLite-ObjectDatabase/blob/master/data/csv/ja/0x0272.csv
    process.exit();
})();

function messageHandler(rinfo, els, err) {
    if (err) {
        console.dir(err);
        return;
    }
    if (els['SEOJ'] == '027201') {
        console.log(els);
    }
}

async function ofuroGet(item) {
    EL.sendOPC1('192.XXX.XXX.XXX', [0x05, 0xff, 0x01], [0x02, 0x72, 0x01], EL.GET, item);
    await sleep(50);
}

function sleep(interval) {
    return new Promise((resolve) => {
        setTimeout(() => { resolve() }, interval);
    });
} 

実行すると、以下のようなレスポンスが得られます。echonet-liteライブラリにより、DETAILsの中に取り扱いやすいオブジェクトが格納された状態で得られます。この値は16進数なので 0x28= 40度 ということになります。

{
  EHD: '1081',
  TID: '0002',
  SEOJ: '027201',
  DEOJ: '05ff01',
  EDATA: '7201d10128',
  ESV: '72',
  OPC: '01',
  DETAIL: 'd10128',
  DETAILs: { d1: '28' }
}

コマンドラインツールの作成

これらの処理をまとめたCLIツールを作成します。NodeでCLIツールを書くならcacが便利です。

https://www.npmjs.com/package/cac

ofuro

特にCLIツールの方はこれといって説明することはなく、npmに ofuro という名前で公開しています。同一の機種をお持ちの方はこちらを使うと実験できると思います。使い方と注意事項はREADMEに記載しているので確認してください。

https://www.npmjs.com/package/ofuro

$ npm install ofuro -g
$ ofuro status
♨ ofuro ♨
項目名                 値
---------------------- ------
システム               ON
異常発生               平常
給湯器燃焼状態         停止中
給湯温度               40℃
給湯器燃焼状態(風呂) 停止中
給湯温度(風呂)       41℃
ふろ自動モード         ON
追いだきモード         OFF
足し湯モード           OFF
ぬるめモード           OFF
設定湯量               4/11

Discussion