Skip to content

Commit

Permalink
Added new generic device identification based on device class informa…
Browse files Browse the repository at this point in the history
…tion from the ISY SDK. When device is not found in the isydevicetypes.json file the system will attempt to use type information to identify the device generically. Unfortunately the categories are not as clean as needed, some overrides will be needed which is why isydevicetypes.json will stay for now.

Minor fixup on number rounding for dim levels. Was rounding down resulting in small off by one errors.

Minro fix where device was out of order in isydevicetypes.json.

Renamed test.js to scratch.js to better represent it's purpose. It's not a test it's a simple test app when I am doing quick testing.

Added a mocha based test suite based on working with the fake-isy-994i project. If you run an instance of that server locally this set of mocha tests will workout the API.
  • Loading branch information
rodtoll committed Jan 2, 2016
1 parent b8e68ec commit a39beb2
Show file tree
Hide file tree
Showing 7 changed files with 526 additions and 7 deletions.
133 changes: 129 additions & 4 deletions isy.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,127 @@ ISY.prototype.DEVICE_TYPE_ALARM_PANEL = 'AlarmPanel';
ISY.prototype.DEVICE_TYPE_MOTION_SENSOR = 'MotionSensor';
ISY.prototype.DEVICE_TYPE_SCENE = 'Scene';

ISY.prototype.buildDeviceInfoRecord = function(isyType, deviceFamily, deviceType) {
return {
type: isyType,
address: "",
name: "Generic Device",
deviceType: deviceType,
connectionType: deviceFamily,
batteryOperated: false
};
}

ISY.prototype.getDeviceTypeBasedOnISYTable = function(deviceNode) {
var familyId = 1;
if(deviceNode.childNamed('family') != null) {
familyId = Number(deviceNode.childNamed('family').val);
}
var isyType = deviceNode.childNamed('type').val;
var addressData = deviceNode.childNamed('address').val
var addressElements = addressData.split(' ');
var typeElements = isyType.split('.');
var mainType = Number(typeElements[0]);
var subType = Number(typeElements[1]);
var subAddress = Number(addressElements[3]);
// ZWave nodes identify themselves with devtype node
if(deviceNode.childNamed('devtype') != null) {
if(deviceNode.childNamed('devtype').childNamed('cat') != null) {
subType = Number(deviceNode.childNamed('devtype').childNamed('cat').val);
}
}
// Insteon Device Family
if(familyId == 1) {

// Dimmable Devices
if(mainType == 1) {
// Special case fanlinc has a fan element
if(subType == 46 && subAddress == 2) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_FAN);
} else {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_DIMMABLE_LIGHT);
}
} else if(mainType == 2) {
// Special case appliance Lincs into outlets
if(subType == 6 || subType == 9 || subType == 12 || subType == 23) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_OUTLET);
// Outlet lincs
} else if(subType == 8 || subType == 33) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_OUTLET);
// Dual outlets
} else if(subType == 57) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_OUTLET);
} else {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_LIGHT);
}
// Sensors
} else if(mainType == 7) {
// I/O Lincs
if(subType == 0) {
if(subAddress == 1) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_DOOR_WINDOW_SENSOR);
} else {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_OUTLET);
}
// Other sensors. Not yet supported
} else {
return null;
}
// Access controls/doors/locks
} else if(mainType == 15) {
// MorningLinc
if(subType == 6) {
if(subAddress == 1) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_LOCK);
// Ignore subdevice which operates opposite for the locks
} else {
return null;
}
// Other devices, going to guess they are similar to MorningLinc
} else {
return null;
}
} else if(mainType == 16) {
// Motion sensors
if(subType == 1 || subType == 3) {
if(subAddress == 1) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_MOTION_SENSOR);
// Ignore battery level sensor and daylight sensor
} else {

}
} else if(subType == 2 || subType == 9 || subType == 17) {
return this.buildDeviceInfoRecord(isyType, "Insteon", this.DEVICE_TYPE_DOOR_WINDOW_SENSOR);
// Smoke, leak sensors, don't yet know how to support
} else {
return null;
}
// No idea how to test or support
} else {
return null;
}
// Z-Wave Device Family
} else if(familyId == 4) {
// Appears to be all ZWave devices seen so far
if(mainType == 4) {
// Identified by user zwave on/off switch
if(subType == 16) {
return this.buildDeviceInfoRecord(isyType, "ZWave", this.DEVICE_TYPE_LIGHT);
// Identified by user door lock
} else if(subType == 111) {
return this.buildDeviceInfoRecord(isyType, "ZWave", this.DEVICE_TYPE_SECURE_LOCK);
// This is a guess based on the naming in the ISY SDK
} else if(subType == 109) {
return this.buildDeviceInfoRecord(isyType, "ZWave", this.DEVICE_TYPE_DIMMABLE_LIGHT);
// Otherwise we don't know how to handle
} else {
return null;
}
}
}
return null;
}

