diff --git a/package.json b/package.json index 3a3ca74..a55f737 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#4650e771f356a220868d873d16564a6be6603ff7", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", + "isy-js": "", "lifx-api": "^1.0.1", "lifx": "git+https://github.com/magicmonkey/lifxjs.git", "mdns": "^2.2.4", diff --git a/platforms/isy-js.js b/platforms/isy-js.js new file mode 100644 index 0000000..37d515b --- /dev/null +++ b/platforms/isy-js.js @@ -0,0 +1,674 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var isy = require('isy-js'); +var Service = require("HAP-NodeJS").Service; +var Characteristic = require("HAP-NodeJS").Characteristic; +var inherits = require('util').inherits; + +var deviceMap = {}; + +function ISYChangeHandler(isy,device) { + var deviceToUpdate = deviceMap[device.address]; + if(deviceToUpdate != null) { + deviceToUpdate.handleExternalChange(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// PLATFORM + +function ISYPlatform(log,config) { + this.log = log; + this.config = config; + this.host = config.host; + this.username = config.username; + this.password = config.password; + this.elkEnabled = config.elkEnabled; + this.isy = new isy.ISY(this.host, this.username,this.password, config.elkEnabled, ISYChangeHandler); +} + +ISYPlatform.prototype.shouldIgnore = function(device) { + var deviceAddress = device.address; + var deviceName = device.name; + for(var index = 0; index < this.config.ignoreDevices.length; index++) { + var rule = this.config.ignoreDevices[index]; + if(rule.nameContains != "") { + if(deviceName.indexOf(rule.nameContains) == -1) { + continue; + } + } + if(rule.lastAddressDigit != "") { + if(deviceAddress.indexOf(rule.lastAddressDigit,deviceAddress.length-2) == -1) { + continue; + } + } + if(rule.address != "") { + if(deviceAddress != rule.address) { + continue; + } + } + console.log("@@@@@@ Ignoring device: "+deviceName+" ["+deviceAddress+"] because of rule ["+rule.nameContains+"] ["+rule.lastAddressDigit+"] ["+rule.address+"]"); + return true; + + } + return false; +} + +ISYPlatform.prototype.accessories = function(callback) { + var that = this; + this.isy.initialize(function() { + var results = []; + var deviceList = that.isy.getDeviceList(); + for(var index = 0; index < deviceList.length; index++) { + var device = deviceList[index]; + var homeKitDevice = null; + if(!that.shouldIgnore(device)) { + + if(device.deviceType == that.isy.DEVICE_TYPE_LIGHT || device.deviceType == that.isy.DEVICE_TYPE_DIMMABLE_LIGHT) { + homeKitDevice = new ISYLightAccessory(that.log,device); + } else if(device.deviceType == that.isy.DEVICE_TYPE_LOCK || device.deviceType == that.isy.DEVICE_TYPE_SECURE_LOCK) { + homeKitDevice = new ISYLockAccessory(that.log,device); + } else if(device.deviceType == that.isy.DEVICE_TYPE_OUTLET) { + homeKitDevice = new ISYOutletAccessory(that.log,device); + } else if(device.deviceType == that.isy.DEVICE_TYPE_FAN) { + homeKitDevice = new ISYFanAccessory(that.log,device); + } else if(device.deviceType == that.isy.DEVICE_TYPE_DOOR_WINDOW_SENSOR) { + homeKitDevice = new ISYDoorWindowSensorAccessory(that.log,device); + } else if(device.deviceType == that.isy.DEVICE_TYPE_ALARM_DOOR_WINDOW_SENSOR) { + homeKitDevice = new ISYDoorWindowSensorAccessory(that.log,device); + } else if(device.deviceType == that.isy.DEVICE_TYPE_ALARM_PANEL) { + homeKitDevice = new ISYElkAlarmPanelAccessory(that.log,device); + } + if(homeKitDevice != null) { + deviceMap[device.address] = homeKitDevice; + results.push(homeKitDevice); + } + } + } + if(that.isy.elkEnabled) { + var panelDevice = that.isy.getElkAlarmPanel(); + var panelDeviceHK = new ISYElkAlarmPanelAccessory(that.log,panelDevice); + deviceMap[panelDevice.address] = panelDeviceHK; + results.push(panelDeviceHK); + } + console.log("Filtered device has: "+results.length+" devices"); + callback(results); + }); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// FANS + +function ISYFanAccessory(log,device) { + this.log = log; + this.device = device; + this.address = device.address; + this.name = device.name; +} + +ISYFanAccessory.prototype.identify = function(callback) { + // Do the identify action + callback(); +} + +ISYFanAccessory.prototype.translateFanSpeedToHK = function(fanSpeed) { + if(fanSpeed == "Off") { + return 0; + } else if(fanSpeed == "Low") { + return 32; + } else if(fanSpeed == "Medium") { + return 67; + } else if(fanSpeed == "High") { + return 100; + } else { + this.log("!!!! ERROR: Unknown fan speed: "+fanSpeed); + return 0; + } +} + +ISYFanAccessory.prototype.translateHKToFanSpeed = function(fanStateHK) { + if(fanStateHK == 0) { + return "Off"; + } else if(fanStateHK > 0 && fanStateHK <=32) { + return "Low"; + } else if(fanStateHK > 33 && fanStateHK <= 67) { + return "Medium"; + } else if(fanStateHK > 67) { + return "High"; + } else { + this.log("!!!!! ERROR: Unknown fan state!"); + return "Off"; + } +} + +ISYFanAccessory.prototype.getFanRotationSpeed = function(callback) { + callback(null,this.translateFanSpeedToHK(this.device.getCurrentFanState())); +} + +ISYFanAccessory.prototype.setFanRotationSpeed = function(fanStateHK,callback) { + var newFanState = this.translateHKToFanSpeed(fanStateHK); + this.log("Sending command to set fan state to: "+newFanState); + if(newFanState != this.device.getCurrentFanState()) { + this.device.sendFanCommand(newFanState, function(result) { + callback(); + }); + } else { + this.log("Fan command does not change actual speed"); + callback(); + } +} + + +ISYFanAccessory.prototype.getIsFanOn = function() { + return (this.device.getCurrentFanState() != "Off"); +} + +ISYFanAccessory.prototype.getFanOnState = function(callback) { + callback(null,this.getIsFanOn()); +} + +ISYFanAccessory.prototype.setFanOnState = function(onState,callback) { + if(onState != this.getIsFanOn()) { + if(onState) { + this.setFanRotationSpeed(this.translateFanSpeedToHK("Medium"), callback); + } else { + this.setFanRotationSpeed(this.translateFanSpeedToHK("Off"), callback); + } + } else { + this.log("Fan command does not change actual state"); + callback(); + } +} + + +ISYFanAccessory.prototype.handleExternalChange = function() { + this.fanService + .setCharacteristic(Characteristic.On, this.getIsFanOn()); + + this.fanService + .setCharacteristic(Characteristic.RotationSpeed, this.translateFanSpeedToHK(this.device.getCurrentFanState())); +} + +ISYFanAccessory.prototype.getServices = function() { + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "SmartHome") + .setCharacteristic(Characteristic.Model, this.device.deviceFriendlyName) + .setCharacteristic(Characteristic.SerialNumber, this.device.address); + + var fanService = new Service.Fan(); + + this.fanService = fanService; + this.informationService = informationService; + + fanService + .getCharacteristic(Characteristic.On) + .on('set', this.setFanOnState.bind(this)); + + fanService + .getCharacteristic(Characteristic.On) + .on('get', this.getFanOnState.bind(this)); + + fanService + .addCharacteristic(new Characteristic.RotationSpeed()) + .on('get', this.getFanRotationSpeed.bind(this)); + + fanService + .getCharacteristic(Characteristic.RotationSpeed) + .on('set', this.setFanRotationSpeed.bind(this)); + + return [informationService, fanService]; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// OUTLETS + +function ISYOutletAccessory(log,device) { + this.log = log; + this.device = device; + this.address = device.address; + this.name = device.name; +} + +ISYOutletAccessory.prototype.identify = function(callback) { + // Do the identify action + callback(); +} + +ISYOutletAccessory.prototype.setOutletState = function(outletState,callback) { + this.log("Sending command to set outlet state to: "+outletState); + if(outletState != this.device.getCurrentOutletState()) { + this.device.sendOutletCommand(outletState, function(result) { + callback(); + }); + } else { + callback(); + } +} + +ISYOutletAccessory.prototype.getOutletState = function(callback) { + callback(null,this.device.getCurrentOutletState()); +} + +ISYOutletAccessory.prototype.getOutletInUseState = function(callback) { + callback(null, true); +} + +ISYOutletAccessory.prototype.handleExternalChange = function() { + this.outletService + .setCharacteristic(Characteristic.On, this.device.getCurrentOutletState()); +} + +ISYOutletAccessory.prototype.getServices = function() { + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "SmartHome") + .setCharacteristic(Characteristic.Model, this.device.deviceFriendlyName) + .setCharacteristic(Characteristic.SerialNumber, this.device.address); + + var outletService = new Service.Outlet(); + + this.outletService = outletService; + this.informationService = informationService; + + outletService + .getCharacteristic(Characteristic.On) + .on('set', this.setOutletState.bind(this)); + + outletService + .getCharacteristic(Characteristic.On) + .on('get', this.getOutletState.bind(this)); + + outletService + .getCharacteristic(Characteristic.OutletInUse) + .on('get', this.getOutletInUseState.bind(this)); + + return [informationService, outletService]; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// LOCKS + +function ISYLockAccessory(log,device) { + this.log = log; + this.device = device; + this.address = device.address; + this.name = device.name; +} + +ISYLockAccessory.prototype.identify = function(callback) { + callback(); +} + +ISYLockAccessory.prototype.setTargetLockState = function(lockState,callback) { + this.log("Sending command to set lock state to: "+lockState); + if(lockState != this.getDeviceCurrentStateAsHK()) { + var targetLockValue = (lockState == 0) ? false : true; + this.device.sendLockCommand(targetLockValue, function(result) { + callback(); + }); + } else { + callback(); + } +} + +ISYLockAccessory.prototype.getDeviceCurrentStateAsHK = function() { + return (this.device.getCurrentLockState() ? 1 : 0); +} + +ISYLockAccessory.prototype.getLockCurrentState = function(callback) { + callback(null, this.getDeviceCurrentStateAsHK()); +} + +ISYLockAccessory.prototype.getTargetLockState = function(callback) { + this.getLockCurrentState(callback); +} + +ISYLockAccessory.prototype.handleExternalChange = function() { + this.lockService + .setCharacteristic(Characteristic.LockTargetState, this.getDeviceCurrentStateAsHK()); + this.lockService + .setCharacteristic(Characteristic.LockCurrentState, this.getDeviceCurrentStateAsHK()); +} + +ISYLockAccessory.prototype.getServices = function() { + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "SmartHome") + .setCharacteristic(Characteristic.Model, this.device.deviceFriendlyName) + .setCharacteristic(Characteristic.SerialNumber, this.device.address); + + var lockMechanismService = new Service.LockMechanism(); + + this.lockService = lockMechanismService; + this.informationService = informationService; + + lockMechanismService + .getCharacteristic(Characteristic.LockTargetState) + .on('set', this.setTargetLockState.bind(this)); + + lockMechanismService + .getCharacteristic(Characteristic.LockTargetState) + .on('get', this.getTargetLockState.bind(this)); + + lockMechanismService + .getCharacteristic(Characteristic.LockCurrentState) + .on('get', this.getLockCurrentState.bind(this)); + + return [informationService, lockMechanismService]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// LIGHTS + +function ISYLightAccessory(log,device) { + this.log = log; + this.device = device; + this.address = device.address; + this.name = device.name; + this.dimmable = (this.device.deviceType == "DimmableLight"); +} + +ISYLightAccessory.prototype.identify = function(callback) { + this.device.sendLightCommand(true, function(result) { + this.device.sendLightCommand(false, function(result) { + callback(); + }); + }); +} + +ISYLightAccessory.prototype.setPowerState = function(powerOn,callback) { + this.log("=== Setting powerstate to %s", powerOn); + if(powerOn != this.device.getCurrentLightState()) { + this.log("+++ Changing powerstate to "+powerOn); + this.device.sendLightCommand(powerOn, function(result) { + callback(); + }); + } else { + this.log("--- Ignoring redundant setPowerState"); + callback(); + } +} + +ISYLightAccessory.prototype.handleExternalChange = function() { + this.log("=== Handling external change for light"); + this.lightService + .setCharacteristic(Characteristic.On, this.device.getCurrentLightState()); + if(this.device.deviceType == this.device.isy.DEVICE_TYPE_DIMMABLE_LIGHT) { + this.lightService + .setCharacteristic(Characteristic.Brightness, this.device.getCurrentLightDimState() ); + } +} + +ISYLightAccessory.prototype.getPowerState = function(callback) { + callback(null,this.device.getCurrentLightState()); +} + +ISYLightAccessory.prototype.setBrightness = function(level,callback) { + this.log("Setting brightness to %s", level); + if(level != this.device.getCurrentLightDimState()) { + this.log("+++ Changing Brightness to "+level); + this.device.sendLightDimCommand(level, function(result) { + callback(); + }); + } else { + this.log("--- Ignoring redundant setBrightness"); + callback(); + } +} + +ISYLightAccessory.prototype.getBrightness = function(callback) { + callback(null,this.device.getCurrentLightDimState()); +} + +ISYLightAccessory.prototype.getServices = function() { + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "SmartHome") + .setCharacteristic(Characteristic.Model, this.device.deviceFriendlyName) + .setCharacteristic(Characteristic.SerialNumber, this.device.address); + + var lightBulbService = new Service.Lightbulb(); + + this.informationService = informationService; + this.lightService = lightBulbService; + + lightBulbService + .getCharacteristic(Characteristic.On) + .on('set', this.setPowerState.bind(this)); + + lightBulbService + .getCharacteristic(Characteristic.On) + .on('get', this.getPowerState.bind(this)); + + if(this.dimmable) { + lightBulbService + .addCharacteristic(new Characteristic.Brightness()) + .on('get', this.getBrightness.bind(this)); + + lightBulbService + .getCharacteristic(Characteristic.Brightness) + .on('set', this.setBrightness.bind(this)); + } + + return [informationService, lightBulbService]; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// CONTACT SENSOR + +function ISYDoorWindowSensorAccessory(log,device) { + this.log = log; + this.device = device; + this.address = device.address; + this.name = device.name; + this.doorWindowState = false; +} + +ISYDoorWindowSensorAccessory.prototype.identify = function(callback) { + // Do the identify action + callback(); +} + +ISYDoorWindowSensorAccessory.prototype.translateCurrentDoorWindowState = function() { + return (this.device.getCurrentDoorWindowState()) ? Characteristic.ContactSensorState.CONTACT_NOT_DETECTED : Characteristic.ContactSensorState.CONTACT_DETECTED; +} + +ISYDoorWindowSensorAccessory.prototype.getCurrentDoorWindowState = function(callback) { + callback(null,this.translateCurrentDoorWindowState()); +} + +ISYDoorWindowSensorAccessory.prototype.handleExternalChange = function() { + this.sensorService + .setCharacteristic(Characteristic.ContactSensorState, this.translateCurrentDoorWindowState()); +} + +ISYDoorWindowSensorAccessory.prototype.getServices = function() { + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "SmartHome") + .setCharacteristic(Characteristic.Model, this.device.deviceFriendlyName) + .setCharacteristic(Characteristic.SerialNumber, this.device.address); + + var sensorService = new Service.ContactSensor(); + + this.sensorService = sensorService; + this.informationService = informationService; + + sensorService + .getCharacteristic(Characteristic.ContactSensorState) + .on('get', this.getCurrentDoorWindowState.bind(this)); + + return [informationService, sensorService]; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// ELK SENSOR PANEL + +function ISYElkAlarmPanelAccessory(log,device) { + this.log = log; + this.device = device; + this.address = device.address; + this.name = device.name; +} + +ISYElkAlarmPanelAccessory.prototype.identify = function(callback) { + callback(); +} + +ISYElkAlarmPanelAccessory.prototype.setAlarmTargetState = function(targetStateHK,callback) { + this.log("Sending command to set alarm panel state to: "+targetStateHK); + var targetState = this.translateHKToAlarmTargetState(targetState); + if(this.alarmTargetState != targetState) { + this.device.sendSetAlarmModeCommand(targetState, function(result) { + callback(); + }); + } else { + callback(); + } +} + +////// Current State + +/* +ELKAlarmPanelDevice.prototype.ALARM_STATE_NOT_READY_TO_ARM = 0; +ELKAlarmPanelDevice.prototype.ALARM_STATE_READY_TO_ARM = 1; +ELKAlarmPanelDevice.prototype.ALARM_STATE_READY_TO_ARM_VIOLATION = 2; +ELKAlarmPanelDevice.prototype.ALARM_STATE_ARMED_WITH_TIMER = 3; +ELKAlarmPanelDevice.prototype.ALARM_STATE_ARMED_FULLY = 4; +ELKAlarmPanelDevice.prototype.ALARM_STATE_FORCE_ARMED_VIOLATION = 5; +ELKAlarmPanelDevice.prototype.ALARM_STATE_ARMED_WITH_BYPASS = 6; +*/ + +/* +ELKAlarmPanelDevice.prototype.ALARM_TRIP_STATE_DISARMED = 0; +ELKAlarmPanelDevice.prototype.ALARM_TRIP_STATE_EXIT_DELAY = 1; +ELKAlarmPanelDevice.prototype.ALARM_TRIP_STATE_TRIPPED = 2; +*/ + +/* +Characteristic.SecuritySystemCurrentState.STAY_ARM = 0; +Characteristic.SecuritySystemCurrentState.AWAY_ARM = 1; +Characteristic.SecuritySystemCurrentState.NIGHT_ARM = 2; +Characteristic.SecuritySystemCurrentState.DISARMED = 3; +Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED = 4; +*/ + +ISYElkAlarmPanelAccessory.prototype.translateAlarmCurrentStateToHK = function() { + var tripState = this.device.getAlarmTripState(); + if(tripState == this.device.ALARM_TRIP_STATE_DISARMED || tripState == this.device.ALARM_TRIP_STATE_EXIT_DELAY) { + return Characteristic.SecuritySystemCurrentState.DISARMED; + } else if(tripState ==this.device.ALARM_TRIP_STATE_TRIPPED) { + return Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED; + } else { + var sourceAlarmState = this.device.getAlarmMode(); + if(sourceAlarmState == this.device.ALARM_MODE_STAY || sourceAlarmState == this.device.ALARM_MODE_STAY_INSTANT ) { + return Characteristic.SecuritySystemCurrentState.STAY_ARM; + } else if(sourceAlarmState == this.device.ALARM_MODE_AWAY || sourceAlarmState == this.device.ALARM_MODE_VACATION) { + return Characteristic.SecuritySystemCurrentState.AWAY_ARM; + } else if(sourceAlarmState == this.device.ALARM_MODE_NIGHT || sourceAlarmState == this.device.ALARM_MODE_NIGHT_INSTANT) { + return Characteristic.SecuritySystemCurrentState.NIGHT_ARM; + } else { + return Characteristic.SecuritySystemCurrentState.DISARM; + } + } +} + +////// Target Mode + +/* +ELKAlarmPanelDevice.prototype.ALARM_MODE_DISARMED = 0; +ELKAlarmPanelDevice.prototype.ALARM_MODE_AWAY = 1; +ELKAlarmPanelDevice.prototype.ALARM_MODE_STAY = 2; +ELKAlarmPanelDevice.prototype.ALARM_MODE_STAY_INSTANT = 3; +ELKAlarmPanelDevice.prototype.ALARM_MODE_NIGHT = 4; +ELKAlarmPanelDevice.prototype.ALARM_MODE_NIGHT_INSTANT = 5; +ELKAlarmPanelDevice.prototype.ALARM_MODE_VACATION = 6; +*/ + +/* +Characteristic.SecuritySystemTargetState.STAY_ARM = 0; +Characteristic.SecuritySystemTargetState.AWAY_ARM = 1; +Characteristic.SecuritySystemTargetState.NIGHT_ARM = 2; +Characteristic.SecuritySystemTargetState.DISARM = 3; +*/ + + +ISYElkAlarmPanelAccessory.prototype.translateAlarmTargetStateToHK = function() { + var sourceAlarmState = this.device.getAlarmMode(); + if(sourceAlarmState == this.device.ALARM_MODE_STAY || sourceAlarmState == this.device.ALARM_MODE_STAY_INSTANT ) { + return Characteristic.SecuritySystemTargetState.STAY_ARM; + } else if(sourceAlarmState == this.device.ALARM_MODE_AWAY || sourceAlarmState == this.device.ALARM_MODE_VACATION) { + return Characteristic.SecuritySystemTargetState.AWAY_ARM; + } else if(sourceAlarmState == this.device.ALARM_MODE_NIGHT || sourceAlarmState == this.device.ALARM_MODE_NIGHT_INSTANT) { + return Characteristic.SecuritySystemTargetState.NIGHT_ARM; + } else { + return Characteristic.SecuritySystemTargetState.DISARM; + } +} + +ISYElkAlarmPanelAccessory.prototype.translateHKToAlarmTargetState = function(state) { + if(state == Characteristic.SecuritySystemTargetState.STAY_ARM) { + return this.device.ALARM_MODE_STAY; + } else if(state == Characteristic.SecuritySystemTargetState.AWAY_ARM) { + return this.device.ALARM_MODE_AWAY; + } else if(state == Characteristic.SecuritySystemTargetState.NIGHT_ARM) { + return this.device.NIGHT_ARM; + } else { + return this.device.DISARM; + } +} + +ISYElkAlarmPanelAccessory.prototype.getAlarmTargetState = function(callback) { + callback(null,this.translateAlarmTargetStateToHK()); +} + +ISYElkAlarmPanelAccessory.prototype.getAlarmCurrentState = function(callback) { + callback(null,this.translateAlarmCurrentStateToHK()); +} + +ISYElkAlarmPanelAccessory.prototype.handleExternalChange = function() { + this.alarmPanelService + .setCharacteristic(Characteristic.SecuritySystemTargetState, this.translateAlarmTargetStateToHK()); + this.alarmPanelService + .setCharacteristic(Characteristic.SecuritySystemCurrentState, this.translateAlarmCurrentStateToHK()); +} + +ISYElkAlarmPanelAccessory.prototype.getServices = function() { + var informationService = new Service.AccessoryInformation(); + + informationService + .setCharacteristic(Characteristic.Manufacturer, "SmartHome") + .setCharacteristic(Characteristic.Model, this.device.deviceFriendlyName) + .setCharacteristic(Characteristic.SerialNumber, this.device.address); + + var alarmPanelService = new Service.SecuritySystem(); + + this.alarmPanelService = alarmPanelService; + this.informationService = informationService; + + alarmPanelService + .getCharacteristic(Characteristic.SecuritySystemTargetState) + .on('set', this.setAlarmTargetState.bind(this)); + + alarmPanelService + .getCharacteristic(Characteristic.SecuritySystemTargetState) + .on('get', this.getAlarmTargetState.bind(this)); + + alarmPanelService + .getCharacteristic(Characteristic.SecuritySystemCurrentState) + .on('get', this.getAlarmCurrentState.bind(this)); + + return [informationService, alarmPanelService]; +} + +module.exports.platform = ISYPlatform; +module.exports.accessory = ISYFanAccessory; +module.exports.accessory = ISYLightAccessory; +module.exports.accessory = ISYLockAccessory; +module.exports.accessory = ISYOutletAccessory; +module.exports.accessory = ISYDoorWindowSensorAccessory;