mirror of
https://github.com/mtan93/homebridge.git
synced 2026-04-11 14:23:07 +01:00
Merge branch 'master' of https://github.com/nfarina/homebridge
pulling down latest changes.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var AD2USB = require('ad2usb');
|
var AD2USB = require('ad2usb');
|
||||||
var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8";
|
var CUSTOM_PANEL_LCD_TEXT_CTYPE = "A3E7B8F9-216E-42C1-A21C-97D4E3BE52C8";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var carwings = require("carwingsjs");
|
var carwings = require("carwingsjs");
|
||||||
|
|
||||||
function CarwingsAccessory(log, config) {
|
function CarwingsAccessory(log, config) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var elkington = require("elkington");
|
var elkington = require("elkington");
|
||||||
|
|
||||||
function ElkM1Accessory(log, config) {
|
function ElkM1Accessory(log, config) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var chokidar = require("chokidar");
|
var chokidar = require("chokidar");
|
||||||
var debug = require("debug")("FileSensorAccessory");
|
var debug = require("debug")("FileSensorAccessory");
|
||||||
var crypto = require("crypto");
|
var crypto = require("crypto");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var SerialPort = require("serialport").SerialPort;
|
var SerialPort = require("serialport").SerialPort;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
function HomeMatic(log, config) {
|
function HomeMatic(log, config) {
|
||||||
@@ -30,7 +30,31 @@ HomeMatic.prototype = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getPowerState: function(callback) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
this.log("Getting Power State of CCU");
|
||||||
|
request.get({
|
||||||
|
url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID,
|
||||||
|
}, function(err, response, body) {
|
||||||
|
|
||||||
|
if (!err && response.statusCode == 200) {
|
||||||
|
|
||||||
|
//that.log("Response:"+response.body);
|
||||||
|
var responseString = response.body.substring(83,87);
|
||||||
|
//that.log(responseString);
|
||||||
|
switch(responseString){
|
||||||
|
case "true": {modvalue = "1";break;}
|
||||||
|
case "fals": {modvalue = "0";break;}
|
||||||
|
}
|
||||||
|
callback(parseInt(modvalue));
|
||||||
|
that.log("Getting Power State complete.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
that.log("Error '"+err+"' getting Power State: " + body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
getServices: function() {
|
getServices: function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
return [{
|
return [{
|
||||||
@@ -101,6 +125,7 @@ HomeMatic.prototype = {
|
|||||||
},{
|
},{
|
||||||
cType: types.POWER_STATE_CTYPE,
|
cType: types.POWER_STATE_CTYPE,
|
||||||
onUpdate: function(value) { that.setPowerState(value); },
|
onUpdate: function(value) { that.setPowerState(value); },
|
||||||
|
onRead: function(callback) { that.getPowerState(callback); },
|
||||||
perms: ["pw","pr","ev"],
|
perms: ["pw","pr","ev"],
|
||||||
format: "bool",
|
format: "bool",
|
||||||
initialValue: false,
|
initialValue: false,
|
||||||
|
|||||||
264
accessories/HomeMaticThermo.js
Normal file
264
accessories/HomeMaticThermo.js
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
|
var request = require("request");
|
||||||
|
|
||||||
|
function HomeMaticThermo(log, config) {
|
||||||
|
this.log = log;
|
||||||
|
this.name = config["name"];
|
||||||
|
this.ccuIDTargetTemp = config["ccu_id_TargetTemp"];
|
||||||
|
this.ccuIDCurrentTemp = config["ccu_id_CurrentTemp"];
|
||||||
|
this.ccuIDControlMode = config["ccu_id_ControlMode"];
|
||||||
|
this.ccuIDManuMode = config["ccu_id_ManuMode"];
|
||||||
|
this.ccuIDAutoMode = config["ccu_id_AutoMode"];
|
||||||
|
this.ccuIP = config["ccu_ip"];
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeMaticThermo.prototype = {
|
||||||
|
|
||||||
|
setTargetTemperature: function(value) {
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
this.log("Setting target Temperature of CCU to " + value);
|
||||||
|
this.log(this.ccuIDTargetTemp + " " + value);
|
||||||
|
|
||||||
|
request.put({
|
||||||
|
url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+this.ccuIDTargetTemp+"&new_value="+ value,
|
||||||
|
}, function(err, response, body) {
|
||||||
|
|
||||||
|
if (!err && response.statusCode == 200) {
|
||||||
|
that.log("State change complete.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
that.log("Error '"+err+"' setting Temperature: " + body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getCurrentTemperature: function(callback) {
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
this.log("Getting current Temperature of CCU");
|
||||||
|
request.get({
|
||||||
|
url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDCurrentTemp,
|
||||||
|
}, function(err, response, body) {
|
||||||
|
|
||||||
|
if (!err && response.statusCode == 200) {
|
||||||
|
|
||||||
|
//that.log("Response:"+response.body);
|
||||||
|
var responseString = response.body.substring(83,87);
|
||||||
|
//that.log(responseString);
|
||||||
|
callback(parseFloat(responseString));
|
||||||
|
//that.log("Getting current temperature complete.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
that.log("Error '"+err+"' getting Temperature: " + body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTargetTemperature: function(callback) {
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
this.log("Getting target Temperature of CCU");
|
||||||
|
request.get({
|
||||||
|
url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDTargetTemp,
|
||||||
|
}, function(err, response, body) {
|
||||||
|
|
||||||
|
if (!err && response.statusCode == 200) {
|
||||||
|
|
||||||
|
//that.log("Response:"+response.body);
|
||||||
|
var responseString = response.body.substring(83,87);
|
||||||
|
//that.log(responseString);
|
||||||
|
callback(parseFloat(responseString));
|
||||||
|
//that.log("Getting target temperature complete.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
that.log("Error '"+err+"' getting Temperature: " + body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getMode: function(callback) {
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
//this.log("Getting target Mode of CCU");
|
||||||
|
//this.log(this.ccuID+ value);
|
||||||
|
|
||||||
|
request.get({
|
||||||
|
url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuIDControlMode,
|
||||||
|
}, function(err, response, body) {
|
||||||
|
|
||||||
|
if (!err && response.statusCode == 200) {
|
||||||
|
|
||||||
|
//that.log("Response:"+response.body);
|
||||||
|
var responseInt = response.body.substring(83,84);
|
||||||
|
//that.log(responseString);
|
||||||
|
if (responseInt == 1)
|
||||||
|
{ callback(parseInt("0")); }
|
||||||
|
if (responseInt == 0)
|
||||||
|
{ callback(parseInt("1")); }
|
||||||
|
//that.log("Getting mode complete.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
that.log("Error '"+err+"' getting Mode: " + body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setMode: function(value) {
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
//this.log("Seting target Mode of CCU:" + value);
|
||||||
|
var modvalue;
|
||||||
|
var dpID;
|
||||||
|
switch(value) {
|
||||||
|
case 3: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //auto
|
||||||
|
case 1: {modvalue = "true";dpID=this.ccuIDAutoMode;break;} //heating => auto
|
||||||
|
default: {modvalue = "1";dpID=this.ccuIDManuMode;} //default => off (manual)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.put({
|
||||||
|
url: "http://"+this.ccuIP+"/config/xmlapi/statechange.cgi?ise_id="+dpID+"&new_value="+ modvalue,
|
||||||
|
}, function(err, response, body) {
|
||||||
|
|
||||||
|
if (!err && response.statusCode == 200) {
|
||||||
|
//that.log("Setting Mode complete.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
that.log("Error '"+err+"' setting Mode: " + body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getServices: function() {
|
||||||
|
var that = this;
|
||||||
|
return [{
|
||||||
|
sType: types.ACCESSORY_INFORMATION_STYPE,
|
||||||
|
characteristics: [{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: this.name,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of the accessory",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MANUFACTURER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: "test",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Manufacturer",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MODEL_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: "test",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Model",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.SERIAL_NUMBER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: "A1S2NREF88EW",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "SN",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.IDENTIFY_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pw"],
|
||||||
|
format: "bool",
|
||||||
|
initialValue: false,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Identify Accessory",
|
||||||
|
designedMaxLength: 1
|
||||||
|
}]
|
||||||
|
},{
|
||||||
|
sType: types.THERMOSTAT_STYPE,
|
||||||
|
characteristics: [{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: this.name,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of service",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.CURRENTHEATINGCOOLING_CTYPE,
|
||||||
|
onRead: function(callback) { that.getMode(callback); },
|
||||||
|
perms: ["pr","ev"],
|
||||||
|
format: "int",
|
||||||
|
initialValue: 0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Current Mode",
|
||||||
|
designedMaxLength: 1,
|
||||||
|
designedMinValue: 0,
|
||||||
|
designedMaxValue: 2,
|
||||||
|
designedMinStep: 1,
|
||||||
|
},{
|
||||||
|
cType: types.TARGETHEATINGCOOLING_CTYPE,
|
||||||
|
onRead: function(callback) { that.getMode(callback); },
|
||||||
|
onUpdate: function(value) { that.setMode(value);},
|
||||||
|
perms: ["pw","pr","ev"],
|
||||||
|
format: "int",
|
||||||
|
initialValue: 0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Target Mode",
|
||||||
|
designedMinValue: 0,
|
||||||
|
designedMaxValue: 3,
|
||||||
|
designedMinStep: 1,
|
||||||
|
},{
|
||||||
|
cType: types.CURRENT_TEMPERATURE_CTYPE,
|
||||||
|
onRead: function(callback) { that.getCurrentTemperature(callback); },
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr","ev"],
|
||||||
|
format: "float",
|
||||||
|
initialValue: 13.0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Current Temperature",
|
||||||
|
unit: "celsius"
|
||||||
|
},{
|
||||||
|
cType: types.TARGET_TEMPERATURE_CTYPE,
|
||||||
|
onUpdate: function(value) { that.setTargetTemperature(value); },
|
||||||
|
onRead: function(callback) { that.getTargetTemperature(callback); },
|
||||||
|
perms: ["pw","pr","ev"],
|
||||||
|
format: "float",
|
||||||
|
initialValue: 19.0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Target Temperature",
|
||||||
|
designedMinValue: 4,
|
||||||
|
designedMaxValue: 25,
|
||||||
|
designedMinStep: 0.1,
|
||||||
|
unit: "celsius"
|
||||||
|
},{
|
||||||
|
cType: types.TEMPERATURE_UNITS_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr","ev"],
|
||||||
|
format: "int",
|
||||||
|
initialValue: 0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Unit"
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.accessory = HomeMaticThermo;
|
||||||
123
accessories/HomeMaticWindow.js
Normal file
123
accessories/HomeMaticWindow.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
|
var request = require("request");
|
||||||
|
|
||||||
|
function HomeMaticWindow(log, config) {
|
||||||
|
this.log = log;
|
||||||
|
this.name = config["name"];
|
||||||
|
this.ccuID = config["ccu_id"];
|
||||||
|
this.ccuIP = config["ccu_ip"];
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeMaticWindow.prototype = {
|
||||||
|
|
||||||
|
|
||||||
|
getPowerState: function(callback) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
this.log("Getting Window State of CCU");
|
||||||
|
request.get({
|
||||||
|
url: "http://"+this.ccuIP+"/config/xmlapi/state.cgi?datapoint_id="+this.ccuID,
|
||||||
|
}, function(err, response, body) {
|
||||||
|
|
||||||
|
if (!err && response.statusCode == 200) {
|
||||||
|
|
||||||
|
//that.log("Response:"+response.body);
|
||||||
|
var responseString = response.body.substring(83,84);
|
||||||
|
//that.log(responseString);
|
||||||
|
switch(responseString){
|
||||||
|
case "0": {callback(Characteristic.ContactSensorState.CONTACT_DETECTED);break;}
|
||||||
|
case "1": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;}
|
||||||
|
case "2": {callback(Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);break;}
|
||||||
|
}
|
||||||
|
that.log("Getting Window State complete.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
that.log("Error '"+err+"' getting Window State: " + body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getServices: function() {
|
||||||
|
var that = this;
|
||||||
|
return [{
|
||||||
|
sType: types.ACCESSORY_INFORMATION_STYPE,
|
||||||
|
characteristics: [{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: this.name,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of the accessory",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MANUFACTURER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: "Homematic",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Manufacturer",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MODEL_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: "HM-Sec-RHS",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Model",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.SERIAL_NUMBER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: "A1S2NASF88EW",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "SN",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.IDENTIFY_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pw"],
|
||||||
|
format: "bool",
|
||||||
|
initialValue: false,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Identify Accessory",
|
||||||
|
designedMaxLength: 1
|
||||||
|
}]
|
||||||
|
},{
|
||||||
|
sType: types.CONTACT_SENSOR_STYPE,
|
||||||
|
characteristics: [{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: this.name,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of service",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.CONTACT_SENSOR_STATE_CTYPE,
|
||||||
|
onRead: function(callback) { that.getPowerState(callback); },
|
||||||
|
perms: ["pr","ev"],
|
||||||
|
format: "bool",
|
||||||
|
initialValue: false,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Get Window state of a Variable",
|
||||||
|
designedMaxLength: 1
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.accessory = HomeMaticWindow;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
71
accessories/HttpHygrometer.js
Normal file
71
accessories/HttpHygrometer.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
var Service = require("hap-nodejs").Service;
|
||||||
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
|
var request = require("request");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
accessory: HygrometerAccessory
|
||||||
|
}
|
||||||
|
|
||||||
|
function HygrometerAccessory(log, config) {
|
||||||
|
this.log = log;
|
||||||
|
|
||||||
|
// url info
|
||||||
|
this.url = config["url"];
|
||||||
|
this.http_method = config["http_method"];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HygrometerAccessory.prototype = {
|
||||||
|
|
||||||
|
httpRequest: function(url, method, callback) {
|
||||||
|
request({
|
||||||
|
url: url,
|
||||||
|
method: method
|
||||||
|
},
|
||||||
|
function (error, response, body) {
|
||||||
|
callback(error, response, body)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
identify: function(callback) {
|
||||||
|
this.log("Identify requested!");
|
||||||
|
callback(); // success
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentRelativeHumidity: function (callback) {
|
||||||
|
var that = this;
|
||||||
|
that.log ("getting CurrentCurrentRelativeHumidity");
|
||||||
|
|
||||||
|
this.httpRequest(this.url, this.http_method, function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HTTP function failed: %s', error);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HTTP function succeeded - %s', body);
|
||||||
|
callback(null, Number(body));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getServices: function() {
|
||||||
|
|
||||||
|
// you can OPTIONALLY create an information service if you wish to override
|
||||||
|
// the default values for things like serial number, model, etc.
|
||||||
|
var informationService = new Service.AccessoryInformation();
|
||||||
|
|
||||||
|
informationService
|
||||||
|
.setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer")
|
||||||
|
.setCharacteristic(Characteristic.Model, "HTTP Hygrometer")
|
||||||
|
.setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number");
|
||||||
|
|
||||||
|
var humidityService = new Service.HumiditySensor();
|
||||||
|
|
||||||
|
humidityService
|
||||||
|
.getCharacteristic(Characteristic.CurrentRelativeHumidity)
|
||||||
|
.on('get', this.getCurrentRelativeHumidity.bind(this));
|
||||||
|
|
||||||
|
return [informationService, humidityService];
|
||||||
|
}
|
||||||
|
};
|
||||||
79
accessories/HttpThermometer.js
Normal file
79
accessories/HttpThermometer.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
var Service = require("hap-nodejs").Service;
|
||||||
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
|
var request = require("request");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
accessory: ThermometerAccessory
|
||||||
|
}
|
||||||
|
|
||||||
|
function ThermometerAccessory(log, config) {
|
||||||
|
this.log = log;
|
||||||
|
|
||||||
|
// url info
|
||||||
|
this.url = config["url"];
|
||||||
|
this.http_method = config["http_method"];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ThermometerAccessory.prototype = {
|
||||||
|
|
||||||
|
httpRequest: function(url, method, callback) {
|
||||||
|
request({
|
||||||
|
url: url,
|
||||||
|
method: method
|
||||||
|
},
|
||||||
|
function (error, response, body) {
|
||||||
|
callback(error, response, body)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
identify: function(callback) {
|
||||||
|
this.log("Identify requested!");
|
||||||
|
callback(); // success
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentTemperature: function (callback) {
|
||||||
|
var that = this;
|
||||||
|
that.log ("getting CurrentTemperature");
|
||||||
|
|
||||||
|
|
||||||
|
this.httpRequest(this.url, this.http_method, function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HTTP function failed: %s', error);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HTTP function succeeded - %s', body);
|
||||||
|
callback(null, Number(body));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getTemperatureUnits: function (callback) {
|
||||||
|
var that = this;
|
||||||
|
that.log ("getTemperature Units");
|
||||||
|
// 1 = F and 0 = C
|
||||||
|
callback (null, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
getServices: function() {
|
||||||
|
|
||||||
|
// you can OPTIONALLY create an information service if you wish to override
|
||||||
|
// the default values for things like serial number, model, etc.
|
||||||
|
var informationService = new Service.AccessoryInformation();
|
||||||
|
|
||||||
|
informationService
|
||||||
|
.setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer")
|
||||||
|
.setCharacteristic(Characteristic.Model, "HTTP Thermometer")
|
||||||
|
.setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number");
|
||||||
|
|
||||||
|
var temperatureService = new Service.TemperatureSensor();
|
||||||
|
|
||||||
|
temperatureService
|
||||||
|
.getCharacteristic(Characteristic.CurrentTemperature)
|
||||||
|
.on('get', this.getCurrentTemperature.bind(this));
|
||||||
|
|
||||||
|
return [informationService, temperatureService];
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
var Color = require('color');
|
var Color = require('color');
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
// This seems to be the "id" of the official LiftMaster iOS app
|
// This seems to be the "id" of the official LiftMaster iOS app
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
var Service = require('HAP-NodeJS').Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require('HAP-NodeJS').Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var tesla = require("teslams");
|
var tesla = require("teslams");
|
||||||
|
|
||||||
function TeslaAccessory(log, config) {
|
function TeslaAccessory(log, config) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var wemo = require('wemo');
|
var wemo = require('wemo');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
function X10(log, config) {
|
function X10(log, config) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
var iControl = require('node-icontrol').iControl;
|
var iControl = require('node-icontrol').iControl;
|
||||||
var Service = require('HAP-NodeJS').Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require('HAP-NodeJS').Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
accessory: iControlAccessory
|
accessory: iControlAccessory
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ New 2015-10-07:
|
|||||||
- Accept uuid_base parameter from config.json to use as unique identifier in UUIDs instead of name (optional)
|
- Accept uuid_base parameter from config.json to use as unique identifier in UUIDs instead of name (optional)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var knxd = require("eibd");
|
var knxd = require("eibd");
|
||||||
var knxd_registerGA = require('../platforms/KNX.js').registerGA;
|
var knxd_registerGA = require('../platforms/KNX.js').registerGA;
|
||||||
var knxd_startMonitor = require('../platforms/KNX.js').startMonitor;
|
var knxd_startMonitor = require('../platforms/KNX.js').startMonitor;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
var komponist = require('komponist')
|
var komponist = require('komponist')
|
||||||
|
|
||||||
|
|||||||
16
app.js
16
app.js
@@ -1,14 +1,14 @@
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var storage = require('node-persist');
|
var storage = require('node-persist');
|
||||||
var hap = require('HAP-NodeJS');
|
var hap = require("hap-nodejs");
|
||||||
var uuid = require('HAP-NodeJS').uuid;
|
var uuid = require("hap-nodejs").uuid;
|
||||||
var Bridge = require('HAP-NodeJS').Bridge;
|
var Bridge = require("hap-nodejs").Bridge;
|
||||||
var Accessory = require('HAP-NodeJS').Accessory;
|
var Accessory = require("hap-nodejs").Accessory;
|
||||||
var Service = require('HAP-NodeJS').Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require('HAP-NodeJS').Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var accessoryLoader = require('HAP-NodeJS').AccessoryLoader;
|
var accessoryLoader = require("hap-nodejs").AccessoryLoader;
|
||||||
var once = require('HAP-NodeJS/lib/util/once').once;
|
var once = require("hap-nodejs/lib/util/once").once;
|
||||||
|
|
||||||
console.log("Starting HomeBridge server...");
|
console.log("Starting HomeBridge server...");
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
"token" : "telldus token",
|
"token" : "telldus token",
|
||||||
"token_secret" : "telldus token secret"
|
"token_secret" : "telldus token secret"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"platform" : "Telldus",
|
||||||
|
"name" : "Telldus"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"platform": "Wink",
|
"platform": "Wink",
|
||||||
"name": "Wink",
|
"name": "Wink",
|
||||||
@@ -149,6 +153,24 @@
|
|||||||
"ccu_id": "The XMP-API id of your HomeMatic device",
|
"ccu_id": "The XMP-API id of your HomeMatic device",
|
||||||
"ccu_ip": "The IP-Adress of your HomeMatic CCU device"
|
"ccu_ip": "The IP-Adress of your HomeMatic CCU device"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"accessory": "HomeMaticWindow",
|
||||||
|
"name": "Contact",
|
||||||
|
"description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)",
|
||||||
|
"ccu_id": "The XMP-API id of your HomeMatic device (type HM-Sec-RHS)",
|
||||||
|
"ccu_ip": "The IP-Adress of your HomeMatic CCU device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accessory": "HomeMaticThermo",
|
||||||
|
"name": "Contact",
|
||||||
|
"description": "Control HomeMatic devices (The XMP-API addon for the CCU is required)",
|
||||||
|
"ccu_id_TargetTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )",
|
||||||
|
"ccu_id_CurrentTemp": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )",
|
||||||
|
"ccu_id_ControlMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )",
|
||||||
|
"ccu_id_ManuMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )",
|
||||||
|
"ccu_id_AutoMode": "The XMP-API id of your HomeMatic device (type HM-CC-RT-DN )",
|
||||||
|
"ccu_ip": "The IP-Adress of your HomeMatic CCU device"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"accessory": "X10",
|
"accessory": "X10",
|
||||||
"name": "Lamp",
|
"name": "Lamp",
|
||||||
@@ -164,7 +186,20 @@
|
|||||||
"off_url": "https://192.168.1.22:3030/devices/23222/off",
|
"off_url": "https://192.168.1.22:3030/devices/23222/off",
|
||||||
"brightness_url": "https://192.168.1.22:3030/devices/23222/brightness/%b",
|
"brightness_url": "https://192.168.1.22:3030/devices/23222/brightness/%b",
|
||||||
"http_method": "POST"
|
"http_method": "POST"
|
||||||
},{
|
},
|
||||||
|
{
|
||||||
|
"accessory": "HttpHygrometer",
|
||||||
|
"name": "Kitchen",
|
||||||
|
"url": "http://host/URL",
|
||||||
|
"http_method": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accessory": "HttpThermometer",
|
||||||
|
"name": "Garage",
|
||||||
|
"url": "http://home/URL",
|
||||||
|
"http_method": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
"accessory": "ELKM1",
|
"accessory": "ELKM1",
|
||||||
"name": "Security System",
|
"name": "Security System",
|
||||||
"description": "Allows basic control of Elk M1 security system. You can use 1 of 3 arm modes: Away, Stay, Night. If you need to access all 3, create 3 accessories with different names.",
|
"description": "Allows basic control of Elk M1 security system. You can use 1 of 3 arm modes: Away, Stay, Night. If you need to access all 3, create 3 accessories with different names.",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"color": "0.10.x",
|
"color": "0.10.x",
|
||||||
"eibd": "^0.3.1",
|
"eibd": "^0.3.1",
|
||||||
"elkington": "kevinohara80/elkington",
|
"elkington": "kevinohara80/elkington",
|
||||||
"hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#4650e771f356a220868d873d16564a6be6603ff7",
|
"hap-nodejs": "^0.0.2",
|
||||||
"harmonyhubjs-client": "^1.1.4",
|
"harmonyhubjs-client": "^1.1.4",
|
||||||
"harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git",
|
"harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git",
|
||||||
"isy-js": "",
|
"isy-js": "",
|
||||||
@@ -25,14 +25,13 @@
|
|||||||
"lifx": "git+https://github.com/magicmonkey/lifxjs.git",
|
"lifx": "git+https://github.com/magicmonkey/lifxjs.git",
|
||||||
"mdns": "^2.2.4",
|
"mdns": "^2.2.4",
|
||||||
"node-hue-api": "^1.0.5",
|
"node-hue-api": "^1.0.5",
|
||||||
"node-icontrol": "^0.1.4",
|
"node-icontrol": "^0.1.5",
|
||||||
"node-milight-promise": "0.0.x",
|
"node-milight-promise": "0.0.x",
|
||||||
"node-persist": "0.0.x",
|
"node-persist": "0.0.x",
|
||||||
"q": "1.4.x",
|
"q": "1.4.x",
|
||||||
"tough-cookie": "^2.0.0",
|
"tough-cookie": "^2.0.0",
|
||||||
"request": "2.49.x",
|
"request": "2.49.x",
|
||||||
"sonos": "0.8.x",
|
"sonos": "0.8.x",
|
||||||
"telldus-live": "0.2.x",
|
|
||||||
"teslams": "1.0.1",
|
"teslams": "1.0.1",
|
||||||
"unofficial-nest-api": "git+https://github.com/hachidorii/unofficial_nodejs_nest.git#d8d48edc952b049ff6320ef99afa7b2f04cdee98",
|
"unofficial-nest-api": "git+https://github.com/hachidorii/unofficial_nodejs_nest.git#d8d48edc952b049ff6320ef99afa7b2f04cdee98",
|
||||||
"wemo": "0.2.x",
|
"wemo": "0.2.x",
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
// When you attempt to add a device, it will ask for a "PIN code".
|
// When you attempt to add a device, it will ask for a "PIN code".
|
||||||
// The default code for all HomeBridge accessories is 031-45-154.
|
// The default code for all HomeBridge accessories is 031-45-154.
|
||||||
//
|
//
|
||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
function DomoticzPlatform(log, config){
|
function DomoticzPlatform(log, config){
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
// When you attempt to add a device, it will ask for a "PIN code".
|
// When you attempt to add a device, it will ask for a "PIN code".
|
||||||
// The default code for all HomeBridge accessories is 031-45-154.
|
// The default code for all HomeBridge accessories is 031-45-154.
|
||||||
|
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
|
|
||||||
var types = require('HAP-NodeJS/accessories/types.js');
|
var types = require('hap-nodejs/accessories/types.js');
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
// When you attempt to add a device, it will ask for a "PIN code".
|
// When you attempt to add a device, it will ask for a "PIN code".
|
||||||
// The default code for all HomeBridge accessories is 031-45-154.
|
// The default code for all HomeBridge accessories is 031-45-154.
|
||||||
|
|
||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
function FibaroHC2Platform(log, config){
|
function FibaroHC2Platform(log, config){
|
||||||
|
|||||||
@@ -67,8 +67,8 @@
|
|||||||
// When you attempt to add a device, it will ask for a "PIN code".
|
// When you attempt to add a device, it will ask for a "PIN code".
|
||||||
// The default code for all HomeBridge accessories is 031-45-154.
|
// The default code for all HomeBridge accessories is 031-45-154.
|
||||||
|
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var url = require('url')
|
var url = require('url')
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,34 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
//
|
//
|
||||||
// HomeSeer Platform Shim for HomeBridge
|
// HomeSeer Platform Shim for HomeBridge by Jean-Michel Joudrier - (stipus at stipus dot com)
|
||||||
// V0.1 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/07 - Initial version
|
// V0.1 - 2015/10/07
|
||||||
|
// - Initial version
|
||||||
|
// V0.2 - 2015/10/10
|
||||||
|
// - Occupancy sensor fix
|
||||||
|
// V0.3 - 2015/10/11
|
||||||
|
// - Added TemperatureUnit=F|C option to temperature sensors
|
||||||
|
// - Added negative temperature support to temperature sensors
|
||||||
|
// V0.4 - 2015/10/12
|
||||||
|
// - Added thermostat support
|
||||||
|
// V0.5 - 2015/10/12
|
||||||
|
// - Added Humidity sensor support
|
||||||
|
// V0.6 - 2015/10/12
|
||||||
|
// - Added Battery support
|
||||||
|
// - Added low battery support for all sensors
|
||||||
|
// - Added HomeSeer event support (using HomeKit switches...)
|
||||||
|
// V0.7 - 2015/10/13
|
||||||
|
// - You can add multiple HomeKit devices for the same HomeSeer device reference
|
||||||
|
// - Added CarbonMonoxide sensor
|
||||||
|
// - Added CarbonDioxide sensor
|
||||||
|
// - Added onValues option to all binary sensors
|
||||||
|
// V0.8 - 2015/10/14
|
||||||
|
// - Added uuid_base parameter to all accessories
|
||||||
|
// V0.9 - 2015/10/16
|
||||||
|
// - Smoke sensor battery fix
|
||||||
|
// - Added offEventGroup && offEventName to events (turn <event> on launches one HS event. turn <event> off can launch another HS event)
|
||||||
|
// - Added GarageDoorOpener support
|
||||||
|
// - Added Lock support
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Remember to add platform to config.json.
|
// Remember to add platform to config.json.
|
||||||
@@ -13,24 +39,119 @@
|
|||||||
// Example:
|
// Example:
|
||||||
// "platforms": [
|
// "platforms": [
|
||||||
// {
|
// {
|
||||||
// "platform": "HomeSeer", // required
|
// "platform": "HomeSeer", // Required
|
||||||
// "name": "HomeSeer", // required
|
// "name": "HomeSeer", // Required
|
||||||
// "url": "http://192.168.3.4:81", // required
|
// "host": "http://192.168.3.4:81", // Required - If you did setup HomeSeer authentication, use "http://user:password@ip_address:port"
|
||||||
// "accessories":[
|
//
|
||||||
|
// "events":[ // Optional - List of Events - Currently they are imported into HomeKit as switches
|
||||||
// {
|
// {
|
||||||
// "ref":8, // required - HomeSeer Device Reference (To get it, select the HS Device - then Advanced Tab)
|
// "eventGroup":"My Group", // Required - The HomeSeer event group
|
||||||
// "type":"Lightbulb", // Optional - Lightbulb is the default
|
// "eventName":"My On Event", // Required - The HomeSeer event name
|
||||||
// "name":"My Light", // Optional - HomeSeer device name is the default
|
// "offEventGroup":"My Group", // Optional - The HomeSeer event group for turn-off <event>
|
||||||
// "offValue":"0", // Optional - 0 is the default
|
// "offEventName":"My Off Event", // Optional - The HomeSeer event name for turn-off <event>
|
||||||
// "onValue":"100", // Optional - 100 is the default
|
// "name":"Test", // Optional - HomeSeer event name is the default
|
||||||
// "can_dim":true // Optional - true is the default - false for a non dimmable lightbulb
|
// "uuid_base":"SomeUniqueId" // Optional - HomeKit identifier will be derived from this parameter instead of the name
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
//
|
||||||
|
// "accessories":[ // Required - List of Accessories
|
||||||
|
// {
|
||||||
|
// "ref":8, // Required - HomeSeer Device Reference (To get it, select the HS Device - then Advanced Tab)
|
||||||
|
// "type":"Lightbulb", // Optional - Lightbulb is the default
|
||||||
|
// "name":"My Light", // Optional - HomeSeer device name is the default
|
||||||
|
// "offValue":"0", // Optional - 0 is the default
|
||||||
|
// "onValue":"100", // Optional - 100 is the default
|
||||||
|
// "can_dim":true, // Optional - true is the default - false for a non dimmable lightbulb
|
||||||
|
// "uuid_base":"SomeUniqueId2" // Optional - HomeKit identifier will be derived from this parameter instead of the name. You SHOULD add this parameter to all accessories !
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// "ref":9 // This is a dimmable Lightbulb by default
|
// "ref":9 // This is a dimmable Lightbulb by default
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// "ref":58, // This is an controllable outlet
|
// "ref":58, // This is a controllable outlet
|
||||||
// "type":"Outlet"
|
// "type":"Outlet"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "ref":111, // Required - HomeSeer Device Reference for your sensor
|
||||||
|
// "type":"TemperatureSensor", // Required for a temperature sensor
|
||||||
|
// "temperatureUnit":"F", // Optional - C is the default
|
||||||
|
// "name":"Bedroom temp", // Optional - HomeSeer device name is the default
|
||||||
|
// "batteryRef":112, // Optional - HomeSeer device reference for the sensor battery level
|
||||||
|
// "batteryThreshold":15 // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "ref":34, // Required - HomeSeer Device Reference for your sensor
|
||||||
|
// "type":"SmokeSensor", // Required for a smoke sensor
|
||||||
|
// "name":"Kichen smoke detector", // Optional - HomeSeer device name is the default
|
||||||
|
// "batteryRef":35, // Optional - HomeSeer device reference for the sensor battery level
|
||||||
|
// "batteryThreshold":15, // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10
|
||||||
|
// "onValues":[1,1.255] // Optional - List of all HomeSeer values triggering a "ON" sensor state - Default is any value different than 0
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "ref":34, // Required - HomeSeer Device Reference for your sensor (Here it's the same device as the SmokeSensor above)
|
||||||
|
// "type":"CarbonMonoxideSensor", // Required for a carbon monoxide sensor
|
||||||
|
// "name":"Kichen CO detector", // Optional - HomeSeer device name is the default
|
||||||
|
// "batteryRef":35, // Optional - HomeSeer device reference for the sensor battery level
|
||||||
|
// "batteryThreshold":15, // Optional - If sensor battery level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10
|
||||||
|
// "onValues":[2,2.255] // Optional - List of all HomeSeer values triggering a "ON" sensor state - Default is any value different than 0
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "ref":113, // Required - HomeSeer Device Reference of the Current Temperature Device
|
||||||
|
// "type":"Thermostat", // Required for a Thermostat
|
||||||
|
// "name":"Température Salon", // Optional - HomeSeer device name is the default
|
||||||
|
// "temperatureUnit":"C", // Optional - F for Fahrenheit, C for Celsius, C is the default
|
||||||
|
// "setPointRef":167, // Required - HomeSeer device reference for your thermostat Set Point.
|
||||||
|
// "setPointReadOnly":true, // Optional - Set to false if your SetPoint is read/write. true is the default
|
||||||
|
// "stateRef":166, // Required - HomeSeer device reference for your thermostat current state
|
||||||
|
// "stateOffValues":[0,4,5], // Required - List of the HomeSeer device values for a HomeKit state=OFF
|
||||||
|
// "stateHeatValues":[1], // Required - List of the HomeSeer device values for a HomeKit state=HEAT
|
||||||
|
// "stateCoolValues":[2], // Required - List of the HomeSeer device values for a HomeKit state=COOL
|
||||||
|
// "stateAutoValues":[3], // Required - List of the HomeSeer device values for a HomeKit state=AUTO
|
||||||
|
// "controlRef":168, // Required - HomeSeer device reference for your thermostat mode control (It can be the same as stateRef for some thermostats)
|
||||||
|
// "controlOffValue":0, // Required - HomeSeer device control value for OFF
|
||||||
|
// "controlHeatValue":1, // Required - HomeSeer device control value for HEAT
|
||||||
|
// "controlCoolValue":2, // Required - HomeSeer device control value for COOL
|
||||||
|
// "controlAutoValue":3, // Required - HomeSeer device control value for AUTO
|
||||||
|
// "coolingThresholdRef":169, // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat cooling threshold
|
||||||
|
// "heatingThresholdRef":170 // Optional - Not-implemented-yet - HomeSeer device reference for your thermostat heating threshold
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "ref":200, // Required - HomeSeer Device Reference of a garage door opener
|
||||||
|
// "type":"GarageDoorOpener", // Required for a Garage Door Opener
|
||||||
|
// "name":"Garage Door", // Optional - HomeSeer device name is the default
|
||||||
|
// "stateRef":201, // Required - HomeSeer device reference for your garage door opener current state (can be the same as ref)
|
||||||
|
// "stateOpenValues":[0], // Required - List of the HomeSeer device values for a HomeKit state=OPEN
|
||||||
|
// "stateClosedValues":[1], // Required - List of the HomeSeer device values for a HomeKit state=CLOSED
|
||||||
|
// "stateOpeningValues":[2], // Optional - List of the HomeSeer device values for a HomeKit state=OPENING
|
||||||
|
// "stateClosingValues":[3], // Optional - List of the HomeSeer device values for a HomeKit state=CLOSING
|
||||||
|
// "stateStoppedValues":[4], // Optional - List of the HomeSeer device values for a HomeKit state=STOPPED
|
||||||
|
// "controlRef":201, // Required - HomeSeer device reference for your garage door opener control (can be the same as ref and stateRef)
|
||||||
|
// "controlOpenValue":0, // Required - HomeSeer device control value for OPEN
|
||||||
|
// "controlCloseValue":1, // Required - HomeSeer device control value for CLOSE
|
||||||
|
// "obstructionRef":201, // Optional - HomeSeer device reference for your garage door opener obstruction state (can be the same as ref)
|
||||||
|
// "obstructionValues":[5], // Optional - List of the HomeSeer device values for a HomeKit obstruction state=OBSTRUCTION
|
||||||
|
// "lockRef":202, // Optional - HomeSeer device reference for your garage door lock (can be the same as ref)
|
||||||
|
// "lockUnsecuredValues":[0], // Optional - List of the HomeSeer device values for a HomeKit lock state=UNSECURED
|
||||||
|
// "lockSecuredValues":[1], // Optional - List of the HomeSeer device values for a HomeKit lock state=SECURED
|
||||||
|
// "lockJammedValues":[2], // Optional - List of the HomeSeer device values for a HomeKit lock state=JAMMED
|
||||||
|
// "unlockValue":0, // Optional - HomeSeer device control value to unlock the garage door opener
|
||||||
|
// "lockValue":1 // Optional - HomeSeer device control value to lock the garage door opener
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "ref":210, // Required - HomeSeer Device Reference of a Lock
|
||||||
|
// "type":"Lock", // Required for a Lock
|
||||||
|
// "name":"Main Door Lock", // Optional - HomeSeer device name is the default
|
||||||
|
// "lockUnsecuredValues":[0], // Required - List of the HomeSeer device values for a HomeKit lock state=UNSECURED
|
||||||
|
// "lockSecuredValues":[1], // Required - List of the HomeSeer device values for a HomeKit lock state=SECURED
|
||||||
|
// "lockJammedValues":[2], // Optional - List of the HomeSeer device values for a HomeKit lock state=JAMMED
|
||||||
|
// "unlockValue":0, // Required - HomeSeer device control value to unlock
|
||||||
|
// "lockValue":1 // Required - HomeSeer device control value to lock
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "ref":115, // Required - HomeSeer Device Reference for a device holding battery level (0-100)
|
||||||
|
// "type":"Battery", // Required for a Battery
|
||||||
|
// "name":"Roomba battery", // Optional - HomeSeer device name is the default
|
||||||
|
// "batteryThreshold":15 // Optional - If the level is below this value, the HomeKit LowBattery characteristic is set to 1. Default is 10
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
@@ -38,22 +159,29 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
// SUPORTED TYPES:
|
// SUPORTED TYPES:
|
||||||
// - Lightbulb (can_dim, onValue, offValue options)
|
// - Lightbulb (can_dim, onValue, offValue options)
|
||||||
// - Fan (onValue, offValue options)
|
// - Fan (onValue, offValue options)
|
||||||
// - Switch (onValue, offValue options)
|
// - Switch (onValue, offValue options)
|
||||||
// - Outlet (onValue, offValue options)
|
// - Outlet (onValue, offValue options)
|
||||||
// - TemperatureSensor
|
// - Thermostat (temperatureUnit, setPoint, state, control options)
|
||||||
// - ContactSensor
|
// - TemperatureSensor (temperatureUnit=C|F)
|
||||||
// - MotionSensor
|
// - HumiditySensor (HomeSeer device value in % - batteryRef, batteryThreshold options)
|
||||||
// - LeakSensor
|
// - LightSensor (HomeSeer device value in Lux - batteryRef, batteryThreshold options)
|
||||||
// - LightSensor
|
// - ContactSensor (onValues, batteryRef, batteryThreshold options)
|
||||||
// - OccupancySensor
|
// - MotionSensor (onValues, batteryRef, batteryThreshold options)
|
||||||
// - SmokeSensor
|
// - LeakSensor (onValues, batteryRef, batteryThreshold options)
|
||||||
|
// - OccupancySensor (onValues, batteryRef, batteryThreshold options)
|
||||||
|
// - SmokeSensor (onValues, batteryRef, batteryThreshold options)
|
||||||
|
// - CarbonMonoxideSensor (onValues, batteryRef, batteryThreshold options)
|
||||||
|
// - CarbonDioxideSensor (onValues, batteryRef, batteryThreshold options)
|
||||||
|
// - Battery (batteryThreshold option)
|
||||||
|
// - GarageDoorOpener (state, control, obstruction, lock options)
|
||||||
|
// - Lock (unsecured, secured, jammed options)
|
||||||
// - Door
|
// - Door
|
||||||
|
|
||||||
|
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
|
|
||||||
@@ -76,17 +204,24 @@ function HomeSeerPlatform(log, config){
|
|||||||
|
|
||||||
HomeSeerPlatform.prototype = {
|
HomeSeerPlatform.prototype = {
|
||||||
accessories: function(callback) {
|
accessories: function(callback) {
|
||||||
this.log("Fetching HomeSeer devices.");
|
var that = this;
|
||||||
|
var foundAccessories = [];
|
||||||
|
|
||||||
|
if( this.config.events ) {
|
||||||
|
this.log("Creating HomeSeer events.");
|
||||||
|
for( var i=0; i<this.config.events.length; i++ ) {
|
||||||
|
var event = new HomeSeerEvent( that.log, that.config, that.config.events[i] );
|
||||||
|
foundAccessories.push( event );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log("Fetching HomeSeer devices.");
|
||||||
var refList = "";
|
var refList = "";
|
||||||
for( var i=0; i<this.config.accessories.length; i++ ) {
|
for( var i=0; i<this.config.accessories.length; i++ ) {
|
||||||
refList = refList + this.config.accessories[i].ref;
|
refList = refList + this.config.accessories[i].ref;
|
||||||
if( i < this.config.accessories.length - 1 )
|
if( i < this.config.accessories.length - 1 )
|
||||||
refList = refList + ",";
|
refList = refList + ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
var that = this;
|
|
||||||
var foundAccessories = [];
|
|
||||||
var url = this.config["host"] + "/JSON?request=getstatus&ref=" + refList;
|
var url = this.config["host"] + "/JSON?request=getstatus&ref=" + refList;
|
||||||
httpRequest( url, "GET", function(error, response, body) {
|
httpRequest( url, "GET", function(error, response, body) {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -96,9 +231,14 @@ HomeSeerPlatform.prototype = {
|
|||||||
else {
|
else {
|
||||||
this.log('HomeSeer status function succeeded!');
|
this.log('HomeSeer status function succeeded!');
|
||||||
var response = JSON.parse( body );
|
var response = JSON.parse( body );
|
||||||
for( var i=0; i<response.Devices.length; i++ ) {
|
for( var i=0; i<this.config.accessories.length; i++ ) {
|
||||||
var accessory = new HomeSeerAccessory( that.log, that.config, response.Devices[i] );
|
for( var j=0; j<response.Devices.length; j++ ) {
|
||||||
foundAccessories.push( accessory );
|
if( this.config.accessories[i].ref == response.Devices[j].ref ) {
|
||||||
|
var accessory = new HomeSeerAccessory( that.log, that.config, this.config.accessories[i], response.Devices[j] );
|
||||||
|
foundAccessories.push( accessory );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callback( foundAccessories );
|
callback( foundAccessories );
|
||||||
}
|
}
|
||||||
@@ -106,28 +246,25 @@ HomeSeerPlatform.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomeSeerAccessory(log, platformConfig, status ) {
|
function HomeSeerAccessory(log, platformConfig, accessoryConfig, status ) {
|
||||||
this.log = log;
|
this.log = log;
|
||||||
|
this.config = accessoryConfig;
|
||||||
this.ref = status.ref;
|
this.ref = status.ref;
|
||||||
this.name = status.name
|
this.name = status.name
|
||||||
this.model = status.device_type_string;
|
this.model = status.device_type_string;
|
||||||
this.onValue = "100";
|
this.onValue = 100;
|
||||||
this.offValue = "0";
|
this.offValue = 0;
|
||||||
|
|
||||||
this.control_url = platformConfig["host"] + "/JSON?request=controldevicebyvalue&ref=" + this.ref + "&value=";
|
this.access_url = platformConfig["host"] + "/JSON?";
|
||||||
this.status_url = platformConfig["host"] + "/JSON?request=getstatus&ref=" + this.ref;
|
this.control_url = this.access_url + "request=controldevicebyvalue&ref=" + this.ref + "&value=";
|
||||||
|
this.status_url = this.access_url + "request=getstatus&ref=" + this.ref;
|
||||||
for( var i=0; i<platformConfig.accessories.length; i++ ) {
|
|
||||||
if( platformConfig.accessories[i].ref == this.ref )
|
|
||||||
{
|
|
||||||
this.config = platformConfig.accessories[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( this.config.name )
|
if( this.config.name )
|
||||||
this.name = this.config.name;
|
this.name = this.config.name;
|
||||||
|
|
||||||
|
if( this.config.uuid_base )
|
||||||
|
this.uuid_base = this.config.uuid_base;
|
||||||
|
|
||||||
if( this.config.onValue )
|
if( this.config.onValue )
|
||||||
this.onValue = this.config.onValue;
|
this.onValue = this.config.onValue;
|
||||||
|
|
||||||
@@ -186,6 +323,35 @@ HomeSeerAccessory.prototype = {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getBinarySensorState: function(callback) {
|
||||||
|
var url = this.status_url;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get binary sensor state function failed: %s', error.message);
|
||||||
|
callback( error, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
|
||||||
|
this.log('HomeSeer get binary sensor state function succeeded: value=' + value );
|
||||||
|
if( this.config.onValues ) {
|
||||||
|
if( this.config.onValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 1 );
|
||||||
|
else
|
||||||
|
callback( null, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if( value != 0 )
|
||||||
|
callback( null, 1 );
|
||||||
|
else
|
||||||
|
callback( null, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
setValue: function(level, callback) {
|
setValue: function(level, callback) {
|
||||||
var url = this.control_url + level;
|
var url = this.control_url + level;
|
||||||
@@ -222,6 +388,316 @@ HomeSeerAccessory.prototype = {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setTemperature: function(temperature, callback) {
|
||||||
|
this.log("Setting temperature to %s", temperature);
|
||||||
|
if( this.config.temperatureUnit == "F" ) {
|
||||||
|
temperature = temperature*9/5+32;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = this.control_url + temperature;
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer set temperature function failed: %s', error.message);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HomeSeer set temperature function succeeded!');
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getTemperature: function(callback) {
|
||||||
|
var url = this.status_url;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get temperature function failed: %s', error.message);
|
||||||
|
callback( error, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
|
||||||
|
this.log('HomeSeer get temperature function succeeded: value=' + value );
|
||||||
|
if( this.config.temperatureUnit == "F" ) {
|
||||||
|
value = (value-32)*5/9;
|
||||||
|
}
|
||||||
|
callback( null, value );
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getThermostatCurrentHeatingCoolingState: function(callback) {
|
||||||
|
var ref = this.config.stateRef;
|
||||||
|
var url = this.access_url + "request=getstatus&ref=" + ref;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get thermostat current heating cooling state function failed: %s', error.message);
|
||||||
|
callback( error, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
|
||||||
|
this.log('HomeSeer get thermostat current heating cooling state function succeeded: value=' + value );
|
||||||
|
if( this.config.stateOffValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 0 );
|
||||||
|
else if( this.config.stateHeatValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 1 );
|
||||||
|
else if( this.config.stateCoolValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 2 );
|
||||||
|
else if( this.config.stateAutoValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 3 );
|
||||||
|
else {
|
||||||
|
this.log( "Error: value for thermostat current heating cooling state not in offValues, heatValues, coolValues or autoValues" );
|
||||||
|
callback( null, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
setThermostatCurrentHeatingCoolingState: function(state, callback) {
|
||||||
|
this.log("Setting thermostat current heating cooling state to %s", state);
|
||||||
|
|
||||||
|
var ref = this.config.controlRef;
|
||||||
|
var value = 0;
|
||||||
|
if( state == 0 )
|
||||||
|
value = this.config.controlOffValue;
|
||||||
|
else if( state == 1 )
|
||||||
|
value = this.config.controlHeatValue;
|
||||||
|
else if( state == 2 )
|
||||||
|
value = this.config.controlCoolValue;
|
||||||
|
else if( state == 3 )
|
||||||
|
value = this.config.controlAutoValue;
|
||||||
|
|
||||||
|
var url = this.access_url + "request=controldevicebyvalue&ref=" + ref + "&value=" + value;
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer set thermostat current heating cooling state function failed: %s', error.message);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HomeSeer set thermostat current heating cooling state function succeeded!');
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getThermostatTargetTemperature: function(callback) {
|
||||||
|
var ref = this.config.setPointRef;
|
||||||
|
var url = this.access_url + "request=getstatus&ref=" + ref;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get thermostat target temperature function failed: %s', error.message);
|
||||||
|
callback( error, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
|
||||||
|
this.log('HomeSeer get thermostat target temperature function succeeded: value=' + value );
|
||||||
|
if( this.config.temperatureUnit == "F" ) {
|
||||||
|
value = (value-32)*5/9;
|
||||||
|
}
|
||||||
|
callback( null, value );
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
setThermostatTargetTemperature: function(temperature, callback) {
|
||||||
|
this.log("Setting thermostat target temperature to %s", temperature);
|
||||||
|
if( this.config.temperatureUnit == "F" ) {
|
||||||
|
temperature = temperature*9/5+32;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ref = this.config.setPointRef;
|
||||||
|
var url = this.access_url + "request=controldevicebyvalue&ref=" + ref + "&value=" + temperature;
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer set thermostat target temperature function failed: %s', error.message);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HomeSeer set thermostat target temperature function succeeded!');
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getThermostatTemperatureDisplayUnits: function(callback) {
|
||||||
|
if( this.config.temperatureUnit == "F" )
|
||||||
|
callback( null, 1 );
|
||||||
|
else
|
||||||
|
callback( null, 0 );
|
||||||
|
},
|
||||||
|
|
||||||
|
getLowBatteryStatus: function(callback) {
|
||||||
|
var ref = this.config.batteryRef;
|
||||||
|
var url = this.access_url + "request=getstatus&ref=" + ref;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get battery status function failed: %s', error.message);
|
||||||
|
callback( error, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
var minValue = 10;
|
||||||
|
|
||||||
|
this.log('HomeSeer get battery status function succeeded: value=' + value );
|
||||||
|
if( this.config.batteryThreshold ) {
|
||||||
|
minValue = this.config.batteryThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( value > minValue )
|
||||||
|
callback( null, 0 );
|
||||||
|
else
|
||||||
|
callback( null, 1 );
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentDoorState: function(callback) {
|
||||||
|
var ref = this.config.stateRef;
|
||||||
|
var url = this.access_url + "request=getstatus&ref=" + ref;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get current door state function failed: %s', error.message);
|
||||||
|
callback( error, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
|
||||||
|
this.log('HomeSeer get target door state function succeeded: value=' + value );
|
||||||
|
if( this.config.stateOpenValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 0 );
|
||||||
|
else if( this.config.stateClosedValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 1 );
|
||||||
|
else if( this.config.stateOpeningValues && this.config.stateOpeningValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 2 );
|
||||||
|
else if( this.config.stateClosingValues && this.config.stateClosingValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 3 );
|
||||||
|
else if( this.config.stateStoppedValues && this.config.stateStoppedValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 4 );
|
||||||
|
else {
|
||||||
|
this.log( "Error: value for current door state not in stateO0penValues, stateClosedValues, stateOpeningValues, stateClosingValues, stateStoppedValues" );
|
||||||
|
callback( null, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
setTargetDoorState: function(state, callback) {
|
||||||
|
this.log("Setting target door state state to %s", state);
|
||||||
|
|
||||||
|
var ref = this.config.controlRef;
|
||||||
|
var value = 0;
|
||||||
|
if( state == 0 )
|
||||||
|
value = this.config.controlOpenValue;
|
||||||
|
else if( state == 1 )
|
||||||
|
value = this.config.controlCloseValue;
|
||||||
|
|
||||||
|
var url = this.access_url + "request=controldevicebyvalue&ref=" + ref + "&value=" + value;
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer set target door state function failed: %s', error.message);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HomeSeer set target door state function succeeded!');
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getObstructionDetected: function(callback) {
|
||||||
|
if( this.config.obstructionRef ) {
|
||||||
|
var ref = this.config.obstructionRef;
|
||||||
|
var url = this.access_url + "request=getstatus&ref=" + ref;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get obstruction detected function failed: %s', error.message);
|
||||||
|
callback( error, 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
|
||||||
|
this.log('HomeSeer get obstruction detected function succeeded: value=' + value );
|
||||||
|
if( this.config.obstructionValues && this.config.obstructionValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 1 );
|
||||||
|
else {
|
||||||
|
callback( null, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback( null, 0 );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getLockCurrentState: function(callback) {
|
||||||
|
var ref = this.config.lockRef;
|
||||||
|
var url = this.access_url + "request=getstatus&ref=" + ref;
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer get lock current state function failed: %s', error.message);
|
||||||
|
callback( error, 3 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var status = JSON.parse( body );
|
||||||
|
var value = status.Devices[0].value;
|
||||||
|
|
||||||
|
this.log('HomeSeer get lock current state function succeeded: value=' + value );
|
||||||
|
if( this.config.lockUnsecuredValues && this.config.lockUnsecuredValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 0 );
|
||||||
|
else if( this.config.lockSecuredValues && this.config.lockSecuredValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 1 );
|
||||||
|
else if( this.config.lockJammedValues && this.config.lockJammedValues.indexOf(value) != -1 )
|
||||||
|
callback( null, 2 );
|
||||||
|
else {
|
||||||
|
callback( null, 3 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
setLockTargetState: function(state, callback) {
|
||||||
|
this.log("Setting target lock state state to %s", state);
|
||||||
|
|
||||||
|
var ref = this.config.lockRef;
|
||||||
|
var value = 0;
|
||||||
|
if( state == 0 && this.config.unlockValue )
|
||||||
|
value = this.config.unlockValue;
|
||||||
|
else if( state == 1 && this.config.lockValue )
|
||||||
|
value = this.config.lockValue;
|
||||||
|
|
||||||
|
var url = this.access_url + "request=controldevicebyvalue&ref=" + ref + "&value=" + value;
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer set target lock state function failed: %s', error.message);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HomeSeer set target lock state function succeeded!');
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getPositionState: function(callback) {
|
||||||
|
callback( null, 2 ); // Temporarily return STOPPED. TODO: full door support
|
||||||
|
},
|
||||||
|
|
||||||
getServices: function() {
|
getServices: function() {
|
||||||
var services = []
|
var services = []
|
||||||
|
|
||||||
@@ -282,15 +758,53 @@ HomeSeerAccessory.prototype = {
|
|||||||
var temperatureSensorService = new Service.TemperatureSensor();
|
var temperatureSensorService = new Service.TemperatureSensor();
|
||||||
temperatureSensorService
|
temperatureSensorService
|
||||||
.getCharacteristic(Characteristic.CurrentTemperature)
|
.getCharacteristic(Characteristic.CurrentTemperature)
|
||||||
.on('get', this.getValue.bind(this));
|
.on('get', this.getTemperature.bind(this));
|
||||||
|
temperatureSensorService
|
||||||
|
.getCharacteristic(Characteristic.CurrentTemperature).setProps( {minValue: -100} );
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
temperatureSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
services.push( temperatureSensorService );
|
services.push( temperatureSensorService );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "CarbonMonoxideSensor": {
|
||||||
|
var carbonMonoxideSensorService = new Service.CarbonMonoxideSensor();
|
||||||
|
carbonMonoxideSensorService
|
||||||
|
.getCharacteristic(Characteristic.CarbonMonoxideDetected)
|
||||||
|
.on('get', this.getBinarySensorState.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
carbonMonoxideSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
|
services.push( carbonMonoxideSensorService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "CarbonDioxideSensor": {
|
||||||
|
var carbonDioxideSensorService = new Service.CarbonDioxideSensor();
|
||||||
|
carbonDioxideSensorService
|
||||||
|
.getCharacteristic(Characteristic.CarbonDioxideDetected)
|
||||||
|
.on('get', this.getBinarySensorState.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
carbonDioxideSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
|
services.push( carbonDioxideSensorService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "ContactSensor": {
|
case "ContactSensor": {
|
||||||
var contactSensorService = new Service.ContactSensor();
|
var contactSensorService = new Service.ContactSensor();
|
||||||
contactSensorService
|
contactSensorService
|
||||||
.getCharacteristic(Characteristic.ContactSensorState)
|
.getCharacteristic(Characteristic.ContactSensorState)
|
||||||
.on('get', this.getPowerState.bind(this));
|
.on('get', this.getBinarySensorState.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
contactSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
services.push( contactSensorService );
|
services.push( contactSensorService );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -298,7 +812,12 @@ HomeSeerAccessory.prototype = {
|
|||||||
var motionSensorService = new Service.MotionSensor();
|
var motionSensorService = new Service.MotionSensor();
|
||||||
motionSensorService
|
motionSensorService
|
||||||
.getCharacteristic(Characteristic.MotionDetected)
|
.getCharacteristic(Characteristic.MotionDetected)
|
||||||
.on('get', this.getPowerState.bind(this));
|
.on('get', this.getBinarySensorState.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
motionSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
services.push( motionSensorService );
|
services.push( motionSensorService );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -306,23 +825,25 @@ HomeSeerAccessory.prototype = {
|
|||||||
var leakSensorService = new Service.LeakSensor();
|
var leakSensorService = new Service.LeakSensor();
|
||||||
leakSensorService
|
leakSensorService
|
||||||
.getCharacteristic(Characteristic.LeakDetected)
|
.getCharacteristic(Characteristic.LeakDetected)
|
||||||
.on('get', this.getPowerState.bind(this));
|
.on('get', this.getBinarySensorState.bind(this));
|
||||||
services.push( leakSensorService );
|
if( this.config.batteryRef ) {
|
||||||
break;
|
leakSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
}
|
}
|
||||||
case "LightSensor": {
|
services.push( leakSensorService );
|
||||||
var lightSensorService = new Service.LightSensor();
|
|
||||||
lightSensorService
|
|
||||||
.getCharacteristic(Characteristic.CurrentAmbientLightLevel)
|
|
||||||
.on('get', this.getValue.bind(this));
|
|
||||||
services.push( lightSensorService );
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "OccupancySensor": {
|
case "OccupancySensor": {
|
||||||
var occupancySensorService = new Service.OccupancySensor();
|
var occupancySensorService = new Service.OccupancySensor();
|
||||||
motionSensorService
|
occupancySensorService
|
||||||
.getCharacteristic(Characteristic.OccupancyDetected)
|
.getCharacteristic(Characteristic.OccupancyDetected)
|
||||||
.on('get', this.getPowerState.bind(this));
|
.on('get', this.getBinarySensorState.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
occupancySensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
services.push( occupancySensorService );
|
services.push( occupancySensorService );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -330,10 +851,41 @@ HomeSeerAccessory.prototype = {
|
|||||||
var smokeSensorService = new Service.SmokeSensor();
|
var smokeSensorService = new Service.SmokeSensor();
|
||||||
smokeSensorService
|
smokeSensorService
|
||||||
.getCharacteristic(Characteristic.SmokeDetected)
|
.getCharacteristic(Characteristic.SmokeDetected)
|
||||||
.on('get', this.getPowerState.bind(this));
|
.on('get', this.getBinarySensorState.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
smokeSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
services.push( smokeSensorService );
|
services.push( smokeSensorService );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "LightSensor": {
|
||||||
|
var lightSensorService = new Service.LightSensor();
|
||||||
|
lightSensorService
|
||||||
|
.getCharacteristic(Characteristic.CurrentAmbientLightLevel)
|
||||||
|
.on('get', this.getValue.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
lightSensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
|
services.push( lightSensorService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "HumiditySensor": {
|
||||||
|
var humiditySensorService = new Service.HumiditySensor();
|
||||||
|
humiditySensorService
|
||||||
|
.getCharacteristic(Characteristic.CurrentRelativeHumidity)
|
||||||
|
.on('get', this.getValue.bind(this));
|
||||||
|
if( this.config.batteryRef ) {
|
||||||
|
humiditySensorService
|
||||||
|
.addCharacteristic(new Characteristic.StatusLowBattery())
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
}
|
||||||
|
services.push( humiditySensorService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "Door": {
|
case "Door": {
|
||||||
var doorService = new Service.Door();
|
var doorService = new Service.Door();
|
||||||
doorService
|
doorService
|
||||||
@@ -342,9 +894,87 @@ HomeSeerAccessory.prototype = {
|
|||||||
doorService
|
doorService
|
||||||
.getCharacteristic(Characteristic.TargetPosition)
|
.getCharacteristic(Characteristic.TargetPosition)
|
||||||
.on('set', this.setValue.bind(this));
|
.on('set', this.setValue.bind(this));
|
||||||
|
doorService
|
||||||
|
.getCharacteristic(Characteristic.PositionState)
|
||||||
|
.on('get', this.getPositionState.bind(this));
|
||||||
services.push( doorService );
|
services.push( doorService );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "Battery": {
|
||||||
|
this.config.batteryRef = this.ref;
|
||||||
|
var batteryService = new Service.BatteryService();
|
||||||
|
batteryService
|
||||||
|
.getCharacteristic(Characteristic.BatteryLevel)
|
||||||
|
.on('get', this.getValue.bind(this));
|
||||||
|
batteryService
|
||||||
|
.getCharacteristic(Characteristic.StatusLowBattery)
|
||||||
|
.on('get', this.getLowBatteryStatus.bind(this));
|
||||||
|
services.push( batteryService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Thermostat": {
|
||||||
|
var thermostatService = new Service.Thermostat();
|
||||||
|
thermostatService
|
||||||
|
.getCharacteristic(Characteristic.CurrentTemperature)
|
||||||
|
.on('get', this.getTemperature.bind(this));
|
||||||
|
thermostatService
|
||||||
|
.getCharacteristic(Characteristic.TargetTemperature)
|
||||||
|
.on('get', this.getThermostatTargetTemperature.bind(this));
|
||||||
|
if( this.config.setPointReadOnly === null || this.config.setPointReadOnly === false )
|
||||||
|
thermostatService
|
||||||
|
.getCharacteristic(Characteristic.TargetTemperature)
|
||||||
|
.on('set', this.setThermostatTargetTemperature.bind(this));
|
||||||
|
thermostatService
|
||||||
|
.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
|
||||||
|
.on('get', this.getThermostatCurrentHeatingCoolingState.bind(this));
|
||||||
|
thermostatService
|
||||||
|
.getCharacteristic(Characteristic.TargetHeatingCoolingState)
|
||||||
|
.on('get', this.getThermostatCurrentHeatingCoolingState.bind(this));
|
||||||
|
thermostatService
|
||||||
|
.getCharacteristic(Characteristic.TargetHeatingCoolingState)
|
||||||
|
.on('set', this.setThermostatCurrentHeatingCoolingState.bind(this));
|
||||||
|
thermostatService
|
||||||
|
.getCharacteristic(Characteristic.TemperatureDisplayUnits)
|
||||||
|
.on('get', this.getThermostatTemperatureDisplayUnits.bind(this));
|
||||||
|
|
||||||
|
services.push( thermostatService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "GarageDoorOpener": {
|
||||||
|
var garageDoorOpenerService = new Service.GarageDoorOpener();
|
||||||
|
garageDoorOpenerService
|
||||||
|
.getCharacteristic(Characteristic.CurrentDoorState)
|
||||||
|
.on('get', this.getCurrentDoorState.bind(this));
|
||||||
|
garageDoorOpenerService
|
||||||
|
.getCharacteristic(Characteristic.TargetDoorState)
|
||||||
|
.on('set', this.setTargetDoorState.bind(this));
|
||||||
|
garageDoorOpenerService
|
||||||
|
.getCharacteristic(Characteristic.ObstructionDetected)
|
||||||
|
.on('get', this.getObstructionDetected.bind(this));
|
||||||
|
if( this.config.lockRef ) {
|
||||||
|
garageDoorOpenerService
|
||||||
|
.addCharacteristic(new Characteristic.LockCurrentState())
|
||||||
|
.on('get', this.getLockCurrentState.bind(this));
|
||||||
|
garageDoorOpenerService
|
||||||
|
.addCharacteristic(new Characteristic.LockTargetState())
|
||||||
|
.on('set', this.setLockTargetState.bind(this));
|
||||||
|
}
|
||||||
|
services.push( garageDoorOpenerService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Lock": {
|
||||||
|
this.config.lockRef = this.ref;
|
||||||
|
var lockService = new Service.LockMechanism();
|
||||||
|
lockService
|
||||||
|
.getCharacteristic(Characteristic.LockCurrentState)
|
||||||
|
.on('get', this.getLockCurrentState.bind(this));
|
||||||
|
lockService
|
||||||
|
.getCharacteristic(Characteristic.LockTargetState)
|
||||||
|
.on('set', this.setLockTargetState.bind(this));
|
||||||
|
services.push( lockService );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:{
|
default:{
|
||||||
var lightbulbService = new Service.Lightbulb();
|
var lightbulbService = new Service.Lightbulb();
|
||||||
lightbulbService
|
lightbulbService
|
||||||
@@ -366,5 +996,71 @@ HomeSeerAccessory.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.accessory = HomeSeerAccessory;
|
function HomeSeerEvent(log, platformConfig, eventConfig ) {
|
||||||
|
this.log = log;
|
||||||
|
this.config = eventConfig;
|
||||||
|
this.name = eventConfig.eventName
|
||||||
|
this.model = "HomeSeer Event";
|
||||||
|
|
||||||
|
this.access_url = platformConfig["host"] + "/JSON?";
|
||||||
|
this.on_url = this.access_url + "request=runevent&group=" + encodeURIComponent(this.config.eventGroup) + "&name=" + encodeURIComponent(this.config.eventName);
|
||||||
|
|
||||||
|
if( this.config.offEventGroup && this.config.offEventName ) {
|
||||||
|
this.off_url = this.access_url + "request=runevent&group=" + encodeURIComponent(this.config.offEventGroup) + "&name=" + encodeURIComponent(this.config.offEventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( this.config.name )
|
||||||
|
this.name = this.config.name;
|
||||||
|
|
||||||
|
if( this.config.uuid_base )
|
||||||
|
this.uuid_base = this.config.uuid_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeSeerEvent.prototype = {
|
||||||
|
|
||||||
|
identify: function(callback) {
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
|
||||||
|
launchEvent: function(value, callback) {
|
||||||
|
this.log("Setting event value to %s", value);
|
||||||
|
|
||||||
|
var url = this.on_url;
|
||||||
|
if( value == 0 && this.off_url ) {
|
||||||
|
url = this.off_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
httpRequest(url, 'GET', function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
this.log('HomeSeer run event function failed: %s', error.message);
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.log('HomeSeer run event function succeeded!');
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getServices: function() {
|
||||||
|
var services = []
|
||||||
|
|
||||||
|
var informationService = new Service.AccessoryInformation();
|
||||||
|
informationService
|
||||||
|
.setCharacteristic(Characteristic.Manufacturer, "HomeSeer")
|
||||||
|
.setCharacteristic(Characteristic.Model, this.model )
|
||||||
|
.setCharacteristic(Characteristic.SerialNumber, "HS Event " + this.config.eventGroup + " " + this.config.eventName);
|
||||||
|
services.push( informationService );
|
||||||
|
|
||||||
|
var switchService = new Service.Switch();
|
||||||
|
switchService
|
||||||
|
.getCharacteristic(Characteristic.On)
|
||||||
|
.on('set', this.launchEvent.bind(this));
|
||||||
|
services.push( switchService );
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.platform = HomeSeerPlatform;
|
module.exports.platform = HomeSeerPlatform;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var xml2js = require('xml2js');
|
var xml2js = require('xml2js');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|||||||
541
platforms/Indigo.js
Normal file
541
platforms/Indigo.js
Normal file
@@ -0,0 +1,541 @@
|
|||||||
|
// Indigo Platform Shim for HomeBridge
|
||||||
|
// Written by Mike Riccio (https://github.com/webdeck)
|
||||||
|
// Based on many of the other HomeBridge plartform modules
|
||||||
|
// See http://www.indigodomo.com/ for more info on Indigo
|
||||||
|
//
|
||||||
|
// Remember to add platform to config.json. Example:
|
||||||
|
// "platforms": [
|
||||||
|
// {
|
||||||
|
// "platform": "Indigo", // required
|
||||||
|
// "name": "Indigo", // required
|
||||||
|
// "host": "127.0.0.1", // required
|
||||||
|
// "port": "8176", // required
|
||||||
|
// "username": "username", // optional
|
||||||
|
// "password": "password" // optional
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
//
|
||||||
|
// When you attempt to add a device, it will ask for a "PIN code".
|
||||||
|
// The default code for all HomeBridge accessories is 031-45-154.
|
||||||
|
//
|
||||||
|
|
||||||
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
|
var request = require('request');
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
|
||||||
|
function IndigoPlatform(log, config) {
|
||||||
|
this.log = log;
|
||||||
|
|
||||||
|
this.baseURL = "http://" + config["host"] + ":" + config["port"];
|
||||||
|
|
||||||
|
if (config["username"] && config["password"]) {
|
||||||
|
this.auth = {
|
||||||
|
'user': config["username"],
|
||||||
|
'pass': config["password"],
|
||||||
|
'sendImmediately': false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IndigoPlatform.prototype = {
|
||||||
|
accessories: function(callback) {
|
||||||
|
var that = this;
|
||||||
|
this.log("Discovering Indigo Devices.");
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
url: this.baseURL + "/devices.json/",
|
||||||
|
method: 'GET'
|
||||||
|
};
|
||||||
|
if (this.auth) {
|
||||||
|
options['auth'] = this.auth;
|
||||||
|
}
|
||||||
|
this.foundAccessories = [];
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
request(options, function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
console.trace("Requesting Indigo devices.");
|
||||||
|
that.log(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cheesy hack because response may have an extra comma at the start of the array, which is invalid
|
||||||
|
var firstComma = body.indexOf(",");
|
||||||
|
if (firstComma < 10) {
|
||||||
|
body = "[" + body.substr(firstComma + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = JSON.parse(body);
|
||||||
|
async.each(json, function(item, asyncCallback) {
|
||||||
|
var deviceURL = that.baseURL + item.restURL;
|
||||||
|
var deviceOptions = {
|
||||||
|
url: deviceURL,
|
||||||
|
method: 'GET'
|
||||||
|
};
|
||||||
|
if (that.auth) {
|
||||||
|
deviceOptions['auth'] = that.auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
request(deviceOptions, function(deviceError, deviceResponse, deviceBody) {
|
||||||
|
if (deviceError) {
|
||||||
|
console.trace("Requesting Indigo device info: " + deviceURL + "\nError: " + deviceError + "\nResponse: " + deviceBody);
|
||||||
|
asyncCallback(deviceError)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var deviceJson = JSON.parse(deviceBody);
|
||||||
|
that.log("Discovered " + deviceJson.type + ": " + deviceJson.name);
|
||||||
|
that.foundAccessories.push(
|
||||||
|
new IndigoAccessory(that.log, that.auth, deviceURL, deviceJson));
|
||||||
|
asyncCallback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, function(asyncError) {
|
||||||
|
// This will be called after all the requests complete
|
||||||
|
if (asyncError) {
|
||||||
|
console.trace("Requesting Indigo device info.");
|
||||||
|
that.log(asyncError);
|
||||||
|
}
|
||||||
|
|
||||||
|
that.callback(that.foundAccessories.sort(function (a,b) {
|
||||||
|
return (a.name > b.name) - (a.name < b.name);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function IndigoAccessory(log, auth, deviceURL, json) {
|
||||||
|
this.log = log;
|
||||||
|
this.auth = auth;
|
||||||
|
this.deviceURL = deviceURL;
|
||||||
|
|
||||||
|
for (var prop in json) {
|
||||||
|
if (json.hasOwnProperty(prop)) {
|
||||||
|
this[prop] = json[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IndigoAccessory.prototype = {
|
||||||
|
getStatus: function(callback) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
url: this.deviceURL,
|
||||||
|
method: 'GET'
|
||||||
|
};
|
||||||
|
if (this.auth) {
|
||||||
|
options['auth'] = this.auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
request(options, function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
console.trace("Requesting Device Status.");
|
||||||
|
that.log(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
that.log("getStatus of " + that.name + ": " + body);
|
||||||
|
callback(JSON.parse(body));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStatus: function(params) {
|
||||||
|
var that = this;
|
||||||
|
var options = {
|
||||||
|
url: this.deviceURL + "?" + params,
|
||||||
|
method: 'PUT'
|
||||||
|
};
|
||||||
|
if (this.auth) {
|
||||||
|
options['auth'] = this.auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log("updateStatus of " + that.name + ": " + params);
|
||||||
|
request(options, function(error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
console.trace("Updating Device Status.");
|
||||||
|
that.log(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
query: function(prop, callback) {
|
||||||
|
this.getStatus(function(json) {
|
||||||
|
callback(json[prop]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
turnOn: function() {
|
||||||
|
if (this.typeSupportsOnOff) {
|
||||||
|
this.updateStatus("isOn=1");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
turnOff: function() {
|
||||||
|
if (this.typeSupportsOnOff) {
|
||||||
|
this.updateStatus("isOn=0");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setBrightness: function(brightness) {
|
||||||
|
if (this.typeSupportsDim && brightness >= 0 && brightness <= 100) {
|
||||||
|
this.updateStatus("brightness=" + brightness);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setSpeedIndex: function(speedIndex) {
|
||||||
|
if (this.typeSupportsSpeedControl && speedIndex >= 0 && speedIndex <= 3) {
|
||||||
|
this.updateStatus("speedIndex=" + speedIndex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentHeatingCooling: function(callback) {
|
||||||
|
this.getStatus(function(json) {
|
||||||
|
var mode = 0;
|
||||||
|
if (json["hvacOperatonModeIsHeat"]) {
|
||||||
|
mode = 1;
|
||||||
|
}
|
||||||
|
else if (json["hvacOperationModeIsCool"]) {
|
||||||
|
mode = 2;
|
||||||
|
}
|
||||||
|
else if (json["hvacOperationModeIsAuto"]) {
|
||||||
|
mode = 3;
|
||||||
|
}
|
||||||
|
callback(mode);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setTargetHeatingCooling: function(mode) {
|
||||||
|
if (mode == 0) {
|
||||||
|
param = "Off";
|
||||||
|
}
|
||||||
|
else if (mode == 1) {
|
||||||
|
param = "Heat";
|
||||||
|
}
|
||||||
|
else if (mode == 2) {
|
||||||
|
param = "Cool";
|
||||||
|
}
|
||||||
|
else if (mode == 3) {
|
||||||
|
param = "Auto";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param) {
|
||||||
|
this.updateStatus("hvacOperationModeIs" + param + "=true");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Note: HomeKit wants all temperature values to be in celsius
|
||||||
|
getCurrentTemperature: function(callback) {
|
||||||
|
this.query("displayRawState", function(temperature) {
|
||||||
|
callback((temperature - 32.0) * 5.0 / 9.0);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getTargetTemperature: function(callback) {
|
||||||
|
this.getStatus(function(json) {
|
||||||
|
var temperature;
|
||||||
|
if (json["hvacOperatonModeIsHeat"]) {
|
||||||
|
temperature = json["setpointHeat"];
|
||||||
|
}
|
||||||
|
else if (json["hvacOperationModeIsCool"]) {
|
||||||
|
temperature = json["setpointCool"];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
temperature = (json["setpointHeat"] + json["setpointCool"]) / 2.0;
|
||||||
|
}
|
||||||
|
callback((temperature - 32.0) * 5.0 / 9.0);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setTargetTemperature: function(temperature) {
|
||||||
|
var that = this;
|
||||||
|
var t = (temperature * 9.0 / 5.0) + 32.0;
|
||||||
|
this.getStatus(function(json) {
|
||||||
|
if (json["hvacOperatonModeIsHeat"]) {
|
||||||
|
that.updateStatus("setpointHeat=" + t);
|
||||||
|
}
|
||||||
|
else if (json["hvacOperationModeIsCool"]) {
|
||||||
|
that.updateStatus("setpointCool=" + t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var cool = t + 5;
|
||||||
|
var heat = t - 5;
|
||||||
|
that.updateStatus("setpointCool=" + cool + "&setpointHeat=" + heat);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
informationCharacteristics: function() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: [Characteristic.Perms.READ],
|
||||||
|
format: Characteristic.Formats.STRING,
|
||||||
|
initialValue: this.name,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of the accessory",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MANUFACTURER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: [Characteristic.Perms.READ],
|
||||||
|
format: Characteristic.Formats.STRING,
|
||||||
|
initialValue: "Indigo",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Manufacturer",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MODEL_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: [Characteristic.Perms.READ],
|
||||||
|
format: Characteristic.Formats.STRING,
|
||||||
|
initialValue: this.type,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Model",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.SERIAL_NUMBER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: [Characteristic.Perms.READ],
|
||||||
|
format: Characteristic.Formats.STRING,
|
||||||
|
initialValue: this.addressStr,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "SN",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.IDENTIFY_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: [Characteristic.Perms.WRITE],
|
||||||
|
format: Characteristic.Formats.BOOL,
|
||||||
|
initialValue: false,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Identify Accessory",
|
||||||
|
designedMaxLength: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
controlCharacteristics: function(that) {
|
||||||
|
var hasAType = false;
|
||||||
|
|
||||||
|
var cTypes = [{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: [Characteristic.Perms.READ],
|
||||||
|
format: Characteristic.Formats.STRING,
|
||||||
|
initialValue: that.name,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of the accessory",
|
||||||
|
designedMaxLength: 255
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (that.typeSupportsDim) {
|
||||||
|
hasAType = true;
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.BRIGHTNESS_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.INT,
|
||||||
|
initialValue: that.brightness,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Adjust Brightness of Light",
|
||||||
|
designedMinValue: 0,
|
||||||
|
designedMaxValue: 100,
|
||||||
|
designedMinStep: 1,
|
||||||
|
unit: Characteristic.Units.PERCENTAGE,
|
||||||
|
onUpdate: function(value) {
|
||||||
|
that.setBrightness(value);
|
||||||
|
},
|
||||||
|
onRead: function(callback) {
|
||||||
|
that.query("brightness", callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (that.typeSupportsSpeedControl) {
|
||||||
|
hasAType = true;
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.ROTATION_SPEED_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.INT,
|
||||||
|
initialValue: 0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Change the speed of the fan",
|
||||||
|
designedMaxLength: 1,
|
||||||
|
designedMinValue: 0,
|
||||||
|
designedMaxValue: 3,
|
||||||
|
designedMinStep: 1,
|
||||||
|
onUpdate: function(value) {
|
||||||
|
that.setSpeedIndex(value);
|
||||||
|
},
|
||||||
|
onRead: function(callback) {
|
||||||
|
that.query("speedIndex", callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (that.typeSupportsHVAC) {
|
||||||
|
hasAType = true;
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.CURRENTHEATINGCOOLING_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.INT,
|
||||||
|
initialValue: 0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Current Mode",
|
||||||
|
designedMaxLength: 1,
|
||||||
|
designedMinValue: 0,
|
||||||
|
designedMaxValue: 3,
|
||||||
|
designedMinStep: 1,
|
||||||
|
onUpdate: null,
|
||||||
|
onRead: function(callback) {
|
||||||
|
that.getCurrentHeatingCooling(callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.TARGETHEATINGCOOLING_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.INT,
|
||||||
|
initialValue: 0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Target Mode",
|
||||||
|
designedMaxLength: 1,
|
||||||
|
designedMinValue: 0,
|
||||||
|
designedMaxValue: 3,
|
||||||
|
designedMinStep: 1,
|
||||||
|
onUpdate: function(value) {
|
||||||
|
that.setTargetHeatingCooling(value);
|
||||||
|
},
|
||||||
|
onRead: function(callback) {
|
||||||
|
that.getCurrentHeatingCooling(callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.CURRENT_TEMPERATURE_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.INT,
|
||||||
|
designedMinValue: 16,
|
||||||
|
designedMaxValue: 38,
|
||||||
|
designedMinStep: 1,
|
||||||
|
initialValue: 20,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Current Temperature",
|
||||||
|
unit: Characteristic.Units.FAHRENHEIT,
|
||||||
|
onUpdate: null,
|
||||||
|
onRead: function(callback) {
|
||||||
|
that.getCurrentTemperature(callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.TARGET_TEMPERATURE_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.INT,
|
||||||
|
designedMinValue: 16,
|
||||||
|
designedMaxValue: 38,
|
||||||
|
designedMinStep: 1,
|
||||||
|
initialValue: 20,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Target Temperature",
|
||||||
|
unit: Characteristic.Units.FAHRENHEIT,
|
||||||
|
onUpdate: function(value) {
|
||||||
|
that.setTargetTemperature(value);
|
||||||
|
},
|
||||||
|
onRead: function(callback) {
|
||||||
|
that.getTargetTemperature(callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.TEMPERATURE_UNITS_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.INT,
|
||||||
|
initialValue: 1,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Unit",
|
||||||
|
onUpdate: null,
|
||||||
|
onRead: function(callback) {
|
||||||
|
callback(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (that.typeSupportsOnOff || !hasAType) {
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.POWER_STATE_CTYPE,
|
||||||
|
perms: [Characteristic.Perms.WRITE,Characteristic.Perms.READ,Characteristic.Perms.NOTIFY],
|
||||||
|
format: Characteristic.Formats.BOOL,
|
||||||
|
initialValue: (that.isOn) ? 1 : 0,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Change the power state",
|
||||||
|
designedMaxLength: 1,
|
||||||
|
onUpdate: function(value) {
|
||||||
|
if (value == 0) {
|
||||||
|
that.turnOff();
|
||||||
|
} else {
|
||||||
|
that.turnOn();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRead: function(callback) {
|
||||||
|
that.query("isOn", function(isOn) {
|
||||||
|
callback((isOn) ? 1 : 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return cTypes;
|
||||||
|
},
|
||||||
|
|
||||||
|
sType: function() {
|
||||||
|
if (this.typeSupportsHVAC) {
|
||||||
|
return types.THERMOSTAT_STYPE;
|
||||||
|
} else if (this.typeSupportsDim) {
|
||||||
|
return types.LIGHTBULB_STYPE;
|
||||||
|
} else if (this.typeSupportsSpeedControl) {
|
||||||
|
return types.FAN_STYPE;
|
||||||
|
} else if (this.typeSupportsOnOff) {
|
||||||
|
return types.SWITCH_STYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.SWITCH_STYPE;
|
||||||
|
},
|
||||||
|
|
||||||
|
getServices: function() {
|
||||||
|
var that = this;
|
||||||
|
var services = [{
|
||||||
|
sType: types.ACCESSORY_INFORMATION_STYPE,
|
||||||
|
characteristics: that.informationCharacteristics(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sType: that.sType(),
|
||||||
|
characteristics: that.controlCharacteristics(that)
|
||||||
|
}];
|
||||||
|
|
||||||
|
that.log("Loaded services for " + that.name);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.accessory = IndigoAccessory;
|
||||||
|
module.exports.platform = IndigoPlatform;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* based on Sonos platform
|
* based on Sonos platform
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
//var hardware = require('myHardwareSupport'); //require any additional hardware packages
|
//var hardware = require('myHardwareSupport'); //require any additional hardware packages
|
||||||
var knxd = require('eibd');
|
var knxd = require('eibd');
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
// The default code for all HomeBridge accessories is 031-45-154.
|
// The default code for all HomeBridge accessories is 031-45-154.
|
||||||
//
|
//
|
||||||
|
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var lifxRemoteObj = require('lifx-api');
|
var lifxRemoteObj = require('lifx-api');
|
||||||
var lifx_remote;
|
var lifx_remote;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
var types = require('HAP-NodeJS/accessories/types.js');
|
var types = require('hap-nodejs/accessories/types.js');
|
||||||
|
|
||||||
var harmonyDiscover = require('harmonyhubjs-discover');
|
var harmonyDiscover = require('harmonyhubjs-discover');
|
||||||
var harmony = require('harmonyhubjs-client');
|
var harmony = require('harmonyhubjs-client');
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ TODO:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var Milight = require('node-milight-promise').MilightController;
|
var Milight = require('node-milight-promise').MilightController;
|
||||||
var commands = require('node-milight-promise').commands;
|
var commands = require('node-milight-promise').commands;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var nest = require('unofficial-nest-api');
|
var nest = require('unofficial-nest-api');
|
||||||
|
|
||||||
function NestPlatform(log, config){
|
function NestPlatform(log, config){
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ var hue = require("node-hue-api"),
|
|||||||
HueApi = hue.HueApi,
|
HueApi = hue.HueApi,
|
||||||
lightState = hue.lightState;
|
lightState = hue.lightState;
|
||||||
|
|
||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
|
|
||||||
function PhilipsHuePlatform(log, config) {
|
function PhilipsHuePlatform(log, config) {
|
||||||
this.log = log;
|
this.log = log;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// SmartThings JSON API SmartApp required
|
// SmartThings JSON API SmartApp required
|
||||||
// https://github.com/jnewland/SmartThings/blob/master/JSON.groovy
|
// https://github.com/jnewland/SmartThings/blob/master/JSON.groovy
|
||||||
//
|
//
|
||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
|
|
||||||
function SmartThingsPlatform(log, config){
|
function SmartThingsPlatform(log, config){
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var sonos = require('sonos');
|
var sonos = require('sonos');
|
||||||
|
|
||||||
function SonosPlatform(log, config){
|
function SonosPlatform(log, config){
|
||||||
|
|||||||
265
platforms/Telldus.js
Normal file
265
platforms/Telldus.js
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
|
var telldus = require('telldus');
|
||||||
|
|
||||||
|
function TelldusPlatform(log, config) {
|
||||||
|
var that = this;
|
||||||
|
that.log = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
TelldusPlatform.prototype = {
|
||||||
|
|
||||||
|
accessories: function(callback) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.log("Fetching devices...");
|
||||||
|
|
||||||
|
var devices = telldus.getDevicesSync();
|
||||||
|
|
||||||
|
that.log("Found " + devices.length + " devices...");
|
||||||
|
|
||||||
|
var foundAccessories = [];
|
||||||
|
|
||||||
|
// Clean non device
|
||||||
|
for (var i = 0; i < devices.length; i++) {
|
||||||
|
if (devices[i].type != 'DEVICE') {
|
||||||
|
devices.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < devices.length; i++) {
|
||||||
|
if (devices[i].type === 'DEVICE') {
|
||||||
|
TelldusAccessory.create(that.log, devices[i], function(err, accessory) {
|
||||||
|
if (!!err) that.log("Couldn't load device info");
|
||||||
|
foundAccessories.push(accessory);
|
||||||
|
if (foundAccessories.length >= devices.length) {
|
||||||
|
callback(foundAccessories);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var TelldusAccessory = function TelldusAccessory(log, device) {
|
||||||
|
|
||||||
|
this.log = log;
|
||||||
|
|
||||||
|
var m = device.model.split(':');
|
||||||
|
|
||||||
|
this.dimTimeout = false;
|
||||||
|
|
||||||
|
// Set accessory info
|
||||||
|
this.device = device;
|
||||||
|
this.id = device.id;
|
||||||
|
this.name = device.name;
|
||||||
|
this.manufacturer = "Telldus"; // NOTE: Change this later
|
||||||
|
this.model = device.model;
|
||||||
|
this.status = device.status;
|
||||||
|
switch (device.status.name) {
|
||||||
|
case 'OFF':
|
||||||
|
this.state = 0;
|
||||||
|
this.stateValue = 0;
|
||||||
|
break;
|
||||||
|
case 'ON':
|
||||||
|
this.state = 2;
|
||||||
|
this.stateValue = 1;
|
||||||
|
break;
|
||||||
|
case 'DIM':
|
||||||
|
this.state = 16;
|
||||||
|
this.stateValue = device.status.level;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TelldusAccessory.create = function (log, device, callback) {
|
||||||
|
|
||||||
|
callback(null, new TelldusAccessory(log, device));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
TelldusAccessory.prototype = {
|
||||||
|
|
||||||
|
dimmerValue: function() {
|
||||||
|
|
||||||
|
if (this.state === 1) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state === 16 && this.stateValue != "unde") {
|
||||||
|
return parseInt(this.stateValue * 100 / 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
informationCharacteristics: function() {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
informationCharacteristics = [
|
||||||
|
{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: that.name,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of the accessory",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MANUFACTURER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: that.manufacturer,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Manufacturer",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.MODEL_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: that.model,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Model",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.SERIAL_NUMBER_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: "A1S2NASF88EW",
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "SN",
|
||||||
|
designedMaxLength: 255
|
||||||
|
},{
|
||||||
|
cType: types.IDENTIFY_CTYPE,
|
||||||
|
onUpdate: function () {
|
||||||
|
telldus.turnOff(that.id, function(err){
|
||||||
|
if (!!err) that.log("Error: " + err.message);
|
||||||
|
telldus.turnOn(that.id, function(err){
|
||||||
|
if (!!err) that.log("Error: " + err.message);
|
||||||
|
telldus.turnOff(that.id, function(err){
|
||||||
|
if (!!err) that.log("Error: " + err.message);
|
||||||
|
telldus.turnOn(that.id, function(err){
|
||||||
|
if (!!err) that.log("Error: " + err.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
perms: ["pw"],
|
||||||
|
format: "bool",
|
||||||
|
initialValue: false,
|
||||||
|
supportEvents: false,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Identify Accessory",
|
||||||
|
designedMaxLength: 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return informationCharacteristics;
|
||||||
|
},
|
||||||
|
|
||||||
|
controlCharacteristics: function() {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
cTypes = [{
|
||||||
|
cType: types.NAME_CTYPE,
|
||||||
|
onUpdate: null,
|
||||||
|
perms: ["pr"],
|
||||||
|
format: "string",
|
||||||
|
initialValue: that.name,
|
||||||
|
supportEvents: true,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Name of service",
|
||||||
|
designedMaxLength: 255
|
||||||
|
}]
|
||||||
|
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.POWER_STATE_CTYPE,
|
||||||
|
onUpdate: function(value) {
|
||||||
|
if (value) {
|
||||||
|
telldus.turnOn(that.id, function(err){
|
||||||
|
if (!!err) {
|
||||||
|
that.log("Error: " + err.message)
|
||||||
|
} else {
|
||||||
|
that.log(that.name + " - Updated power state: ON");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
telldus.turnOff(that.id, function(err){
|
||||||
|
if (!!err) {
|
||||||
|
that.log("Error: " + err.message)
|
||||||
|
} else {
|
||||||
|
that.log(that.name + " - Updated power state: OFF");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
perms: ["pw","pr","ev"],
|
||||||
|
format: "bool",
|
||||||
|
initialValue: (that.state != 2 && (that.state === 16 && that.stateValue != 0)) ? 1 : 0,
|
||||||
|
supportEvents: true,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Change the power state",
|
||||||
|
designedMaxLength: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
if (that.model === "selflearning-dimmer") {
|
||||||
|
cTypes.push({
|
||||||
|
cType: types.BRIGHTNESS_CTYPE,
|
||||||
|
onUpdate: function (value) {
|
||||||
|
if (that.dimTimeout) {
|
||||||
|
clearTimeout(that.dimTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
that.dimTimeout = setTimeout(function(){
|
||||||
|
telldus.dim(that.id, (255 * (value / 100)), function(err, result){
|
||||||
|
if (!!err) {
|
||||||
|
that.log("Error: " + err.message);
|
||||||
|
} else {
|
||||||
|
that.log(that.name + " - Updated brightness: " + value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
that.dimTimeout = false;
|
||||||
|
}, 250);
|
||||||
|
},
|
||||||
|
perms: ["pw", "pr", "ev"],
|
||||||
|
format: "int",
|
||||||
|
initialValue: that.dimmerValue(),
|
||||||
|
supportEvents: true,
|
||||||
|
supportBonjour: false,
|
||||||
|
manfDescription: "Adjust Brightness of Light",
|
||||||
|
designedMinValue: 0,
|
||||||
|
designedMaxValue: 100,
|
||||||
|
designedMinStep: 1,
|
||||||
|
unit: "%"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return cTypes
|
||||||
|
},
|
||||||
|
|
||||||
|
getServices: function() {
|
||||||
|
|
||||||
|
var services = [
|
||||||
|
{
|
||||||
|
sType: types.ACCESSORY_INFORMATION_STYPE,
|
||||||
|
characteristics: this.informationCharacteristics()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sType: types.LIGHTBULB_STYPE,
|
||||||
|
characteristics: this.controlCharacteristics()
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.platform = TelldusPlatform;
|
||||||
|
module.exports.accessory = TelldusAccessory;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var TellduAPI = require("telldus-live");
|
var TellduAPI = require("telldus-live");
|
||||||
|
|
||||||
function TelldusLivePlatform(log, config) {
|
function TelldusLivePlatform(log, config) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var wink = require('wink-js');
|
var wink = require('wink-js');
|
||||||
|
|
||||||
var model = {
|
var model = {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
var debug = require('debug')('YamahaAVR');
|
var debug = require('debug')('YamahaAVR');
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var Yamaha = require('yamaha-nodejs');
|
var Yamaha = require('yamaha-nodejs');
|
||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
var mdns = require('mdns');
|
var mdns = require('mdns');
|
||||||
@@ -31,24 +31,24 @@ function YamahaAVRPlatform(log, config){
|
|||||||
|
|
||||||
YamahaAVRPlatform.AudioVolume = function() {
|
YamahaAVRPlatform.AudioVolume = function() {
|
||||||
Characteristic.call(this, 'Audio Volume', '00001001-0000-1000-8000-135D67EC4377');
|
Characteristic.call(this, 'Audio Volume', '00001001-0000-1000-8000-135D67EC4377');
|
||||||
this.format = 'uint8';
|
this.setProps({
|
||||||
this.unit = 'percentage';
|
format: Characteristic.Formats.UINT8,
|
||||||
this.maximumValue = 100;
|
unit: Characteristic.Units.PERCENTAGE,
|
||||||
this.minimumValue = 0;
|
maxValue: 100,
|
||||||
this.stepValue = 1;
|
minValue: 0,
|
||||||
this.readable = true;
|
minStep: 1,
|
||||||
this.writable = true;
|
perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]
|
||||||
this.supportsEventNotification = true;
|
});
|
||||||
this.value = this.getDefaultValue();
|
this.value = this.getDefaultValue();
|
||||||
};
|
};
|
||||||
inherits(YamahaAVRPlatform.AudioVolume, Characteristic);
|
inherits(YamahaAVRPlatform.AudioVolume, Characteristic);
|
||||||
|
|
||||||
YamahaAVRPlatform.Muting = function() {
|
YamahaAVRPlatform.Muting = function() {
|
||||||
Characteristic.call(this, 'Muting', '00001002-0000-1000-8000-135D67EC4377');
|
Characteristic.call(this, 'Muting', '00001002-0000-1000-8000-135D67EC4377');
|
||||||
this.format = 'bool';
|
this.setProps({
|
||||||
this.readable = true;
|
format: Characteristic.Formats.UINT8,
|
||||||
this.writable = true;
|
perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]
|
||||||
this.supportsEventNotification = true;
|
});
|
||||||
this.value = this.getDefaultValue();
|
this.value = this.getDefaultValue();
|
||||||
};
|
};
|
||||||
inherits(YamahaAVRPlatform.Muting, Characteristic);
|
inherits(YamahaAVRPlatform.Muting, Characteristic);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
var debug = require('debug')('ZWayServer');
|
var debug = require('debug')('ZWayServer');
|
||||||
var Service = require("HAP-NodeJS").Service;
|
var Service = require("hap-nodejs").Service;
|
||||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
var Characteristic = require("hap-nodejs").Characteristic;
|
||||||
var types = require("HAP-NodeJS/accessories/types.js");
|
var types = require("hap-nodejs/accessories/types.js");
|
||||||
var request = require("request");
|
var request = require("request");
|
||||||
var tough = require('tough-cookie');
|
var tough = require('tough-cookie');
|
||||||
var Q = require("q");
|
var Q = require("q");
|
||||||
@@ -83,7 +83,20 @@ ZWayServerPlatform.prototype = {
|
|||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
|
getTagValue: function(vdev, tagStem){
|
||||||
|
if(!(vdev.tags && vdev.tags.length > 0)) return false;
|
||||||
|
var tagStem = "Homebridge." + tagStem;
|
||||||
|
if(vdev.tags.indexOf(tagStem) >= 0) return true;
|
||||||
|
var tags = vdev.tags, l = tags.length, tag;
|
||||||
|
for(var i = 0; i < l; i++){
|
||||||
|
tag = tags[i];
|
||||||
|
if(tag.indexOf(tagStem + ":") === 0){
|
||||||
|
return tag.substr(tagStem.length + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
,
|
||||||
accessories: function(callback) {
|
accessories: function(callback) {
|
||||||
debug("Fetching Z-Way devices...");
|
debug("Fetching Z-Way devices...");
|
||||||
|
|
||||||
@@ -110,21 +123,49 @@ ZWayServerPlatform.prototype = {
|
|||||||
var groupedDevices = {};
|
var groupedDevices = {};
|
||||||
for(var i = 0; i < devices.length; i++){
|
for(var i = 0; i < devices.length; i++){
|
||||||
var vdev = devices[i];
|
var vdev = devices[i];
|
||||||
if(vdev.tags.indexOf("Homebridge:Skip") >= 0) { debug("Tag says skip!"); continue; }
|
if(this.getTagValue("Skip")) { debug("Tag says skip!"); continue; }
|
||||||
if(this.opt_in && vdev.tags.indexOf("Homebridge:Include") < 0) continue;
|
if(this.opt_in && !this.getTagValue(vdev, "Include")) continue;
|
||||||
var gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2');
|
|
||||||
var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, extras: {}, primary: undefined});
|
var gdid = this.getTagValue(vdev, "Accessory.Id");
|
||||||
|
if(!gdid){
|
||||||
|
gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2');
|
||||||
|
}
|
||||||
|
|
||||||
|
var gd = groupedDevices[gdid] || (groupedDevices[gdid] = { devices: [], types: {}, extras: {}, primary: undefined, cxmap: {} });
|
||||||
|
|
||||||
gd.devices.push(vdev);
|
gd.devices.push(vdev);
|
||||||
|
var vdevIndex = gd.devices.length - 1;
|
||||||
|
|
||||||
var tk = ZWayServerPlatform.getVDevTypeKey(vdev);
|
var tk = ZWayServerPlatform.getVDevTypeKey(vdev);
|
||||||
|
|
||||||
|
// If this is explicitly set as primary, set it now...
|
||||||
|
if(this.getTagValue(vdev, "IsPrimary")){
|
||||||
|
// everybody out of the way! Can't be in "extras" if you're the primary...
|
||||||
|
if(gd.types[tk] !== undefined){
|
||||||
|
gd.extras[tk] = gd.extras[tk] || [];
|
||||||
|
gd.extras[tk].push(gd.types[tk]);
|
||||||
|
delete gd.types[tk]; // clear the way for this one to be set here below...
|
||||||
|
}
|
||||||
|
gd.primary = vdevIndex;
|
||||||
|
//gd.types[tk] = gd.primary;
|
||||||
|
}
|
||||||
|
|
||||||
if(gd.types[tk] === undefined){
|
if(gd.types[tk] === undefined){
|
||||||
gd.types[tk] = gd.devices.length - 1;
|
gd.types[tk] = vdevIndex;
|
||||||
} else {
|
} else {
|
||||||
gd.extras[tk] = gd.extras[tk] || [];
|
gd.extras[tk] = gd.extras[tk] || [];
|
||||||
gd.extras[tk].push(gd.devices.length - 1);
|
gd.extras[tk].push(vdevIndex);
|
||||||
|
}
|
||||||
|
if(tk !== vdev.deviceType) gd.types[vdev.deviceType] = vdevIndex; // also include the deviceType only as a possibility
|
||||||
|
|
||||||
|
// Create a map entry when Homebridge.Characteristic.Type is set...
|
||||||
|
var ctype = this.getTagValue(vdev, "Characteristic.Type");
|
||||||
|
if(ctype && Characteristic[ctype]){
|
||||||
|
var cx = new Characteristic[ctype]();
|
||||||
|
gd.cxmap[cx.UUID] = vdevIndex;
|
||||||
}
|
}
|
||||||
if(tk !== vdev.deviceType) gd.types[vdev.deviceType] = gd.devices.length - 1; // also include the deviceType only as a possibility
|
|
||||||
}
|
}
|
||||||
//TODO: Make a second pass, re-splitting any devices that don't make sense together
|
|
||||||
for(var gdid in groupedDevices) {
|
for(var gdid in groupedDevices) {
|
||||||
if(!groupedDevices.hasOwnProperty(gdid)) continue;
|
if(!groupedDevices.hasOwnProperty(gdid)) continue;
|
||||||
|
|
||||||
@@ -136,12 +177,17 @@ ZWayServerPlatform.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var accessory = null;
|
var accessory = null;
|
||||||
for(var ti = 0; ti < primaryDeviceClasses.length; ti++){
|
if(gd.primary !== undefined){
|
||||||
|
var pd = gd.devices[gd.primary];
|
||||||
|
var name = pd.metrics && pd.metrics.title ? pd.metrics.title : pd.id;
|
||||||
|
accessory = new ZWayServerAccessory(name, gd, that);
|
||||||
|
}
|
||||||
|
else for(var ti = 0; ti < primaryDeviceClasses.length; ti++){
|
||||||
if(gd.types[primaryDeviceClasses[ti]] !== undefined){
|
if(gd.types[primaryDeviceClasses[ti]] !== undefined){
|
||||||
gd.primary = gd.types[primaryDeviceClasses[ti]];
|
gd.primary = gd.types[primaryDeviceClasses[ti]];
|
||||||
var pd = gd.devices[gd.primary];
|
var pd = gd.devices[gd.primary];
|
||||||
var name = pd.metrics && pd.metrics.title ? pd.metrics.title : pd.id;
|
var name = pd.metrics && pd.metrics.title ? pd.metrics.title : pd.id;
|
||||||
debug("Using primary device with type " + primaryDeviceClasses[ti] + ", " + name + " (" + pd.id + ") as primary.");
|
//debug("Using primary device with type " + primaryDeviceClasses[ti] + ", " + name + " (" + pd.id + ") as primary.");
|
||||||
accessory = new ZWayServerAccessory(name, gd, that);
|
accessory = new ZWayServerAccessory(name, gd, that);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -153,7 +199,6 @@ ZWayServerPlatform.prototype = {
|
|||||||
foundAccessories.push(accessory);
|
foundAccessories.push(accessory);
|
||||||
|
|
||||||
}
|
}
|
||||||
//foundAccessories = foundAccessories.slice(0, 10); // Limit to a few devices for testing...
|
|
||||||
callback(foundAccessories);
|
callback(foundAccessories);
|
||||||
|
|
||||||
// Start the polling process...
|
// Start the polling process...
|
||||||
@@ -302,8 +347,13 @@ ZWayServerAccessory.prototype = {
|
|||||||
case "switchBinary":
|
case "switchBinary":
|
||||||
services.push(new Service.Switch(vdev.metrics.title, vdev.id));
|
services.push(new Service.Switch(vdev.metrics.title, vdev.id));
|
||||||
break;
|
break;
|
||||||
|
case "switchRGBW":
|
||||||
case "switchMultilevel":
|
case "switchMultilevel":
|
||||||
services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id));
|
if(this.platform.getTagValue(vdev, "Service.Type") === "Switch"){
|
||||||
|
services.push(new Service.Switch(vdev.metrics.title, vdev.id));
|
||||||
|
} else {
|
||||||
|
services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "sensorBinary.Door/Window":
|
case "sensorBinary.Door/Window":
|
||||||
services.push(new Service.GarageDoorOpener(vdev.metrics.title, vdev.id));
|
services.push(new Service.GarageDoorOpener(vdev.metrics.title, vdev.id));
|
||||||
@@ -317,6 +367,11 @@ ZWayServerAccessory.prototype = {
|
|||||||
case "sensorMultilevel.Luminiscence":
|
case "sensorMultilevel.Luminiscence":
|
||||||
services.push(new Service.LightSensor(vdev.metrics.title, vdev.id));
|
services.push(new Service.LightSensor(vdev.metrics.title, vdev.id));
|
||||||
break;
|
break;
|
||||||
|
case "sensorBinary":
|
||||||
|
var stype = this.platform.getTagValue(vdev, "Service.Type");
|
||||||
|
if(stype === "MotionSensor"){
|
||||||
|
services.push(new Service.MotionSensor(vdev.metrics.title, vdev.id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var validServices =[];
|
var validServices =[];
|
||||||
@@ -337,6 +392,12 @@ ZWayServerAccessory.prototype = {
|
|||||||
}
|
}
|
||||||
,
|
,
|
||||||
getVDevForCharacteristic: function(cx, vdevPreferred){
|
getVDevForCharacteristic: function(cx, vdevPreferred){
|
||||||
|
|
||||||
|
// If we know which vdev should be used for this Characteristic, we're done!
|
||||||
|
if(this.devDesc.cxmap[cx.UUID] !== undefined){
|
||||||
|
return this.devDesc.devices[this.devDesc.cxmap[cx.UUID]];
|
||||||
|
}
|
||||||
|
|
||||||
var map = this.uuidToTypeKeyMap;
|
var map = this.uuidToTypeKeyMap;
|
||||||
if(!map){
|
if(!map){
|
||||||
this.uuidToTypeKeyMap = map = {};
|
this.uuidToTypeKeyMap = map = {};
|
||||||
@@ -359,7 +420,7 @@ ZWayServerAccessory.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(cx instanceof Characteristic.Name) return vdevPreferred;
|
if(cx instanceof Characteristic.Name) return vdevPreferred;
|
||||||
|
|
||||||
// Special case!: If cx is a CurrentTemperature, ignore the preferred device...we want the sensor if available!
|
// Special case!: If cx is a CurrentTemperature, ignore the preferred device...we want the sensor if available!
|
||||||
if(cx instanceof Characteristic.CurrentTemperature) vdevPreferred = null;
|
if(cx instanceof Characteristic.CurrentTemperature) vdevPreferred = null;
|
||||||
//
|
//
|
||||||
@@ -398,10 +459,15 @@ ZWayServerAccessory.prototype = {
|
|||||||
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
callback(false, accessory.name);
|
callback(false, accessory.name);
|
||||||
});
|
});
|
||||||
cx.writable = false;
|
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We don't want to override "Name"'s name...so we just move this below that block.
|
||||||
|
var descOverride = this.platform.getTagValue(vdev, "Characteristic.Description");
|
||||||
|
if(descOverride){
|
||||||
|
cx.displayName = descOverride;
|
||||||
|
}
|
||||||
|
|
||||||
if(cx instanceof Characteristic.On){
|
if(cx instanceof Characteristic.On){
|
||||||
cx.zway_getValueFromVDev = function(vdev){
|
cx.zway_getValueFromVDev = function(vdev){
|
||||||
var val = false;
|
var val = false;
|
||||||
@@ -479,12 +545,6 @@ ZWayServerAccessory.prototype = {
|
|||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
cx.writeable = false;
|
|
||||||
//cx.on('set', function(level, callback){
|
|
||||||
// this.command(vdev, "exact", {level: "on", "color.r": 255, "color.g": 0, "color.b": 0}).then(function(result){
|
|
||||||
// callback();
|
|
||||||
// });
|
|
||||||
//}.bind(this));
|
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,12 +574,6 @@ ZWayServerAccessory.prototype = {
|
|||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
cx.writeable = false;
|
|
||||||
//cx.on('set', function(level, callback){
|
|
||||||
// this.command(vdev, "exact", {level: "on", "color.r": 255, "color.g": 0, "color.b": 0}).then(function(result){
|
|
||||||
// callback();
|
|
||||||
// });
|
|
||||||
//}.bind(this));
|
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,8 +589,10 @@ ZWayServerAccessory.prototype = {
|
|||||||
callback(false, cx.zway_getValueFromVDev(result.data));
|
callback(false, cx.zway_getValueFromVDev(result.data));
|
||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
cx.minimumValue = vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : -40;
|
cx.setProps({
|
||||||
cx.maximumValue = vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 999;
|
minValue: vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : -40,
|
||||||
|
maxValue: vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 999
|
||||||
|
});
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,8 +614,10 @@ ZWayServerAccessory.prototype = {
|
|||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
cx.minimumValue = vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : 5;
|
cx.setProps({
|
||||||
cx.maximumValue = vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 40;
|
minValue: vdev.metrics && vdev.metrics.min !== undefined ? vdev.metrics.min : 5,
|
||||||
|
maxValue: vdev.metrics && vdev.metrics.max !== undefined ? vdev.metrics.max : 40
|
||||||
|
});
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,7 +631,9 @@ ZWayServerAccessory.prototype = {
|
|||||||
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
callback(false, Characteristic.TemperatureDisplayUnits.CELSIUS);
|
callback(false, Characteristic.TemperatureDisplayUnits.CELSIUS);
|
||||||
});
|
});
|
||||||
cx.writable = false;
|
cx.setProps({
|
||||||
|
perms: [Characteristic.Perms.READ]
|
||||||
|
});
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,7 +661,6 @@ ZWayServerAccessory.prototype = {
|
|||||||
callback(false, Characteristic.TargetHeatingCoolingState.HEAT);
|
callback(false, Characteristic.TargetHeatingCoolingState.HEAT);
|
||||||
});
|
});
|
||||||
// Hmm... apparently if this is not setable, we can't add a thermostat change to a scene. So, make it writable but a no-op.
|
// Hmm... apparently if this is not setable, we can't add a thermostat change to a scene. So, make it writable but a no-op.
|
||||||
cx.writable = true;
|
|
||||||
cx.on('set', function(newValue, callback){
|
cx.on('set', function(newValue, callback){
|
||||||
debug("WARN: Set of TargetHeatingCoolingState not yet implemented, resetting to HEAT!")
|
debug("WARN: Set of TargetHeatingCoolingState not yet implemented, resetting to HEAT!")
|
||||||
callback(undefined, Characteristic.TargetHeatingCoolingState.HEAT);
|
callback(undefined, Characteristic.TargetHeatingCoolingState.HEAT);
|
||||||
@@ -636,8 +695,9 @@ ZWayServerAccessory.prototype = {
|
|||||||
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
callback(false, Characteristic.TargetDoorState.CLOSED);
|
callback(false, Characteristic.TargetDoorState.CLOSED);
|
||||||
});
|
});
|
||||||
//cx.readable = false;
|
cx.setProps({
|
||||||
cx.writable = false;
|
perms: [Characteristic.Perms.READ]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cx instanceof Characteristic.ObstructionDetected){
|
if(cx instanceof Characteristic.ObstructionDetected){
|
||||||
@@ -650,8 +710,6 @@ ZWayServerAccessory.prototype = {
|
|||||||
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
callback(false, false);
|
callback(false, false);
|
||||||
});
|
});
|
||||||
//cx.readable = false;
|
|
||||||
cx.writable = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cx instanceof Characteristic.BatteryLevel){
|
if(cx instanceof Characteristic.BatteryLevel){
|
||||||
@@ -692,8 +750,6 @@ ZWayServerAccessory.prototype = {
|
|||||||
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
callback(false, Characteristic.ChargingState.NOT_CHARGING);
|
callback(false, Characteristic.ChargingState.NOT_CHARGING);
|
||||||
});
|
});
|
||||||
//cx.readable = false;
|
|
||||||
cx.writable = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cx instanceof Characteristic.CurrentAmbientLightLevel){
|
if(cx instanceof Characteristic.CurrentAmbientLightLevel){
|
||||||
@@ -702,8 +758,8 @@ ZWayServerAccessory.prototype = {
|
|||||||
// Completely unscientific guess, based on test-fit data and Wikipedia real-world lux values.
|
// Completely unscientific guess, based on test-fit data and Wikipedia real-world lux values.
|
||||||
// This will probably change!
|
// This will probably change!
|
||||||
var lux = 0.0005 * (vdev.metrics.level^3.6);
|
var lux = 0.0005 * (vdev.metrics.level^3.6);
|
||||||
if(lux < cx.minimumValue) return cx.minimumValue;
|
// Bounds checking now done upstream!
|
||||||
if(lux > cx.maximumValue) return cx.maximumValue;
|
//if(lux < cx.minimumValue) return cx.minimumValue; if(lux > cx.maximumValue) return cx.maximumValue;
|
||||||
return lux;
|
return lux;
|
||||||
} else {
|
} else {
|
||||||
return vdev.metrics.level;
|
return vdev.metrics.level;
|
||||||
@@ -722,6 +778,43 @@ ZWayServerAccessory.prototype = {
|
|||||||
});
|
});
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(cx instanceof Characteristic.MotionDetected){
|
||||||
|
cx.zway_getValueFromVDev = function(vdev){
|
||||||
|
return vdev.metrics.level === "off" ? false : true;
|
||||||
|
};
|
||||||
|
cx.value = cx.zway_getValueFromVDev(vdev);
|
||||||
|
cx.on('get', function(callback, context){
|
||||||
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
|
this.getVDev(vdev).then(function(result){
|
||||||
|
debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + ".");
|
||||||
|
callback(false, cx.zway_getValueFromVDev(result.data));
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
cx.on('change', function(ev){
|
||||||
|
debug("Device " + vdev.metrics.title + ", characteristic " + cx.displayName + " changed from " + ev.oldValue + " to " + ev.newValue);
|
||||||
|
});
|
||||||
|
return cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cx instanceof Characteristic.StatusTampered){
|
||||||
|
cx.zway_getValueFromVDev = function(vdev){
|
||||||
|
return vdev.metrics.level === "off" ? Characteristic.StatusTampered.NOT_TAMPERED : Characteristic.StatusTampered.TAMPERED;
|
||||||
|
};
|
||||||
|
cx.value = cx.zway_getValueFromVDev(vdev);
|
||||||
|
cx.on('get', function(callback, context){
|
||||||
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
|
this.getVDev(vdev).then(function(result){
|
||||||
|
debug("Got value: " + cx.zway_getValueFromVDev(result.data) + ", for " + vdev.metrics.title + ".");
|
||||||
|
callback(false, cx.zway_getValueFromVDev(result.data));
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
cx.on('change', function(ev){
|
||||||
|
debug("Device " + vdev.metrics.title + ", characteristic " + cx.displayName + " changed from " + ev.oldValue + " to " + ev.newValue);
|
||||||
|
});
|
||||||
|
return cx;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
configureService: function(service, vdev){
|
configureService: function(service, vdev){
|
||||||
@@ -763,22 +856,29 @@ ZWayServerAccessory.prototype = {
|
|||||||
getServices: function() {
|
getServices: function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
|
var vdevPrimary = this.devDesc.devices[this.devDesc.primary];
|
||||||
|
var accId = this.platform.getTagValue(vdevPrimary, "Accessory.Id");
|
||||||
|
if(!accId){
|
||||||
|
accId = "VDev-" + vdevPrimary.h; //FIXME: Is this valid?
|
||||||
|
}
|
||||||
|
|
||||||
var informationService = new Service.AccessoryInformation();
|
var informationService = new Service.AccessoryInformation();
|
||||||
|
|
||||||
informationService
|
informationService
|
||||||
.setCharacteristic(Characteristic.Name, this.name)
|
.setCharacteristic(Characteristic.Name, this.name)
|
||||||
.setCharacteristic(Characteristic.Manufacturer, "Z-Wave.me")
|
.setCharacteristic(Characteristic.Manufacturer, "Z-Wave.me")
|
||||||
.setCharacteristic(Characteristic.Model, "Virtual Device (VDev version 1)")
|
.setCharacteristic(Characteristic.Model, "Virtual Device (VDev version 1)")
|
||||||
.setCharacteristic(Characteristic.SerialNumber, "VDev-" + this.devDesc.devices[this.devDesc.primary].h) //FIXME: Is this valid?);
|
.setCharacteristic(Characteristic.SerialNumber, accId);
|
||||||
|
|
||||||
var services = [informationService];
|
var services = [informationService];
|
||||||
|
|
||||||
services = services.concat(this.getVDevServices(this.devDesc.devices[this.devDesc.primary]));
|
services = services.concat(this.getVDevServices(vdevPrimary));
|
||||||
|
|
||||||
// Any extra switchMultilevels? Could be a RGBW+W bulb, add them as additional services...
|
// Any extra switchMultilevels? Could be a RGBW+W bulb, add them as additional services...
|
||||||
if(this.devDesc.extras["switchMultilevel"]) for(var i = 0; i < this.devDesc.extras["switchMultilevel"].length; i++){
|
if(this.devDesc.extras["switchMultilevel"]) for(var i = 0; i < this.devDesc.extras["switchMultilevel"].length; i++){
|
||||||
var xvdev = this.devDesc.devices[this.devDesc.extras["switchMultilevel"][i]];
|
var xvdev = this.devDesc.devices[this.devDesc.extras["switchMultilevel"][i]];
|
||||||
services = services.concat(this.getVDevServices(xvdev));
|
var xservice = this.getVDevServices(xvdev);
|
||||||
|
services = services.concat(xservice);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.platform.splitServices){
|
if(this.platform.splitServices){
|
||||||
|
|||||||
Reference in New Issue
Block a user