ISY.prototype.nodeChangedHandler = function(node) {
var that = this;
if(this.nodesLoaded) {
Expand Down Expand Up @@ -118,7 +239,11 @@ ISY.prototype.loadDevices = function(document) {
var deviceTypeInfo = isyTypeToTypeName(isyDeviceType, deviceAddress);
var enabled = nodes[index].childNamed('enabled').val;

if(enabled !== 'false') {
if(enabled !== 'false') {
// Try fallback to new generic device identification when not specifically identified.
if(deviceTypeInfo == null) {
deviceTypeInfo = this.getDeviceTypeBasedOnISYTable(nodes[index]);
}
if(deviceTypeInfo != null) {
if(deviceTypeInfo.deviceType == this.DEVICE_TYPE_DIMMABLE_LIGHT ||
deviceTypeInfo.deviceType == this.DEVICE_TYPE_LIGHT) {
Expand Down Expand Up @@ -362,9 +487,9 @@ ISY.prototype.handleISYStateUpdate = function(address, state) {
if(this.scenesInDeviceList) {
// Inefficient, we could build a reverse index (device->scene list)
// but device list is relatively small
for(var sceneIndex = 0; sceneIndex < this.sceneList.length; sceneIndex++) {
if(this.sceneList[sceneIndex].isDeviceIncluded(deviceToUpdate)) {
this.nodeChangedHandler(this.sceneList[sceneIndex]);
for(var index = 0; index < this.sceneList.length; index++) {
if(this.sceneList[index].isDeviceIncluded(deviceToUpdate)) {
this.nodeChangedHandler(this.sceneList[index]);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion isydevice.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ ISYLightDevice.prototype.sendLightCommand = function(lightState,resultHandler) {
}

ISYLightDevice.prototype.sendLightDimCommand = function(dimLevel,resultHandler) {
var isyDimLevel = Math.floor(dimLevel*this.ISY_DIM_LEVEL_MAXIMUM/this.DIM_LEVEL_MAXIMUM);
var isyDimLevel = Math.round(dimLevel*this.ISY_DIM_LEVEL_MAXIMUM/this.DIM_LEVEL_MAXIMUM);
this.isy.sendRestCommand(this.address, this.ISY_COMMAND_LIGHT_ON, isyDimLevel, resultHandler);
}

Expand Down
2 changes: 1 addition & 1 deletion isydevicetypes.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
{ "type": "1.65.67.0", "address": "", "name": "(2334-2) KeypadLinc Dimmer 8 Buttons v.43", "deviceType": "DimmableLight", "connectionType": "Insteon Wired", "batteryOperated": false },
{ "type": "1.66.67.0", "address": "", "name": "(2334-2) KeypadLinc Dimmer 5 Buttons v.43", "deviceType": "DimmableLight", "connectionType": "Insteon Wired", "batteryOperated": false },
{ "type": "1.65.69.0", "address": "", "name": "(2334-222) KeypadLinc Dimmer 8 Buttons", "deviceType": "DimmableLight", "connectionType": "Insteon Wireless", "batteryOperated": false },
{ "type": "2.9.66.0", "address": "", "name": "(2456S3) ApplianceLinc v.42", "deviceType": "Outlet", "connectionType": "Insteon Wired", "batteryOperated": false },
{ "type": "2.9.66.0", "address": "", "name": "(2456S3) ApplianceLinc v.42", "deviceType": "Outlet", "connectionType": "Insteon Wired", "batteryOperated": false },
{ "type": "2.13.53.0", "address": "", "name": "(2466S) ToggleLinc", "deviceType": "Light", "connectionType": "Insteon Wired", "batteryOperated": false },
{ "type": "2.15.54.0", "address": "", "name": "(2486S/WH6) KeypadLinc Relay v.36", "deviceType": "Light", "connectionType": "Insteon Wired", "batteryOperated": false },
{ "type": "2.15.54.157", "address": "", "name": "(2486S/WH6) KeypadLinc Relay v.36", "deviceType": "Light", "connectionType": "Insteon Wired", "batteryOperated": false },
Expand Down
2 changes: 1 addition & 1 deletion isyscene.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ ISYScene.prototype.sendLightCommand = function(lightState,resultHandler) {
}

ISYScene.prototype.sendLightDimCommand = function(dimLevel,resultHandler) {
var isyDimLevel = Math.floor(dimLevel*this.ISY_DIM_LEVEL_MAXIMUM/this.DIM_LEVEL_MAXIMUM);
var isyDimLevel = Math.round(dimLevel*this.ISY_DIM_LEVEL_MAXIMUM/this.DIM_LEVEL_MAXIMUM);
this.isy.sendRestCommand(this.address, this.ISY_COMMAND_LIGHT_ON, isyDimLevel, resultHandler);
}

Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
"name": "Rod Toll",
"email": "rod_toll@hotmail.com"
},
"scripts" : {
"test": "mocha"
},
"keywords" : [
"isy",
"universal devies",
"insteon"
],
"main": "isy.js",
"devDepencies": {
"mocha": ""
},
"dependencies": {
"restler": "",
"faye-websocket": "",
Expand Down
4 changes: 4 additions & 0 deletions test.js → scratch.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// File for scratch testing. Will be removed in the future at is for my own internal purposes

var ISY = require('./isy');
var ISYDevice = require('./isydevice');

function handleInitialized() {
var deviceList = isy.getDeviceList();
console.log("Device count: "+deviceList.length);
if(deviceList == null) {
ISY.debugLog("No device list returned!");
} else {
Expand Down Expand Up @@ -48,6 +51,7 @@ function handleChanged(isy, device) {
var isy = new ISY.ISY('127.0.0.1:3000', 'admin', 'password', true, handleChanged, false, true);

isy.initialize(handleInitialized);
console.log('initialize completed');

function runBasicSceneTest(devices) {
for(var index = 0; index < devices.length; index++) {
Expand Down
Loading

0 comments on commit a39beb2

Please sign in to comment.