UNPKG

utra-bridge

Version:

Bridge Zwave device into Apple HomeKit protocol.

403 lines (328 loc) 10.8 kB
'use strict'; const Operater = require('./Operater.js'); const debug = require('debug')('Gateway'); const events = require('events'); const hap_nodejs = require('hap-nodejs'); const Accessory = hap_nodejs.Accessory; const Service = hap_nodejs.Service; const Characteristic = hap_nodejs.Characteristic; const uuid = hap_nodejs.uuid; const child_process = require('child_process'); const parser = require('xml2json').toJson; const Bridge = hap_nodejs.Bridge; module.exports = Gateway; function Gateway(acc, pwd, ip) { this.setting = { acc: acc, pwd: pwd, ip: ip }; this.emitter = new events.EventEmitter(); let gateway = this; this.emitter.on('find_node', function (node) { debug('Find ' + node.product); switch (node.product) { case "RGB color LED Dimmer": gateway.bridgeLightDimmer(node); break; case 'ZL7435 In-Wall Switch, 2 Relays': gateway.bridgeLightSwitch(node); break; case "Wall Switch x3": gateway.bridgeLightSwitch(node); break; case "MiLocks": gateway.bridgeDoorLock(node); break; case "Wireless Electronic Deadbolt Door Lock(Real Time Version)": gateway.bridgeDoorLock(node); break; case 'ZW4102 Curtain Control Module(Relay Output)': gateway.bridgeCurtain(node); break; case 'ZD2201-5 4-in-1 Multi Sensor': gateway.bridgeSensor(node); break; default: debug('UtraBridge not verified product: ['+node.product+'] Ignored.'); } }); this.emitter.on('find_scene', function (scene) { gateway.bridgeScene(scene); }); var bridge; this.emitter.on('new_accessory', function (accessory) { debug('add new accessory to bridge'); bridge.addBridgedAccessory(accessory); gateway.Bridge = bridge; }); this.emitter.once('configure-finish', function (bridge) { if (gateway.port && gateway.pincode) { bridge.publish({ port: gateway.port, pincode: gateway.pincode, username: bridge.username, category: Accessory.Categories.BRIDGE }); gateway.emitter.emit('pusblish-success'); } }); this.getInfrastructure(function (err, data) { if (!err) { let model = data.node_detail.model; let mac = data.node_detail.gateway_id; let version = data.node_detail.version; let name = model + " " + mac; debug('Make Bridge. '); debug('Mac address: ' + mac); debug('Mode: ' + model); debug('version: ' + version); bridge = new Bridge(name, uuid.generate(mac)); bridge.username = mac.toUpperCase().match(/.{1,2}/g).join(':'); bridge.on('identify', function (paired, callback) { debug("Gateway "+ name +' identified.'); callback(); }); bridge.getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Name, 'UltraBridge') .setCharacteristic(Characteristic.Manufacturer, "Avadesign Technology Corp.") .setCharacteristic(Characteristic.Model, model) .setCharacteristic(Characteristic.SerialNumber, mac) .setCharacteristic(Characteristic.FirmwareRevision, version); data.node_detail.node.forEach(function (node) { gateway.emitter.emit('find_node', node); }); gateway.getScenes(function (err, data) { if (!err && data.sceneid instanceof Array) { debug(data.sceneid); data.scenes.sceneid.forEach(function (scene) { gateway.emitter.emit('find_scene', scene); }); } }); gateway.emitter.emit('configure-finish', bridge); }else{ debug(err); } }); } Gateway.prototype.getInfrastructure = function (callback) { debug('Getting Infrastructure'); let cmd = 'curl --digest -u ' + this.setting.acc + ":"+ this.setting.pwd +' '+ this.setting.ip + ':5000/node_detail.cgi -d "fun=load"'; child_process.exec(cmd, function (err, stdout) { if (stdout === "") err = new Error('Gateway has no response. This may casuse by wrong acc or password.'); let json = parser(stdout, { object:true }); callback(err, json); }); } Gateway.prototype.getScenes = function (callback) { let cmd = 'curl --digest -u ' + this.setting.acc + ":"+ this.setting.pwd +' '+ this.setting.ip + ':5000/scenepost.html -d "fun=load"'; child_process.exec(cmd, function (err, stdout) { let json = parser(stdout, { object:true }); callback(err, json); }); } Gateway.prototype.publish = function (pincode, port, callback) { this.pincode = pincode; this.port = port; this.emitter.once('pusblish-success', function () { let err = null; callback(err); }); } Gateway.prototype.destroy = function () { if (this.Bridge) { console.log('Terminating bridge service.'); this.Bridge.destroy(); this.emitter.removeAllListeners(); }else{ throw Error('Bridge not exists.'); } } /* * * */ Gateway.prototype.bridgeScene = function (sceneid) { let name = sceneid.label + sceneid.id || 'Scene ' + sceneid.id; let accessory = new Accessory(name, uuid.generate(sceneid.id + name)); let gateway = this; accessory.on('identify', function(paired, callback) { debug(name + " identified!!"); callback(); }); let service = accessory.addService(Service.Switch, name); service .getCharacteristic(Characteristic.On) .on('get', function (callback) { callback(null, false); // callback(null, service.getCharacteristic(Characteristic.On).value); }) .on('set', function (on, callback){ let cmd = 'curl --digest -u ' + gateway.setting.acc + ":"+ gateway.setting.pwd +' '+ gateway.setting.ip + ':5000/scenepost.html -d "fun=execute&id='+ sceneid.id +'"'; child_process.exec(cmd, function (err) { service.updateCharacteristic(Characteristic.On, true); callback(err); }); }); this.emitter.emit('new_accessory', accessory); } Gateway.prototype.bridgeSensor = function (node) { let name = node.name === "" ? "未命名裝置" : node.name; let accessory = new Accessory(name, uuid.generate(node.product + node.id)); let gateway = this; accessory .getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, node.manufacturer) .setCharacteristic(Characteristic.Model, node.product); accessory.on('identify', function(paired, callback) { debug(name + " identified!!"); callback(); }); accessory.category = Accessory.Categories.TEMPERATURE_SENSOR; let operater = new Operater(this, accessory); node.value.forEach(function (value) { switch (value.label) { case 'Temperature': operater.TemperatureSensor(node).CurrentTemperature(value); break; case 'Luminance': operater.LightSensor(node).CurrentAmbientLightLevel(value); break; case 'Relative Humidity': operater.HumiditySensor(node).CurrentRelativeHumidity(value); break; case 'Battery Level': operater.Battery(node).BatteryLevel(value); break; case 'Mode': operater.TemperatureSensor(node).StatusActive(value); break; } }); this.emitter.emit('new_accessory', accessory); } Gateway.prototype.bridgeCurtain = function (node) { let name = node.name === "" ? "未命名裝置" : node.name; let accessory = new Accessory(name, uuid.generate(node.product + node.id)); let gateway = this; accessory .getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, node.manufacturer) .setCharacteristic(Characteristic.Model, node.product); accessory.on('identify', function(paired, callback) { debug(name + " identified!!"); callback(); }); accessory.category = Accessory.Categories.WINDOW_COVERING; let operater = new Operater(this, accessory); node.value.forEach(function (value) { switch (value.label) { case 'Level': operater.WindowCovering(node).CurrentPosition(value); operater.WindowCovering(node).TargetPosition(value); } }); this.emitter.emit('new_accessory', accessory); } Gateway.prototype.bridgeLightDimmer = function (node) { let name = node.name === "" ? "未命名裝置" : node.name; let accessory = new Accessory(name, uuid.generate(node.product + node.id)); accessory .getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, node.manufacturer) .setCharacteristic(Characteristic.Model, node.product); accessory.on('identify', function(paired, callback) { debug(name + " identified!!"); callback(); }); let gateway = this; accessory.category = Accessory.Categories.LIGHTBULB; let operater = new Operater(this, accessory); node.value.forEach(function (value) { switch (value.label) { case "Level": operater.Lightbulb(node) .Brightness(value); break; /* case "Bright": operater.Lightbulb(node) .On(value); break; */ case "RGB Color": operater.Lightbulb(node) .Hue(value); operater.Lightbulb(node) .Saturation(value); operater.Lightbulb(node) .ColorTemperature(value); break; } }) this.emitter.emit('new_accessory', accessory); } Gateway.prototype.bridgeLightSwitch = function (node) { let name = node.name === "" ? "未命名裝置" : node.name; let accessory = new Accessory(name, uuid.generate(node.product + node.id)); let gateway = this; debug('Make light switch'); accessory .getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, node.manufacturer) .setCharacteristic(Characteristic.Model, node.product); accessory.on('identify', function(paired, callback) { debug(name + " identified!!"); callback(); }); accessory.category = Accessory.Categories.LIGHTBULB; let operater = new Operater(this, accessory); node.value.forEach((value) => { switch (value.label) { case 'Switch': /*operater.Lightbulb(node).On(value, function (result, callback) { callback(null, result==='True'); });*/ operater.Lightbulb(node).On(value); break; } }); this.emitter.emit('new_accessory', accessory); } Gateway.prototype.bridgeDoorLock = function (node) { let name = node.name === "" ? "未命名裝置" : node.name; let accessory = new Accessory(name, uuid.generate(node.product + node.id)); accessory .getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, node.manufacturer) .setCharacteristic(Characteristic.Model, node.product); accessory.on('identify', function(paired, callback) { debug(name + " identified!!"); callback(); }); accessory.category = Accessory.Categories.DOOR_LOCK; let gateway = this; let operater = new Operater(this, accessory); node.value.forEach((value) => { switch (value.label) { case 'Mode': operater.LockMechanism(node).LockCurrentState(value); operater.LockMechanism(node).LockTargetState(value); break; case 'Battery Level': operater.Battery(node).BatteryLevel(value); break; } }); this.emitter.emit('new_accessory', accessory); } /* */ Gateway.prototype.setValue = function () { } Gateway.prototype.getValue = function () { }