
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
echonet-lite
Advanced tools
このモジュールはECHONET Liteプロトコルをサポートします. ECHONET Liteプロトコルはスマートハウス機器の通信プロトコルです. Node.js上で動作し、IPv4/IPv6デュアルスタックやマルチキャスト検索をサポートします。
This module provides ECHONET Lite protocol for Node.js. The ECHONET Lite protocol is a communication protocol for smart home devices. It supports IPv4/IPv6 dual stack and multicast discovery.
注意:本モジュールによるECHONET Lite通信規格上の保証はなく、SDKとしてもECHONET Liteの認証を受けておりません。 また、製品化の場合には各社・各自がECHONET Lite認証を取得する必要があります。
下記コマンドでモジュールをインストールできます.
You can install the module as following command.
npm i echonet-lite
デモプログラムはこんな感じです。動作させるためにはECHONET Lite対応デバイスが必要です。もしお持ちでない場合には**MoekadenRoom**というシミュレータがおすすめです。
Here is a demonstration script. For test exectuion, some devices with ECHONET Lite is required. If you do not have any device, we recommend the MoekadenRoom as a simulator.
// モジュールの機能をELとして使う
// import functions as EL object
var EL = require('echonet-lite');
// 自分自身のオブジェクトを決める
// set EOJ for this script
// initializeで設定される,必ず何か設定しないといけない,今回はコントローラ
// this EOJ list is required. '05ff01' is a controller.
var objList = ['05ff01'];
////////////////////////////////////////////////////////////////////////////
// 初期化するとともに,受信動作をコールバックで登録する
// initialize and setting callback. the callback is called by reseived packet.
var elsocket = EL.initialize( objList, function( rinfo, els, err ) {
if( err ){
console.dir(err);
}else{
console.log('==============================');
console.log('Get ECHONET Lite data');
console.log('rinfo is ');
console.dir(rinfo);
// elsはELDATA構造になっているので使いやすいかも
// els is ELDATA stracture.
console.log('----');
console.log('els is ');
console.dir(els);
// ELDATAをArrayにする事で使いやすい人もいるかも
// convert ELDATA into byte array.
console.log('----');
console.log( 'ECHONET Lite data array is ' );
console.log( EL.ELDATA2Array( els ) );
// 受信データをもとに,実は内部的にfacilitiesの中で管理している
// this module manages facilities by receved packets.
console.log('----');
console.log( 'Found facilities are ' );
console.dir( EL.facilities );
}
});
// NetworkのELをすべてsearchしてみよう.
// search ECHONET nodes in local network
EL.search();
こんな感じで作ってみたらどうでしょうか. あとはairconObjのプロパティをグローバル変数として,別の関数から書き換えてもいいですよね. これでGetに対応できるようになります.
This is a demo program for developping air conditioner object.
//////////////////////////////////////////////////////////////////////
// Copyright (C) Hiroshi SUGIMURA 2022.09.22 - above.
//////////////////////////////////////////////////////////////////////
'use strict'
//////////////////////////////////////////////////////////////////////
// ECHONET Lite
let EL = require('echonet-lite');
// エアコンと照明があるとする
let objList = ['013001','029001'];
// 自分のエアコンのデータ,今回はこのデータをグローバル的に使用する方法で紹介する.
let dev_details = {
'013001': {
// super
"80": [0x30], // 動作状態
"81": [0xff], // 設置場所
"82": [0x00, 0x00, 0x66, 0x00], // EL version, 1.1
'83': [0xfe, 0x00, 0x00, 0x77, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], // identifier, initialize時に、renewNICList()できちんとセットするとよい, get
"88": [0x42], // 異常状態
"8a": [0x00, 0x00, 0x77], // maker code
"9d": [0x04, 0x80, 0x8f, 0xa0, 0xb0], // inf map, 1 Byte目は個数
"9e": [0x04, 0x80, 0x8f, 0xa0, 0xb0], // set map, 1 Byte目は個数
"9f": [0x0d, 0x80, 0x81, 0x82, 0x88, 0x8a, 0x8f, 0x9d, 0x9e, 0x9f, 0xa0, 0xb0, 0xb3, 0xbb], // get map, 1 Byte目は個数
// child
"8f": [0x41], // 節電動作設定
"a0": [0x31], // 風量設定
"b0": [0x41], // 運転モード設定
"b3": [0x19], // 温度設定値
"bb": [0x1a] // 室内温度計測値
},
'029001': { // lighting
// super
'80': [0x31], // 動作状態, set?, get, inf
'81': [0x0f], // 設置場所, set, get, inf
'82': [0x00, 0x00, 0x50, 0x01], // spec version, P. rev1, get
'83': [0xfe, 0x00, 0x00, 0x77, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03], // identifier, initialize時に、renewNICList()できちんとセットするとよい, get
'88': [0x42], // 異常状態, 0x42 = 異常無, get
'8a': [0x00, 0x00, 0x77], // maker code, kait, get
'9d': [0x04, 0x80, 0x81], // inf map, 1 Byte目は個数, get
'9e': [0x04, 0x80, 0x81, 0xb6], // set map, 1 Byte目は個数, get
'9f': [0x0a, 0x80, 0x81, 0x82, 0x83, 0x88, 0x8a, 0x9d, 0x9e, 0x9f, 0xb6], // get map, 1 Byte目は個数, get
// uniq
'b6': [0x42] // 点灯モード設定, set, get
}
};
async function setup() {
// ノードプロファイルに関しては内部処理するので,ユーザーはエアコンに関する受信処理だけを記述する.
let elsocket = await EL.initialize(objList, function (rinfo, els, e) {
if (e) {
console.error(e);
return;
}
if( els.DEOJ.substr(0,4) == '0ef0' ) {return;} // Node profileに関しては何もしない
// ESVで振り分け,主に0x60系列に対応すればいい
switch (els.ESV) {
////////////////////////////////////////////////////////////////////////////////////
// 0x6x
case EL.SETI: // "60
case EL.SETC: // "61",返信必要あり
EL.replySetDetail( rinfo, els, dev_details );
break;
case EL.GET: // 0x62,Get
EL.replyGetDetail( rinfo, els, dev_details );
break;
case EL.INF_REQ: // 0x63
break;
case EL.SETGET: // "6e"
break;
default:
break;
}
}, 0, {ignoreMe: true, autoGetProperties: false, debugMode: false});
dev_details['013001']['83'][7] = dev_details['029001']['83'][7] = EL.Node_details["83"][7];
dev_details['013001']['83'][8] = dev_details['029001']['83'][8] = EL.Node_details["83"][8];
dev_details['013001']['83'][9] = dev_details['029001']['83'][9] = EL.Node_details["83"][9];
dev_details['013001']['83'][10] = dev_details['029001']['83'][10] = EL.Node_details["83"][10];
dev_details['013001']['83'][11] = dev_details['029001']['83'][11] = EL.Node_details["83"][11];
dev_details['013001']['83'][12] = dev_details['029001']['83'][12] = EL.Node_details["83"][12];
//////////////////////////////////////////////////////////////////////
// 全て立ち上がったのでINFでエアコンONの宣言
EL.sendOPC1('224.0.23.0', [0x01, 0x30, 0x01], [0x0e, 0xf0, 0x01], 0x73, 0x80, 0x30);
// 全て立ち上がったのでINFで照明ONの宣言
EL.sendOPC1('224.0.23.0', [0x02, 0x90, 0x01], [0x0e, 0xf0, 0x01], 0x73, 0x80, 0x30);
}
setup();
//////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////
let EL = {
EL_port: 3610,
EL_Multi: '224.0.23.0',
EL_obj: null,
facilities: {} // ネットワーク内の機器情報リスト; device and property list in the LAN
// Ex.
// { '192.168.0.3': { '05ff01': { d6: '' } },
// { '192.168.0.4': { '05ff01': { '80': '30', '82': '30' } } }
};
ELデータはこのモジュールで定義した構造で,下記のようになっています. ELDATA is ECHONET Lite data stracture, which conteints
ELDATA {
EHD : str.substr( 0, 4 ),
TID : str.substr( 4, 4 ),
SEOJ : str.substr( 8, 6 ),
DEOJ : str.substr( 14, 6 ),
EDATA: str.substr( 20 ), // EDATA is followings
ESV : str.substr( 20, 2 ),
OPC : str.substr( 22, 2 ),
DETAIL: str.substr( 24 ),
DETAILs: EL.parseDetail( str.substr( 22, 2 ), str.substr( 24 ) )
}
こんな感じ
EL.facilities =
{ '192.168.2.103':
{ '05ff01': { '80': '', d6: '' },
'0ef001': { '80': '30', d6: '0100' } },
'192.168.2.104': { '0ef001': { d6: '0105ff01' }, '05ff01': { '80': '30' } },
'192.168.2.115': { '0ef001': { '80': '30', d6: '01013501' } } }
こんな感じ
EL.identificationNumbers
[
{
id: 'fe0000776e5b0d002b5b0ef00100000000',
ip: '192.168.2.11',
OBJ: '0ef001'
},
{
id: 'fe0000776a6ee920fd7002870100000000',
ip: '192.168.2.11',
OBJ: '028701'
}
]
下記はエアコンと照明の詳細オブジェクトを持っている場合である。
// 自分のエアコンのデータ,今回はこのデータをグローバル的に使用する方法で紹介する.
let dev_details = {
'013001': {
// super
"80": [0x30], // 動作状態
"81": [0xff], // 設置場所
"82": [0x00, 0x00, 0x66, 0x00], // EL version, 1.1
"88": [0x42], // 異常状態
"8a": [0x00, 0x00, 0x77], // maker code
"9d": [0x04, 0x80, 0x8f, 0xa0, 0xb0], // inf map, 1 Byte目は個数
"9e": [0x04, 0x80, 0x8f, 0xa0, 0xb0], // set map, 1 Byte目は個数
"9f": [0x0d, 0x80, 0x81, 0x82, 0x88, 0x8a, 0x8f, 0x9d, 0x9e, 0x9f, 0xa0, 0xb0, 0xb3, 0xbb], // get map, 1 Byte目は個数
// child
"8f": [0x41], // 節電動作設定
"a0": [0x31], // 風量設定
"b0": [0x41], // 運転モード設定
"b3": [0x19], // 温度設定値
"bb": [0x1a] // 室内温度計測値
},
'029001': { // lighting
// super
'80': [0x31], // 動作状態, set?, get, inf
'81': [0x0f], // 設置場所, set, get, inf
'82': [0x00, 0x00, 0x50, 0x01], // spec version, P. rev1, get
'83': [0xfe, 0x00, 0x00, 0x77, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03], // identifier, initialize時に、renewNICList()できちんとセットする, get
'88': [0x42], // 異常状態, 0x42 = 異常無, get
'8a': [0x00, 0x00, 0x77], // maker code, kait, get
'9d': [0x04, 0x80, 0x81], // inf map, 1 Byte目は個数, get
'9e': [0x04, 0x80, 0x81, 0xb6], // set map, 1 Byte目は個数, get
'9f': [0x0a, 0x80, 0x81, 0x82, 0x83, 0x88, 0x8a, 0x9d, 0x9e, 0x9f, 0xb6], // get map, 1 Byte目は個数, get
// uniq
'b6': [0x42] // 点灯モード設定, set, get
}
};

