UNPKG

node-enocean-ts

Version:

Typescript full implementation of the enocean protocol (ESP3 + EEP2.6)

202 lines 11.9 kB
"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