このモジュールはECHONET Liteプロトコルをサポートします. ECHONET Liteプロトコルはスマートハウス機器の通信プロトコルです.
This module provides ECHONET Lite protocol. The ECHONET Lite protocol is a communication protocol for smart home devices.
注意:本モジュールによる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.INFREQ: // 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.
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
Options is optional
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()
戻り値はObject
output is object data.
ECHONET Liteネットワーク監視
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の指定方法は、
文字列で '192.168.10.3' のように記述する方法
rinfoオブジェクトのように {address:'192.168.10.3', family:'IPv4'} のように記述する方法の両方使えます
EL送信のベース, base function
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");
EL.sendString = function( ip, string )
複数のEPCで送信する
EL.sendDetails = function (ip, seoj, deoj, esv, DETAILs)
省略したELDATA型で送信する
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]);
神奈川工科大学 創造工学部 ホームエレクトロニクス開発学科; 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
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"