UNPKG

zigbee-herdsman-zigate

Version:

An open source ZigBee gateway solution with node.js.

324 lines 14.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const serialport_1 = __importDefault(require("serialport")); const frame_1 = __importDefault(require("./frame")); const events_1 = require("events"); const debug_1 = require("../debug"); const serialPortUtils_1 = __importDefault(require("../../serialPortUtils")); const socketPortUtils_1 = __importDefault(require("../../socketPortUtils")); const net_1 = __importDefault(require("net")); const utils_1 = require("../../../utils"); const constants_1 = require("./constants"); const ziGateObject_1 = __importDefault(require("./ziGateObject")); const zcl_1 = require("../../../zcl"); const waitress_1 = __importDefault(require("../../../utils/waitress")); const debug = debug_1.Debug('driver'); const autoDetectDefinitions = [ { manufacturer: 'zz', vendorId: 'id', productId: '00' }, ]; const timeouts = { reset: 30000, default: 3000, }; function zeroPad(number, size) { return (number).toString(16).padStart(size || 4, '0'); } function resolve(path, obj, separator = '.') { const properties = Array.isArray(path) ? path : path.split(separator); return properties.reduce((prev, curr) => prev && prev[curr], obj); } class ZiGate extends events_1.EventEmitter { constructor(path, serialPortOptions) { super(); // eslint-disable-next-line prefer-rest-params debug.log('construct', arguments); this.path = path; this.baudRate = typeof serialPortOptions.baudRate === 'number' ? serialPortOptions.baudRate : 115200; this.rtscts = typeof serialPortOptions.rtscts === 'boolean' ? serialPortOptions.rtscts : false; this.portType = socketPortUtils_1.default.isTcpPath(path) ? 'socket' : 'serial'; this.initialized = false; this.queue = new utils_1.Queue(1); this.waitress = new waitress_1.default(this.waitressValidator, this.waitressTimeoutFormatter); } static isValidPath(path) { return __awaiter(this, void 0, void 0, function* () { return serialPortUtils_1.default.is(path, autoDetectDefinitions); }); } static autoDetectPath() { return __awaiter(this, void 0, void 0, function* () { const paths = yield serialPortUtils_1.default.find(autoDetectDefinitions); return paths.length > 0 ? paths[0] : null; }); } open() { return this.portType === 'serial' ? this.openSerialPort() : this.openSocketPort(); } close() { debug.error('close'); return new Promise((resolve, reject) => { if (this.initialized) { this.initialized = false; this.portWrite = null; if (this.portType === 'serial') { this.serialPort.flush(() => { this.serialPort.close((error) => { this.serialPort = null; error == null ? resolve() : reject(new Error(`Error while closing serialPort '${error}'`)); this.emit('close'); }); }); } else { // @ts-ignore this.socketPort.destroy((error) => { this.socketPort = null; error == null ? resolve() : reject(new Error(`Error while closing serialPort '${error}'`)); this.emit('close'); }); } } else { resolve(); this.emit('close'); } }); } sendCommand(code, payload) { return __awaiter(this, void 0, void 0, function* () { // const argument = arguments; return this.queue.execute(() => __awaiter(this, void 0, void 0, function* () { try { debug.log('Send command \x1b[42m>>>> ' + constants_1.ZiGateCommandCode[code] + ' 0x' + zeroPad(code) + ' <<<<\x1b[0m '); debug.log('payload: ', payload); const ziGateObject = ziGateObject_1.default.createRequest(code, payload); const frame = ziGateObject.toZiGateFrame(); const sendBuffer = frame.toBuffer(); debug.log('Send command buff: ', sendBuffer); const waiters = []; ziGateObject.command.response.forEach((rules) => { waiters.push(this.waitress.waitFor({ ziGateObject, rules }, timeouts.default).start().promise); }); // if (ziGateObject.command.response) { // waiter = this.waitress.waitFor({ // responseType: ziGateObject.command.wait_response, // commandCode: ziGateObject.code // }, timeouts.default); // } else if (ziGateObject.command.wait_status) { // waiter = this.waitress.waitFor( // {responseType: 0x8000, commandCode: ziGateObject.code}, // timeouts.default // ); // } this.portWrite.write(sendBuffer); return Promise.race(waiters); // return new Promise(async (resolve, reject) => { // const firstResult = await Promise.race(waiters); // // if (firstResult.code === 0x8000 && firstResult.payload.status !== 0) { // reject(); // } // if (firstResult.code === 0x8000 // && firstResult.payload.status === 0 // && // ) { // && count promiss >0 // const dataResult = await Promise.race(waiters); // } //status // }); } catch (e) { debug.error(e); return new Promise((resolve, reject) => { reject(); }); } })); }); } waitFor(matcher, timeout = timeouts.default) { return this.waitress.waitFor(matcher, timeout); } openSerialPort() { return __awaiter(this, void 0, void 0, function* () { this.serialPort = new serialport_1.default(this.path, { baudRate: this.baudRate, dataBits: 8, parity: 'none', stopBits: 1, lock: false, autoOpen: false }); this.parser = this.serialPort.pipe(new serialport_1.default.parsers.Delimiter({ delimiter: [frame_1.default.STOP_BYTE], includeDelimiter: true })); this.parser.on('data', this.onSerialData.bind(this)); this.portWrite = this.serialPort; return new Promise((resolve, reject) => { this.serialPort.open((err) => __awaiter(this, void 0, void 0, function* () { if (err) { this.serialPort = null; this.parser = null; this.path = null; this.initialized = false; const error = `Error while opening serialPort '${err}'`; debug.error(error); reject(new Error(error)); } else { debug.log('Successfully connected ZiGate port \'' + this.path + '\''); this.serialPort.on('error', (error) => { debug.error(`serialPort error: ${error}`); }); this.serialPort.on('close', this.onPortClose.bind(this)); this.initialized = true; resolve(); } })); }); }); } openSocketPort() { return __awaiter(this, void 0, void 0, function* () { const info = socketPortUtils_1.default.parseTcpPath(this.path); debug.log(`Opening TCP socket with ${info.host}:${info.port}`); this.socketPort = new net_1.default.Socket(); this.socketPort.setNoDelay(true); this.socketPort.setKeepAlive(true, 15000); this.parser = this.socketPort.pipe(new serialport_1.default.parsers.Delimiter({ delimiter: [frame_1.default.STOP_BYTE], includeDelimiter: true })); this.parser.on('data', this.onSerialData.bind(this)); this.portWrite = this.socketPort; return new Promise((resolve, reject) => { this.socketPort.on('connect', function () { debug.log('Socket connected'); }); // eslint-disable-next-line const self = this; this.socketPort.on('ready', function () { return __awaiter(this, void 0, void 0, function* () { debug.log('Socket ready'); self.initialized = true; resolve(); }); }); this.socketPort.once('close', this.onPortClose); this.socketPort.on('error', (error) => { debug.log('Socket error', error); // reject(new Error(`Error while opening socket`)); reject(); self.initialized = false; }); this.socketPort.connect(info.port, info.host); }); }); } onSerialError(err) { debug.error('serial error: ', err); } onPortClose() { debug.log('serial closed'); this.initialized = false; this.emit('close'); } onSerialData(buffer) { try { debug.log(`--- parseNext `, buffer); const frame = new frame_1.default(buffer); const code = frame.readMsgCode(); let msgName; try { msgName = constants_1.ZiGateMessageCode[code] ? constants_1.ZiGateMessageCode[code] : ''; msgName += ' 0x' + zeroPad(code); } catch (e) { } debug.log(`--> parsed frame \x1b[1;34m>>>> ${msgName} <<<<`); // debug.log(frame); try { const ziGateObject = ziGateObject_1.default.fromZiGateFrame(frame); if (ziGateObject === undefined) return; // debug.log(ziGateObject.payload, frame.readRSSI()); this.waitress.resolve(ziGateObject); if (code !== constants_1.ZiGateMessageCode.Status || ziGateObject.payload.status !== 0) { debug.info(`--> frame to object `, ziGateObject.payload); } if (code === constants_1.ZiGateMessageCode.DataIndication) { debug.info('raw'); let zclFrame = null; switch (ziGateObject.payload.clusterID) { case 0x8005: // @ts-ignore zclFrame = ziGateObject_1.default.fromBufer(0x8005, ziGateObject.payload.payload); break; default: try { // @ts-ignore zclFrame = zcl_1.ZclFrame.fromBuffer(ziGateObject.payload.clusterID, ziGateObject.payload.payload); } catch (e) { zclFrame = null; } } this.emit('received', { ziGateObject, zclFrame }); } else if (code === constants_1.ZiGateMessageCode.LeaveIndication) { debug.log('raw leave'); this.emit('LeaveIndication', { ziGateObject }); } else if (code === constants_1.ZiGateMessageCode.DeviceAnnounce) { debug.log('raw announce'); this.emit('DeviceAnnounce', { ziGateObject }); } } catch (error) { this.emit('receivedRaw', { error, frame }); // debug.error(`'${error.stack}'`); } } catch (error) { debug.error(`Error while parsing to ZiGateObject '${error.stack}'`); } } waitressTimeoutFormatter(matcher, timeout) { return `${matcher} after ${timeout}ms`; } waitressValidator(ziGateObject, matcher) { const validator = (rule) => { try { let expectedValue; if (typeof rule.value === "undefined" && typeof rule.expectedProperty !== "undefined") { expectedValue = resolve(rule.expectedProperty, matcher.ziGateObject); } else { expectedValue = rule.value; } const receivedValue = resolve(rule.receivedProperty, ziGateObject); // debug.info(expectedValue, receivedValue, rule); return rule.matcher(expectedValue, receivedValue); } catch (e) { return false; } }; return matcher.rules.every(validator); } } exports.default = ZiGate; //# sourceMappingURL=zigate.js.map