Adding isy-js based ISY platform support

This commit is contained in:
rodtoll
2015-10-11 08:14:48 -07:00
parent fd420c7795
commit e4fa276de2
2 changed files with 675 additions and 0 deletions

View File

@@ -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",

674
platforms/isy-js.js Normal file
View File

@@ -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;