node-enocean-ts
Version:
Typescript full implementation of the enocean protocol (ESP3 + EEP2.6)
202 lines • 11.9 kB
JavaScript
"use strict";
// This file is part of node-enocean.
Object.defineProperty(exports, "__esModule", { value: true });
// node-enocean. is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// node-enocean. is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with node-enocean. If not, see <http://www.gnu.org/licenses/>.
// # EnOcean ESP 3.0 implementation
// this module extracts basic info from telegrams which are passed here as a Buffer
const M = [];
M[0x000] = "MANUFACTURER_RESERVED";
M[0x001] = "PEHA";
M[0x002] = "THERMOKON";
M[0x003] = "SERVODAN";
M[0x004] = "ECHOFLEX_SOLUTIONS";
M[0x005] = "OMNIO_AG";
M[0x006] = "HARDMEIER_ELECTRONICS";
M[0x007] = "REGULVAR_INC";
M[0x008] = "AD_HOC_ELECTRONICS";
M[0x009] = "DISTECH_CONTROLS";
M[0x00A] = "KIEBACK_AND_PETER";
M[0x00B] = "ENOCEAN_GMBH";
M[0x00C] = "PROBARE";
M[0x00D] = "ELTAKO";
M[0x00E] = "LEVITON";
M[0x00F] = "HONEYWELL";
M[0x010] = "SPARTAN_PERIPHERAL_DEVICES";
M[0x011] = "SIEMENS";
M[0x012] = "T_MAC";
M[0x013] = "RELIABLE_CONTROLS_CORPORATION";
M[0x014] = "ELSNER_ELEKTRONIK_GMBH";
M[0x015] = "DIEHL_CONTROLS";
M[0x016] = "BSC_COMPUTER";
M[0x017] = "S_AND_S_REGELTECHNIK_GMBH";
M[0x018] = "MASCO_CORPORATION";
M[0x019] = "INTESIS_SOFTWARE_SL";
M[0x01A] = "VIESSMANN";
M[0x01B] = "LUTUO_TECHNOLOGY";
M[0x01C] = "SCHNEIDER_ELECTRIC";
M[0x01D] = "SAUTER";
M[0x01E] = "BOOT_UP";
M[0x01F] = "OSRAM_SYLVANIA";
M[0x020] = "UNOTECH";
M[0x21] = "DELTA_CONTROLS_INC";
M[0x022] = "UNITRONIC_AG";
M[0x023] = "NANOSENSE";
M[0x024] = "THE_S4_GROUP";
M[0x025] = "MSR_SOLUTIONS";
M[0x26] = "GE";
M[0x027] = "MAICO";
M[0x28] = "RUSKIN_COMPANY";
M[0x29] = "MAGNUM_ENERGY_SOLUTIONS";
M[0x02A] = "KMC_CONTROLS";
M[0x02B] = "ECOLOGIX_CONTROLS";
M[0x2C] = "TRIO_2_SYS";
M[0x02D] = "AFRISO_EURO_INDEX";
M[0x030] = "NEC_ACCESSTECHNICA_LTD";
M[0x031] = "ITEC_CORPORATION";
M[0x32] = "SIMICX_CO_LTD";
M[0x34] = "EUROTRONIC_TECHNOLOGY_GMBH";
M[0x35] = "ART_JAPAN_CO_LTD";
M[0x36] = "TIANSU_AUTOMATION_CONTROL_SYSTE_CO_LTD";
M[0x38] = "GRUPPO_GIORDANO_IDEA_SPA";
M[0x39] = "ALPHAEOS_AG";
M[0x3A] = "TAG_TECHNOLOGIES";
M[0x3C] = "PRESSAC";
M[0x3E] = "GIGA_CONCEPT";
M[0x3F] = "SENSORTEC";
M[0x40] = "JAEGER_DIREKT";
M[0x41] = "AIR_SYSTEM_COMPONENTS_INC";
M[0x46] = "NODON";
M[0x7F] = "MULTI_USER_MANUFACTURER";
var Manufacturer_List = M;
// [ // the List of Manufacturers. the index is equal to the number transmitted in learn telegrams
// 'MANUFACTURER_RESERVED' ,'PEHA','THERMOKON','SERVODAN','ECHOFLEX_SOLUTIONS','OMNIO_AG','HARDMEIER_ELECTRONICS','REGULVAR_INC','AD_HOC_ELECTRONICS',
// 'DISTECH_CONTROLS','KIEBACK_AND_PETER','ENOCEAN_GMBH','PROBARE','ELTAKO','LEVITON','HONEYWELL','SPARTAN_PERIPHERAL_DEVICES','SIEMENS','T_MAC',
// 'RELIABLE_CONTROLS_CORPORATION','ELSNER_ELEKTRONIK_GMBH','DIEHL_CONTROLS','BSC_COMPUTER','S_AND_S_REGELTECHNIK_GMBH','MASCO_CORPORATION','INTESIS_SOFTWARE_SL',
// 'VIESSMANN','LUTUO_TECHNOLOGY','SCHNEIDER_ELECTRIC','SAUTER','BOOT_UP','OSRAM_SYLVANIA','UNOTECH','ELTA_CONTROLS_INC','UNITRONIC_AG','NANOSENSE',
// 'THE_S4_GROUP','MSR_SOLUTIONS','GE','MAICO','RUSKIN_COMPANY','MAGNUM_ENERGY_SOLUTIONS','KMC_CONTROLS','ECOLOGIX_CONTROLS','TRIO_2_SYS','AFRISO_EURO_INDEX',
// 'NEC_ACCESSTECHNICA_LTD','ITEC_CORPORATION','SIMICX_CO_LTD','EUROTRONIC_TECHNOLOGY_GMBH','ART_JAPAN_CO_LTD','TIANSU_AUTOMATION_CONTROL_SYSTE_CO_LTD',
// 'GRUPPO_GIORDANO_IDEA_SPA','ALPHAEOS_AG','TAG_TECHNOLOGIES','CLOUD_BUILDINGS_LTD','GIGA_CONCEPT','SENSORTEC','JAEGER_DIREKT','AIR_SYSTEM_COMPONENTS_INC'
// ]
class Telegram {
constructor(buf) {
this.timestamp = Date.now();
this.base = "";
this.raw = buf;
this.rawByte = buf.toString("hex"); // store the original Buffer as a string in .rawByte
var dataLength = 255 * buf[1] + buf[2]; // length of the Data Part of the telegram
var optionalLength = buf[3]; // length of the optional data part of the telegram
this.packetType = buf[4]; // packet type ( 1=radio telegram , 2=response... )
var headerCRC = buf[5]; // checksum of the header
var rawDataByte = buf.slice(7, dataLength + 7); // Data byte start at Byte 7 and end at 6 + dataLength
switch (this.packetType) {
case 2:
// RESPONSES are not really supportet yet (2.0?) the only propper use here is for getting the base address
this.packetTypeString = "RESPONSE";
this.returnCode = buf[6];
var rc_string = ["RET_OK", "RET_ERROR", "RET_NOT_SUPPORTED", "RET_WRONG_PARAM", "RET_OPERATION_DENIED"];
if (this.returnCode < 128) {
this.returnCodeString = rc_string[this.returnCode];
}
if (dataLength == 5) {
// not really correct, but for now assume that RESPNSES with a length of 5 contain the base address
this.base = buf.slice(7, 11).toString("hex");
}
this.raw = rawDataByte;
break;
case 1:
// this is a RADIO telegram. senderId and choice are part of every radio telegram
this.senderId = buf.slice(dataLength + 1, dataLength + 5).toString("hex"); // should we rename this to sensorId? needs a bit of refactoring...
this.choice = buf[6].toString(16); // choice equals the RORG. for now 4BS (a5), RPS(f6) and 1BS(d5) are supported
var optionalData = buf.slice(dataLength + 6, buf.length - 1);
this.subTelNum = optionalData[0];
this.destinationId = optionalData.slice(1, 5).toString("hex");
this.rssi = optionalData[5];
this.securityLevel = optionalData[6];
switch (this.choice) {
case "a5":
// this is a 4BS (4 Byte Communication) Telegram. the data part is 4 bytes long
this.packetTypeString = "4BS";
this.raw = pad(buf.slice(7, 11).toString("hex"), 8); // the data part as a hex string
var rawNr = parseInt(buf.slice(7, 11).toString("hex"), 16); // the data part as decimal number
//extract the learn bit
// Bit 3 (so the 4th Bit) (0b1000=8) is the learnBit. if it is 0 (f.e. 10111) then this is a learn telegram
this.learnBit = (rawNr & 8) >>> 3;
// allways use zero fill bit shifts to prevent accidetial negative numbers, because in js INTs are signed
if (this.learnBit == 0) {
// the following extractions could be written shorter, but this way ist more clear what goes on
var func = ((parseInt("11111100000000000000000000000000", 2) & rawNr) >>> 26).toString(16); // in the first 6 bits the func part of the eep is stored
var type = ((parseInt("00000011111110000000000000000000", 2) & rawNr) >>> 19).toString(16); // in the next 7 bits the type part of the eep can be found
var MANUFACTURERID = (parseInt("00000000000001111111111100000000", 2) & rawNr) >>> 8; // the next 11 bit contain the manufacturer id, leaving 8 bits (Byte0 of the telegram unused)
// convert the eep into a string of the form "a5-ff-tt" where ff is the func in hex and tt is the type in hex
// pad the values with leading zeros so the numbers are allways 2 digits
this.eep = "a5-" + pad(func, 2) + "-" + pad(type, 2);
this.manufacturer = Manufacturer_List[MANUFACTURERID]; // look up the name of the manufacturer
}
break;
case "f6":
// this is an RPS Packet. It just contains 1 data byte, there is no learnBit
this.raw = pad(buf[7].toString(16), 2); // extract the data byte (Byte0) as a padded hex string
this.packetTypeString = "RPS"; // its an RPS telegram (rocker switch)
break;
case "d5":
// this is a 1BS telegram. it also contains only one data byte, but it also carries a learn bit
this.raw = pad(buf[7].toString(16), 2); // extract the data byte (Byte0) as a padded hex string
// Bit 3 (so the 4th Bit) (0b1000=8) is the learnBit. if it is 0 (f.e. 10111) then this is a learn telegram
this.learnBit = (parseInt(this.raw, 16) & 8) >>> 3;
this.packetTypeString = "1BS";
break;
case "d2":
// this is a VLD telegram.
this.raw = pad(rawDataByte.toString("hex"), dataLength * 2); // extract the data byte (Byte0) as a padded hex string
// Bit 3 (so the 4th Bit) (0b1000=8) is the learnBit. if it is 0 (f.e. 10111) then this is a learn telegram
this.learnBit = 1;
this.packetTypeString = "VLD";
break;
case "d1":
// this is a MSC telegram.
this.raw = pad(rawDataByte.toString("hex"), dataLength * 2); // extract the data byte (Byte0) as a padded hex string
// Bit 3 (so the 4th Bit) (0b1000=8) is the learnBit. if it is 0 (f.e. 10111) then this is a learn telegram
this.learnBit = rawDataByte[dataLength - 1] & 8;
if (this.learnBit == 0) {
var func = pad(rawDataByte[0].toString(16), 2);
var type = pad(rawDataByte[1].toString(16), 2);
this.eep = `d1-${func}-${type}`;
this.manufacturerid = parseInt("0x" + func + "" + type[0]);
this.manufacturer = Manufacturer_List[parseInt("0x" + func + "" + type[0])];
}
this.packetTypeString = "MSC";
break;
case "d4":
// this is an UTE Telegram (Universal Teach In)
this.raw = pad(rawDataByte.toString("hex"), dataLength * 2); // extract the data byte (Byte0) as a padded hex string
this.learnBit = 0;
this.packetTypeString = "UTE";
var typeStr = this.raw.substring(8, 10);
var funcStr = this.raw.substring(10, 12);
var choice = this.raw.substring(12, 14);
var MANUFACTURERID = parseInt(this.raw.substring(4, 6), 16);
this.manufacturerid = MANUFACTURERID;
this.eep = choice + "-" + pad(funcStr, 2) + "-" + pad(typeStr, 2);
this.manufacturer = Manufacturer_List[MANUFACTURERID];
break;
}
break;
}
}
}
exports.default = Telegram;
// a helper function repeated here, so this module does not require anything
function pad(num, size) {
var s = "00000000000000000000000000000000" + num; // maximum number of zero we need
return s.substr(s.length - size); // cut to size
}
//# sourceMappingURL=telegram.js.map