Hiroshi-Sugimura / echonet-lite.js

This project is creating npm module for providing ECHONET Lite API.
MIT License
22 stars 5 forks source link

Overview

このモジュールは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認証を取得する必要があります。

Install

下記コマンドでモジュールをインストールできます.

You can install the module as following command.

npm i echonet-lite

Demos

Controller demo

デモプログラムはこんな感じです。動作させるためには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();

Device demo

こんな感じで作ってみたらどうでしょうか. あとは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
//////////////////////////////////////////////////////////////////////

Data stracture

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

受信データ (=els, =el structure型とも)

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 ) )
}

オブジェクト内の機器情報リスト, facilities information

こんな感じ

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' } } }

識別番号リスト, identification numbers

こんな感じ

EL.identificationNumbers
[
  {
    id: 'fe0000776e5b0d002b5b0ef00100000000',
    ip: '192.168.2.11',
    OBJ: '0ef001'
  },
  {
    id: 'fe0000776a6ee920fd7002870100000000',
    ip: '192.168.2.11',
    OBJ: '028701'
  }
]

自分がデバイス扱いしたい場合は下記の dev_details の形でEPC管理すると使える関数がある

下記はエアコンと照明の詳細オブジェクトを持っている場合である。

// 自分のエアコンのデータ,今回はこのデータをグローバル的に使用する方法で紹介する.
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
    }
};

API

初期化と受信, 監視, initialize, receriver callback and observation

EL.initialize = function ( objList, userfunc, ipVer = 4, Options = {v4: '', v6: '', ignoreMe: true, autoGetProperties: true, debugMode: false} )
function( rinfo, els, err ) {
    console.log('==============================');
    if( err ) {
        console.dir(err);
    }else{
        // ToDo
    }
}
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 ();

データ表示系, data representations

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

変換系, converters

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 )

送信, send

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");
EL.sendString = function( ip, string )
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.');
});

ex.

const cron = require('node-cron');
cron.schedule( '*/3 * * * *', () => {
    EL.complementFacilities();
})

OPC2以上対応, OPC managements for more than two

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] // 室内温度計測値
    }
};

受信データの完全コントロール, Full control method for received data.

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 )

echonet-lite.js 攻略情報 / Knowhow

おそらく一番使いやすい受信データ解析は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]);

meta data

Authors

神奈川工科大学 創造工学部 ホームエレクトロニクス開発学科; Dept. of Home Electronics, Faculty of Creative Engineering, Kanagawa Institute of Technology

杉村 博; SUGIMURA, Hiroshi

thanks

Thanks to Github users!

License

MIT License

-- License summary --
o Commercial use
o Modification
o Distribution
o Private use
x Liability
x Warranty

Log