EL.initialize = function ( objList, userfunc, ipVer = 4, Options = {v4: '', v6: '', ignoreMe: true, autoGetProperties: true, debugMode: false} )
objList is ECHONET Lite object code.
for example, ['05ff01'] is a controller.
userfunc is the your callback function. userfunc is described as following.
function( rinfo, els, err ) {
console.log('==============================');
if( err ) {
console.dir(err);
}else{
// ToDo
}
}
ipVer is optional
ipVer = 0, IPv4 and IPv6
ipVer = 4, IPv4 only
ipVer = 6, IPv6 only
Options is optional
v4 is specified for using IPAddress, default '' is auto
v6 is specified for using NIC name, default '' is auto
ignoreMe is specified to ignore self IP address, default true
autoGetProperties is automatic get for properties, default true (trial)
autoGetDelay is period of the auto get EDTs, default 3000ms
debugMode shows innser log, default false
More examples
let objList = ['05ff01'];
let elsocket = EL.initialize( objList, function( rinfo, els, err ) {
console.log('==============================');
if( err ) {
console.dir(err);
}else{
console.log('----');
console.log('Get ECHONET Lite data');
console.log('rinfo is '); console.dir(rinfo);
console.log('els is '); console.dir(els);
}
}, 0, { 'v4': '', 'v6': '', ignoreMe:true, autoGetProperties: true, autoGetDelay: 1000, debugMode: false}); // Recommendation for a controller
// }, 0, { 'v4': '', 'v6': '', ignoreMe:true, autoGetProperties: false, debugMode: false}); // Recommendation for a device
EL.release = function()
EL.renewNICList = function()
EL.setObserveFacilities = function ( interval, onChanged )
EL.clearObserveFacilities = function ();

