UNPKG

iobroker.wireless-mbus

Version:

Receive data from Wireless Meter-Bus (wM-Bus) devices like gas or electricity meters

350 lines (295 loc) 10.7 kB
'use strict'; const SerialDevice = require('./SerialDevice'); const EbiMessage = require('./EbiMessage'); /* eslint-disable no-unused-vars */ const DEVICE_INFORMATION_PROTOCOL = { 0x00: 'Unknown', 0x01: 'Proprietary', 0x10: '802.15.4', 0x20: 'ZigBee', 0x21: 'ZigBee 2004 (1.0)', 0x22: 'ZigBee 2006', 0x23: 'ZigBee 2007', 0x24: 'ZigBee 2007-Pro', 0x40: 'Wireless M-Bus' }; const DEVICE_INFORMATION_MODULE = { 0x00: 'Unknown', 0x10: 'Reserved', 0x20: 'EMB-ZRF2xx', 0x24: 'EMB-ZRF231xx', 0x26: 'EMB-ZRF231PA', 0x28: 'EMB-ZRF212xx', 0x29: 'EMB-ZRF212B', 0x30: 'EMB-Z253x', 0x34: 'EMB-Z2530x', 0x36: 'EMB-Z2530PA', 0x38: 'EMB-Z2531x', 0x3A: 'EMB-Z2531PA-USB', 0x3C: 'EMB-Z2538x', 0x3D: 'EMB-Z2538PA', 0x40: 'EMB-WMBx', 0x44: 'EMB-WMB169x', 0x45: 'EMB-WMB169T', 0x46: 'EMB-WMB169PA', 0x48: 'EMB-WMB868x', 0x49: 'EMB-WMB868' }; const JOINING_NETWORK_PREFERENCE = { 'JOINING_NETWORK_NOT_PERMITTED': 0x00, 'JOINING_NETWORK_PERMITTED': 0x01 }; const SCAN_MODE = { 'SCAN_MODE_ENERGY': 0x00, 'SCAN_MODE_PASSIVE': 0x01, 'SCAN_MODE_ACTIVE': 0x02 }; const EXECUTION_STATUS_BYTE_VALUE = { 0x00: 'Success', 0x01: 'Generic error', 0x02: 'Parameters not accepted', 0x03: 'Operation timeout', 0x04: 'No memory', 0x05: 'Unsupported', 0x06: 'Busy', 0x07: 'Duty Cycle' }; const CHANNELS_WMB = { 1: 0x01, // 169.40625[MHz] @4.8[kbps] 2: 0x02, // 169,41875[MHz] @4.8[kbps] 3: 0x03, // 169,43125[MHz] @2.4[kbps] 4: 0x04, // 169,44375[MHz] @2.4[kbps] 5: 0x05, // 169,45625[MHz] @4.8[kbps] 6: 0x06, // 169,46875[MHz] @4.8[kbps] 7: 0x07, // 169,43750[MHz] @19.2[kbps] 13: 0x0D, // 868.030[MHz] @4.8[kbps] 14: 0x0E, // 868,090[MHz] @4.8[kbps] 15: 0x0F, // 868,150[MHz] @4.8[kbps] 16: 0x10, // 868.210[MHz] @4.8[kbps] 17: 0x11, // 868.270[MHz] @4.8[kbps] 18: 0x12, // 868.330[MHz] @4.8[kbps] 19: 0x13, // 868.390[MHz] @4.8[kbps] 20: 0x14, // 868.450[MHz] @4.8[kbps] 21: 0x15, // 868.510[MHz] @4.8[kbps] 22: 0x16, // 868.570[MHz] @4.8[kbps] 23: 0x17, // 868,300[MHz] @16,384[kbps] 24: 0x18, // 868,300[MHz] @16,384[kbps] 25: 0x19, // 868,950[MHz] @66.666[kbps] 26: 0x1A, // 868.300[MHz] @16.384[kbps] 27: 0x1B, // 868.030[MHz] @2.4[kbps] 28: 0x1C, // 868.090[MHz] @2.4[kbps] 29: 0x1D, // 868.150[MHz] @2.4[kbps] 30: 0x1E, // 868.210[MHz] @2.4[kbps] 31: 0x1F, // 868.270[MHz] @2.4[kbps] 32: 0x20, // 868.330[MHz] @2.4[kbps] 33: 0x21, // 868.390[MHz] @2.4[kbps] 34: 0x22, // 868.450[MHz] @2.4[kbps] 35: 0x23, // 868.510[MHz] @2.4[kbps] 36: 0x24, // 868.570[MHz] @2.4[kbps] 37: 0x25, // 868.950[MHz] @100[kbps] 38: 0x26 // 869,525[MHz] @50[kbps] }; // Protocol and general device parameters const CMD_DEVICE_INFORMATION = 0x01; const CMD_DEVICE_STATE = 0x04; const CMD_RESET = 0x05; const CMD_FIRMWARE_VERSION = 0x06; const CMD_RESTORE_SETTINGS = 0x07; const CMD_SAVE_SETTINGS = 0x08; const CMD_UART_CONFIG = 0x09; const CMD_OUTPUT_POWER = 0x10; const CMD_OPERATING_CHANNEL = 0x11; const CMD_ENERGY_SAVE = 0x13; const CMD_NETWORK_AUTOMATED_SETTINGS = 0x24; const CMD_NETWORK_START = 0x31; // Bootloader commands const CMD_BOOTLOADER_ENTER = 0x70; const CMD_BOOTLOADER_SETOPTIONS = 0x71; const CMD_BOOTLOADER_ERASEMEMORY = 0x78; const CMD_BOOTLOADER_WRITE = 0x7A; const CMD_BOOTLOADER_READ = 0x7B; const CMD_BOOTLOADER_COMMIT = 0x7F; const RX_POLICY_ALLWAYS_ON_WMB = 0x00; const RX_POLICY_ALLWAYS_OFF_WMB = 0x01; // Receive window after transmission (whose duration is defined by WMBUS // standard [3]); a notification (received data notification 0xE0) is // generated if a packet is received during this receive window. const RX_POLICY_RECEIVED_WINDOW_WMB = 0x02; // Receive window after transmission (whose duration is defined by WMBUS // standard [3]); a notification will be generated if a packet is received // (just like mode 0x02); however, even if no packet is received, a // notification (device state notification, 0x84, with code 0x51) is // generated to indicate the end of the receiving window. const RX_POLICY_RECEIVED_WITH_END_WINDOW_WMB = 0X03; const MCU_POLICY_ALLWAYS_ON_WMB = 0x00; const MCU_POLICY_ALLWAYS_OFF_WMB = 0x01; const NETWORK_ROLE_WMB = { 'NETWORK_ROLE_METER': 0x00, 'NETWORK_ROLE_OTHER_DEVICE': 0x01 }; /* eslint-enable no-unused-vars */ class EbiReceiver extends SerialDevice { constructor(options, mode, onMessage, onError, loggerFunction) { super(options, mode, onMessage, onError, loggerFunction); this.log.setPrefix('EBI'); } buildPayloadPackage(command, payload) { return new EbiMessage() .setPayload(command, payload) .build(); } checkAndExtractMessage() { const length = this.parserBuffer.length; const expectedLength = EbiMessage.tryToGetLength(this.parserBuffer); if ((expectedLength !== -1) && (length >= expectedLength)) { const messageBuffer = this.parserBuffer.subarray(0, expectedLength); this.parserBuffer = this.parserBuffer.subarray(expectedLength); return messageBuffer; } else { return null; } } validateResponse(pkg, response) { const mPkg = new EbiMessage(); mPkg.parse(pkg); const mResponse = new EbiMessage(); mResponse.parse(response); if (mPkg.payload.length) { if (mResponse.payload[0] != 0x00) { throw new Error(`Package validation failed! Execution status: ${EXECUTION_STATUS_BYTE_VALUE[mResponse.payload[0]]}`); } } if (mPkg.setupResponse().messageId != mResponse.messageId) { throw new Error('MessageId mismatch!'); } } parseRawMessage(messageBuffer) { const ebiMessage = new EbiMessage(); const parseResult = ebiMessage.parse(messageBuffer); if (parseResult !== true) { this.log.info(parseResult); } const options = ebiMessage.payload.readUInt16BE(0); const frameType = this.getFrameType(options); const rssi = this.getRssi(options, ebiMessage.payload); const ts = this.getTimestamp(options, ebiMessage.payload); const rawData = this.stripHeader(options, ebiMessage.payload); return { frameType: frameType, containsCrc: false, rawData: rawData, rssi: rssi, ts: ts }; } getRssi(options, payload) { if (options & 0x8000) { return payload.readInt8(2); } else { return -1; } } getFrameType(options) { if (options & 0x0010) { return 'B'; } else { return 'A'; } } getTimestamp(options, payload) { if (options & 0x0008) { // timestamp since power on const pos = 2 + (options & 0x8000 ? 1 : 0); return payload.readUInt32BE(pos) / 32768; } else { return new Date().getTime(); } } stripHeader(options, payload) { const start = 2 + (options & 0x8000 ? 1 : 0) + (options & 0x0008 ? 4 : 0); return payload.subarray(start); } async reset() { await this.sendPackage(CMD_RESET, Buffer.alloc(0)); const response = await this.readResponse(); const m = new EbiMessage(); m.parse(response); if (m.payload[0] != 0x10) { this.log.error(`Device not ready! ${m.payload.toString('hex')}`); return false; } else { this.log.info('Device ready'); return true; } } async getDeviceInformation() { const response = await this.sendPackage(CMD_DEVICE_INFORMATION, Buffer.alloc(0)); const m = new EbiMessage(); m.parse(response); this.log.info(`Found ${DEVICE_INFORMATION_PROTOCOL[m.payload[0]]} protocol and module ${DEVICE_INFORMATION_MODULE[m.payload[1]]}`); return m.payload; } async setOutputPower(power) { let payload; if (power >= 0) { payload = Buffer.from([power & 0xFF]); } else { payload = Buffer.from([(-power) & 0x80]); } await this.sendPackage(CMD_OUTPUT_POWER, payload); } async setOperatingChannel(channel) { await this.sendPackage(CMD_OPERATING_CHANNEL, Buffer.from([CHANNELS_WMB[channel]])); } async setEnergySave(rxPolicy, mcuPolicy) { await this.sendPackage(CMD_ENERGY_SAVE, Buffer.from([rxPolicy, mcuPolicy])); } async setNetworkAutomatedSettings() { await this.sendPackage(CMD_NETWORK_AUTOMATED_SETTINGS, Buffer.from([0x80, 0x00])); } async saveSettings() { await this.sendPackage(CMD_SAVE_SETTINGS, Buffer.alloc(0)); } async networkStart() { await this.sendPackage(CMD_NETWORK_START, Buffer.alloc(0)); } getMode() { switch (this.mode) { case 'T': return 0x19; case 'S': return 0x18; case 'C': return 0x25; default: return 0x19; } } getModeDescription() { switch (this.mode) { case 'T': return 'T-Mode 868.950[MHz] @66.666[kbps]'; case 'S': return 'S-Mode 868.300[MHz] @16.384[kbps]'; case 'C': return 'C-Mode 868.950[MHz] @100[kbps]'; default: return 'T-Mode 868.950[MHz] @66.666[kbps]'; } } async initDevice() { const deviceInfo = await this.getDeviceInformation(); if (!(deviceInfo[0] & 0x40)) { throw new Error('This is not an Embit Wireless M-Bus device!'); } // do a reset for a cleaner state const deviceReady = await this.reset(); // set channel, power, energy saving await this.setOutputPower(0x0F); this.log.info('Power set to max'); await this.setOperatingChannel(this.getMode()); this.log.info(`Channel set to ${this.getModeDescription()}`); await this.setEnergySave(RX_POLICY_ALLWAYS_ON_WMB, MCU_POLICY_ALLWAYS_ON_WMB); this.log.info('Energy saving disabled'); if (deviceReady) { await this.setNetworkAutomatedSettings(); this.log.info('Automatically start network'); await this.saveSettings(); this.log.info('Settings saved'); } await this.networkStart(); this.log.info('Network start okay!'); } } module.exports = EbiReceiver;