UNPKG

tplink-lightbulb-fix

Version:

Control TP-Link smart-home devices from nodejs

311 lines (253 loc) 7.73 kB
import dgram from 'dgram'; import EventEmitter from 'events'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } class TPLSmartDevice { constructor(ip) { this.ip = ip; } // Scan for lightbulbs on your network static scan(filter, broadcast = '255.255.255.255') { const emitter = new EventEmitter(); const client = dgram.createSocket({ type: 'udp4', reuseAddr: true }); client.bind(9998, undefined, () => { client.setBroadcast(true); const msgBuf = TPLSmartDevice.encrypt(Buffer.from('{"system":{"get_sysinfo":{}}}')); client.send(msgBuf, 0, msgBuf.length, 9999, broadcast); }); client.on('message', (msg, rinfo) => { const decryptedMsg = this.decrypt(msg).toString('ascii'); const jsonMsg = JSON.parse(decryptedMsg); const sysinfo = jsonMsg.system.get_sysinfo; if (filter && sysinfo.mic_type !== filter) { return; } const light = new TPLSmartDevice(rinfo.address); light._info = rinfo; light._sysinfo = sysinfo; light.host = rinfo.address; light.port = rinfo.port; light.name = sysinfo.alias; light.deviceId = sysinfo.deviceId; emitter.emit('light', light); }); emitter.stop = () => client.close(); return emitter; } // Send a message to a lightbulb (for RAW JS message objects) send(msg) { return new Promise((resolve, reject) => { if (!this.ip) { return reject(new Error('IP not set.')); } const client = dgram.createSocket('udp4'); const message = this.encrypt(Buffer.from(JSON.stringify(msg))); client.send(message, 0, message.length, 9999, this.ip, (err, bytes) => { if (err) { return reject(err); } client.on('message', msg => { resolve(JSON.parse(this.decrypt(msg).toString())); client.close(); }); }); }); } // TODO: wifi needs more testing. it seems very broken. // Scans the wifi networks in range of the device async listwifi() { var _r1$netif, _r1$netif$get_scaninf; const r1 = await this.send({ netif: { get_scaninfo: { refresh: 1 } } }); if (r1 != null && (_r1$netif = r1.netif) != null && (_r1$netif$get_scaninf = _r1$netif.get_scaninfo) != null && _r1$netif$get_scaninf.ap_list) { return r1.netif.get_scaninfo.ap_list; } else { var _r2$smartlifeIotCom, _r2$smartlifeIotCom$g; // on fail, try older message-format const r2 = await this.send({ 'smartlife.iot.common.softaponboarding': { get_scaninfo: { refresh: 1 } } }); if (r2 && (_r2$smartlifeIotCom = r2['smartlife.iot.common.softaponboarding']) != null && (_r2$smartlifeIotCom$g = _r2$smartlifeIotCom.get_scaninfo) != null && _r2$smartlifeIotCom$g.ap_list) { return r2['smartlife.iot.common.softaponboarding'].get_scaninfo.ap_list; } } } // Connects the device to the access point in the parameters async connectwifi(ssid, password, keyType = 1, cypherType = 0) { var _r1$netif2, _r1$netif2$set_stainf; const r1 = await this.send({ netif: { set_stainfo: { cypher_type: cypherType, key_type: keyType, password, ssid } } }); if ((r1 == null ? void 0 : (_r1$netif2 = r1.netif) == null ? void 0 : (_r1$netif2$set_stainf = _r1$netif2.set_stainfo) == null ? void 0 : _r1$netif2$set_stainf.err_code) === 0) { return true; } // on fail, try older message-format const r2 = await this.send({ 'smartlife.iot.common.softaponboarding': { set_stainfo: { cypher_type: cypherType, key_type: keyType, password, ssid } } }); if (r2['smartlife.iot.common.softaponboarding'] && r2['smartlife.iot.common.softaponboarding'].err_msg) { throw new Error(r2['smartlife.iot.common.softaponboarding'].err_msg); } else { return true; } } // Get info about the TPLSmartDevice async info() { const r = await this.send({ system: { get_sysinfo: {} } }); return r.system.get_sysinfo; } // Set power-state of lightbulb async power(powerState = true, transition = 0, options = {}) { const info = await this.info(); if (typeof info.relay_state !== 'undefined') { return this.send({ system: { set_relay_state: { state: powerState ? 1 : 0 } } }); } else { const r = await this.send({ 'smartlife.iot.smartbulb.lightingservice': { transition_light_state: _extends({ ignore_default: 1, on_off: powerState ? 1 : 0, transition_period: transition }, options) } }); return r['smartlife.iot.smartbulb.lightingservice'].transition_light_state; } } // Set led-state of lightbulb led(ledState = true) { return this.send({ system: { set_led_off: { off: ledState ? 0 : 1 } } }); } // Set the name of lightbulb async name(newAlias) { const info = await this.info(); return typeof info.dev_name !== 'undefined' ? this.send({ system: { set_dev_alias: { alias: newAlias } } }) : this.send({ 'smartlife.iot.common.system': { set_dev_alias: { alias: newAlias } } }); } // Get schedule info async daystat(month, year) { const now = new Date(); month = month || now.getMonth() + 1; year = year || now.getFullYear(); const r = await this.send({ 'smartlife.iot.common.schedule': { get_daystat: { month: month, year: year } } }); return r['smartlife.iot.common.schedule'].get_daystat; } // Get cloud info from bulb async cloud() { const r = await this.send({ 'smartlife.iot.common.cloud': { get_info: {} } }); return r['smartlife.iot.common.cloud'].get_info; } // Get schedule from bulb async schedule() { const r = await this.send({ 'smartlife.iot.common.schedule': { get_rules: {} } }); return r['smartlife.iot.common.schedule'].get_rules; } // Get operational details from bulb details() { return this.send({ 'smartlife.iot.smartbulb.lightingservice': { get_light_details: {} } }); } // Reboot the device reboot() { return this.send({ 'smartlife.iot.common.system': { reboot: { delay: 1 } } }); } // Badly encrypt message in format bulbs use static encrypt(buffer, key = 0xAB) { for (let i = 0; i < buffer.length; i++) { const c = buffer[i]; buffer[i] = c ^ key; key = buffer[i]; } return buffer; } encrypt(buffer, key) { return TPLSmartDevice.encrypt(buffer, key); } // Badly decrypt message from format bulbs use static decrypt(buffer, key = 0xAB) { for (let i = 0; i < buffer.length; i++) { const c = buffer[i]; buffer[i] = c ^ key; key = c; } return buffer; } decrypt(buffer, key) { return TPLSmartDevice.decrypt(buffer, key); } } export { TPLSmartDevice as default }; //# sourceMappingURL=tplink-lightbulb.module.js.map