EL.eldataShow = function( eldata )
EL.stringShow = function( str )
EL.bytesShow = function( bytes )

| from | to | function |
|---|---|---|
| String | ELDATA(EDT) | parseDetail(opc,str) |
| Bytes(=Integer[]) | ELDATA | parseBytes(bytes) |
| String | ELDATA | parseString(str) |
| String | String (like EL) | getSeparatedString_String(str) |
| ELDATA | String (like EL) | getSeparatedString_ELDATA(eldata) |
| ELDATA | Bytes(=Integer[]) | ELDATA2Array(eldata) |
EL.parseDetail = function( opc, str )
EL.parseBytes = function( bytes )
EL.parseString = function( str )
EL.getSeparatedString_String = function( str )
EL.getSeparatedString_ELDATA = function( eldata )
EL.ELDATA2Array = function( eldata )
| from | to | function |
|---|---|---|
| Byte | 16進表現String | toHexString(byte) |
| 16進表現String | Integer[] | toHexArray(str) |
EL.toHexString = function( byte )
EL.toHexArray = function( string )

APIは送信の成功失敗に関わらず,TIDをreturnすることにしました。 送信TIDはEL.tid[]で管理しています。 sendOPC1とEL.sendEPCsはEL.tidを自動的に+1します。
ipの指定方法は、
EL.sendBase = function( ip, buffer )
EL.sendArray = function( ip, array )
EL.sendOPC1 = function( ip, seoj, deoj, esv, epc, edt)
ex.
EL.sendOPC1( '192.168.2.150', [0x05,0xff,0x01], [0x01,0x35,0x01], 0x61, 0x80, [0x31]);
EL.sendOPC1( '192.168.2.150', [0x05,0xff,0x01], [0x01,0x35,0x01], 0x61, 0x80, 0x31);
EL.sendOPC1( '192.168.2.150', "05ff01", "013501", "61", "80", "31");
EL.sendOPC1( '192.168.2.150', "05ff01", "013501", EL.SETC, "80", "31");
プロジェクト内の単体テストは unitTest ディレクトリ配下の Jest で実行できるよ。ルートからでも実行できるようにスクリプトを用意してある。
ローカルで直接実行する場合:
cd unitTest
npm install
npm test
ルートから実行する場合:
npm test
EL.sendString = function( ip, string )
EL.sendDetails = function (ip, seoj, deoj, esv, DETAILs)
EL.sendELDATA = function (ip, eldata)
EL.search = function()
EL.renewFacilities = function( ip, obj, opc, detail )
EL.setObserveFacilities = function( interval, onChanged );
ex.
EL.setObserveFacilities( 1000, function() { // 1000 ms
console.log('EL.facilities are changed.');
});
EL.complementFacilities = function ();
ex.
const cron = require('node-cron');
cron.schedule( '*/3 * * * *', () => {
EL.complementFacilities();
})

