UNPKG

homebridge-green-mountain-grills

Version:

A dynamic platform plugin for homebridge to provide access to your GMG WiFi smoker.

235 lines 9.24 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const dgram_1 = __importDefault(require("dgram")); const GrillStatus_1 = __importDefault(require("./GrillStatus")); const commands = Object.freeze({ powerOn: 'UK001!', powerOff: 'UK004!', getGrillStatus: 'UR001!', getGrillModel: 'UN!', getGrillId: 'UL!', setGrillTempF: (temp) => `UT${temp}!`, setFoodTempF: (temp) => `UF${temp}!` }); const results = Object.freeze({ OK: 'OK' }); class GMGClient { constructor(param) { this.port = 8080; this.host = '255.255.255.255'; this.tries = 5; this.retryMs = 2000; if (param.port) { this.port = param.port; } if (param.host) { this.host = param.host; } if (param.tries) { this.tries = param.tries; } if (param.retryMs) { this.retryMs = param.retryMs; } this._logger = (message) => param.logger.info(message); } getCommandData(command) { const fullCommand = `${command}!\n`; return Buffer.from(fullCommand, 'ascii'); } async getGrillId() { const result = await this.sendCommand(commands.getGrillId); return result.msg.toString(); } async getGrillModelAndFirmware() { try { const result = (await this.sendCommand(commands.getGrillModel)).msg.toString(); const fw = result.substring(11); switch (result.substring(2, 4)) { case 'DB': return { model: 'Daniel Boone', firmware: fw }; case 'JB': return { model: 'Jim Bowie', firmware: fw }; default: return { model: 'Unknown', firmware: result }; } } catch (e) { this._logger(e); } } async getGrillStatus() { try { const result = await this.sendCommand(commands.getGrillStatus); return new GrillStatus_1.default(result.msg); } catch (e) { this._logger(e); } } async powerToggleGrill() { const status = await this.getGrillStatus(); if (status?.isOn) { await this._powerOffGrill(status); } else { await this._powerOnGrill(status); } } async powerOffGrill() { const status = await this.getGrillStatus(); await this._powerOffGrill(status); } async powerOnGrill() { const status = await this.getGrillStatus(); await this._powerOnGrill(status); } async setGrillTemp(fahrenheit) { const status = await this.getGrillStatus(); if (!status?.isOn) { const error = new Error('Cannot set grill temperature when the gill is off!'); this._logger(error.message); throw error; } const command = commands.setGrillTempF(fahrenheit); const result = await this.sendCommand(command); await this._validateResult(result, newState => newState.desiredGrillTemp === fahrenheit); } async setFoodTemp(fahrenheit) { const status = await this.getGrillStatus(); if (!status?.isOn) { const error = new Error('Cannot set food temperature when the gill is off!'); this._logger(error.message); throw error; } const command = commands.setFoodTempF(fahrenheit); const result = await this.sendCommand(command); await this._validateResult(result, newState => newState.desiredFoodTemp === fahrenheit); } async discoverGrill({ tries = this.tries } = {}) { return new Promise((res, rej) => { let attempts = 0, schedule; const socket = dgram_1.default.createSocket('udp4'); const data = this.getCommandData(commands.getGrillId); const finish = (result) => { // eslint-disable-next-line no-undef if (schedule) { clearInterval(schedule); } socket.removeAllListeners('message'); socket.close(); result instanceof Error ? rej(result) : res(result); }; socket.bind(() => { // Listen for response socket.setBroadcast(true); socket.on('message', (msg, info) => { // Make sure the response is not a broadcast to ourself if (!msg.equals(data)) { this.host = info.address; finish(this.host); this._logger(`Received discovery response dgram from Grill (${info.address}:${info.port})`); } }); // Send Commands this._logger('Attempting grill discovery...'); // eslint-disable-next-line no-undef schedule = setInterval(() => { if (attempts >= tries) { const error = new Error(`No response from Grill (${this.host}:${this.port}) after [${attempts}] discovery attempts!`); finish(error); this._logger(error.message); } else { attempts++; socket.send(data, 0, data.byteLength, this.port, this.host, error => { if (error) { this._logger(`Grill (${this.host}:${this.port}) discovery broadcast dgram send failed -> ${error}`); } else { this._logger(`Grill (${this.host}:${this.port}) discovery broadcast dgram sent -> Attempt #${attempts}`); } }); } }, this.retryMs); }); }); } async sendCommand(command, { tries = this.tries } = {}) { if (this.host === '255.255.255.255') { this._logger('Grill host is broadcast address!'); await this.discoverGrill(); } return await new Promise((res, rej) => { let attempts = 0; // eslint-disable-next-line prefer-const let schedule; const data = this.getCommandData(command); const socket = dgram_1.default.createSocket('udp4'); const offset = data.byteLength; const finish = (result) => { // eslint-disable-next-line no-undef if (schedule) { clearInterval(schedule); } socket.removeAllListeners('message'); socket.close(); result instanceof Error ? rej(result) : res(result); }; // Listen for response socket.on('message', (msg, info) => { if (info.address === this.host) { finish({ msg, info }); this._logger(`Received response dgram from Grill (${info.address}:${info.port})`); } }); // Send Commands // eslint-disable-next-line no-undef schedule = setInterval(() => { if (attempts > tries) { const error = new Error(`No response from Grill after [${attempts}] command sent attempts!`); finish(error); this._logger(error.message); } else { attempts++; socket.send(data, 0, offset, this.port, this.host, error => { if (error) { this._logger(`Grill (${this.host}:${this.port}) [${command}] command dgram send failed -> ${error}`); } else { this._logger(`Grill (${this.host}:${this.port}) [${command}] command dgram sent -> Attempt #${attempts}.`); } }); } }, this.retryMs); }); } async _powerOffGrill(status) { if (!status.isOn) { return; } const result = await this.sendCommand(commands.powerOff); await this._validateResult(result, newState => !newState.isOn); } async _powerOnGrill(status) { if (status.fanModeActive) { const error = new Error('Cannot start grill when fan mode is active.'); this._logger(error.message); throw error; } const result = await this.sendCommand(commands.powerOn); await this._validateResult(result, newState => newState.isOn); } async _validateResult(result, _validator) { const response = result.msg.toString(); if (response !== results.OK) { throw new Error(`Grill responded with non OK status -> ${response}`); } } } exports.default = GMGClient; //# sourceMappingURL=GMGClient.js.map