EL.replyOPC1 = function (ip, tid, seoj, deoj, esv, epc, edt)
EL.replyGetDetail = async function(rinfo, els, dev_details)
EL.replySetDetail = async function(rinfo, els, dev_details)
let dev_details = {
'013001': {
// super
"80": [0x30], // 動作状態
"81": [0xff], // 設置場所
"82": [0x00, 0x00, 0x66, 0x00], // EL version, 1.1
"88": [0x42], // 異常状態
"8a": [0x00, 0x00, 0x77], // maker code
"9d": [0x04, 0x80, 0x8f, 0xa0, 0xb0], // inf map, 1 Byte目は個数
"9e": [0x04, 0x80, 0x8f, 0xa0, 0xb0], // set map, 1 Byte目は個数
"9f": [0x0d, 0x80, 0x81, 0x82, 0x88, 0x8a, 0x8f, 0x9d, 0x9e, 0x9f, 0xa0, 0xb0, 0xb3, 0xbb], // get map, 1 Byte目は個数
// child
"8f": [0x41], // 節電動作設定
"a0": [0x31], // 風量設定
"b0": [0x41], // 運転モード設定
"b3": [0x19], // 温度設定値
"bb": [0x1a] // 室内温度計測値
}
};
ELの受信データを振り分けるよ,何とかしよう. ELの受信をすべて自分で書きたい人はこれを完全に書き換えればいいとおもう. 普通の人はinitializeのuserfuncで事足りるはず.
For controlling all receiving data, update EL.returner function by any function. However this method is not recommended. Generally, all process can be described in userfunc of EL.initialize.
EL.returner = function( bytes, rinfo, userfunc )
おそらく一番使いやすい受信データ解析はEL.facilitiesをそのままreadすることかも. たとえば,そのまま表示すると,
Probably, easy analysis of the received data is to display directory. For example,
console.dir( EL.facilities );
データはこんな感じ.
Reseiving data as,
{ '192.168.2.103':
{ '05ff01': { '80': '', d6: '' },
'0ef001': { '80': '30', d6: '0100' } },
'192.168.2.104': { '0ef001': { d6: '0105ff01' }, '05ff01': { '80': '30' } },
'192.168.2.115': { '0ef001': { '80': '30', d6: '01013501' } } }
また,データ送信で一番使いやすそうなのはsendOPC1だとおもう. これの組み合わせてECHONET Liteはほとんど操作できるのではなかろうか.
The simplest sending method is 'sendOPC1.'
EL.sendOPC1( '192.168.2.103', [0x05,0xff,0x01], [0x01,0x35,0x01], 0x61, 0x80, [0x30]);
Ver. 2.18.1 以降、インスタンスリスト通知(d5)や自ノードインスタンスリストS(d6)を受信した際、自動的に EL.facilities が更新され、プロパティマップも自動取得されるようになりました。
したがって、アプリケーション側では EL.search() を呼んだ後、EL.facilities の変化を監視するのが最も確実です。
Since Ver. 2.18.1, EL.facilities is automatically updated when Instance List Notification (d5) or Self-node Instance List S (d6) is received.
Therefore, the most robust way to parse the network is to call EL.search() and observe EL.facilities.
// 監視を開始
EL.setObserveFacilities(1000, () => {
console.dir(EL.facilities);
});
// 検索パケット送信
EL.search();
Firewall / Security Software:
Network Interface (Dual NICs):
initialize.// IPv4: IP address, IPv6: Interface Name
EL.initialize(objList, callback, 0, { v4: '192.168.1.5', v6: 'en0' });
IPv6 Multicast Issues:
FF02::1) might fail to find devices if the Scope ID is not handled correctly.%en0) correctly in EL.facilities.If npm test fails with timeout:
神奈川工科大学 創造工学部 ホームエレクトロニクス開発学科; Dept. of Home Electronics, Faculty of Creative Engineering, Kanagawa Institute of Technology
杉村 博; SUGIMURA, Hiroshi
Thanks to Github users!
MIT License
-- License summary --
o Commercial use
o Modification
o Distribution
o Private use
x Liability
x Warranty
メジャーバージョンアップを計画しています。 ライブラリ構造の近代化と、使いやすさ・テスト容易性の向上が目標です。
現在の実装は初期化やハンドリングにコールバックを使用しています。 フロー制御向上のため、PromiseベースのAPIを提供する予定です。
await el.initialize(...);
const devices = await el.search();
index.js を小さなモジュール(PacketParser, NetworkLayerなど)に分割する。EL.SETI_SNA = "50"
EL.SETC_SNA = "51"
EL.GET_SNA = "52"
EL.INF_SNA = "53"
EL.SETGET_SNA = "5e"
EL.SETI = "60"
EL.SETC = "61"
EL.GET = "62"
EL.INF_REQ = "63"
EL.SETGET = "6e"
EL.SET_RES = "71"
EL.GET_RES = "72"
EL.INF = "73"
EL.INFC = "74"
EL.INFC_RES = "7a"
EL.SETGET_RES = "7e"
FAQs
This module provides ECHONET Lite protocol API.
The npm package echonet-lite receives a total of 26 weekly downloads. As such, echonet-lite popularity was classified as not popular.
We found that echonet-lite demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.