mirror of
https://github.com/mtan93/homebridge.git
synced 2026-04-07 06:13:12 +01:00
@@ -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;
|
||||||
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];
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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.",
|
||||||
|
|||||||
@@ -24,13 +24,14 @@
|
|||||||
"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": "0.0.9",
|
||||||
"telldus-live": "0.2.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",
|
||||||
|
|||||||
@@ -44,7 +44,11 @@ NestPlatform.prototype = {
|
|||||||
|
|
||||||
function NestThermostatAccessory(log, name, device, deviceId) {
|
function NestThermostatAccessory(log, name, device, deviceId) {
|
||||||
// device info
|
// device info
|
||||||
this.name = name;
|
if (name) {
|
||||||
|
this.name = name;
|
||||||
|
} else {
|
||||||
|
this.name = "Nest";
|
||||||
|
}
|
||||||
this.model = device.model_version;
|
this.model = device.model_version;
|
||||||
this.serial = device.serial_number;
|
this.serial = device.serial_number;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
|
|||||||
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;
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ function ZWayServerPlatform(log, config){
|
|||||||
this.url = config["url"];
|
this.url = config["url"];
|
||||||
this.login = config["login"];
|
this.login = config["login"];
|
||||||
this.password = config["password"];
|
this.password = config["password"];
|
||||||
|
this.opt_in = config["opt_in"];
|
||||||
this.name_overrides = config["name_overrides"];
|
this.name_overrides = config["name_overrides"];
|
||||||
this.batteryLow = config["battery_low_level"] || 15;
|
this.batteryLow = config["battery_low_level"] || 15;
|
||||||
this.pollInterval = config["poll_interval"] || 2;
|
this.pollInterval = config["poll_interval"] || 2;
|
||||||
@@ -82,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...");
|
||||||
|
|
||||||
@@ -90,10 +104,10 @@ ZWayServerPlatform.prototype = {
|
|||||||
//Note: Order matters!
|
//Note: Order matters!
|
||||||
var primaryDeviceClasses = [
|
var primaryDeviceClasses = [
|
||||||
"thermostat",
|
"thermostat",
|
||||||
"sensorMultilevel.Temperature",
|
|
||||||
"switchMultilevel",
|
"switchMultilevel",
|
||||||
"switchBinary",
|
"switchBinary",
|
||||||
"sensorBinary.Door/Window"
|
"sensorBinary.Door/Window",
|
||||||
|
"sensorMultilevel.Temperature"
|
||||||
];
|
];
|
||||||
|
|
||||||
var that = this;
|
var that = this;
|
||||||
@@ -109,14 +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; }
|
||||||
var gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2');
|
if(this.opt_in && !this.getTagValue(vdev, "Include")) continue;
|
||||||
var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, 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);
|
||||||
gd.types[ZWayServerPlatform.getVDevTypeKey(vdev)] = gd.devices.length - 1;
|
var vdevIndex = gd.devices.length - 1;
|
||||||
gd.types[vdev.deviceType] = gd.devices.length - 1; // also include the deviceType only as a possibility
|
|
||||||
|
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){
|
||||||
|
gd.types[tk] = vdevIndex;
|
||||||
|
} else {
|
||||||
|
gd.extras[tk] = gd.extras[tk] || [];
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//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;
|
||||||
|
|
||||||
@@ -128,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;
|
||||||
}
|
}
|
||||||
@@ -145,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...
|
||||||
@@ -172,6 +225,11 @@ ZWayServerPlatform.prototype = {
|
|||||||
if(this.cxVDevMap[upd.id]){
|
if(this.cxVDevMap[upd.id]){
|
||||||
var vdev = this.vDevStore[upd.id];
|
var vdev = this.vDevStore[upd.id];
|
||||||
vdev.metrics.level = upd.metrics.level;
|
vdev.metrics.level = upd.metrics.level;
|
||||||
|
if(upd.metrics.color){
|
||||||
|
vdev.metrics.r = upd.metrics.r;
|
||||||
|
vdev.metrics.g = upd.metrics.g;
|
||||||
|
vdev.metrics.b = upd.metrics.b;
|
||||||
|
}
|
||||||
vdev.updateTime = upd.updateTime;
|
vdev.updateTime = upd.updateTime;
|
||||||
var cxs = this.cxVDevMap[upd.id];
|
var cxs = this.cxVDevMap[upd.id];
|
||||||
for(var j = 0; j < cxs.length; j++){
|
for(var j = 0; j < cxs.length; j++){
|
||||||
@@ -222,31 +280,98 @@ ZWayServerAccessory.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
rgb2hsv: function(obj) {
|
||||||
|
// RGB: 0-255; H: 0-360, S,V: 0-100
|
||||||
|
var r = obj.r/255, g = obj.g/255, b = obj.b/255;
|
||||||
|
var max, min, d, h, s, v;
|
||||||
|
|
||||||
|
min = Math.min(r, Math.min(g, b));
|
||||||
|
max = Math.max(r, Math.max(g, b));
|
||||||
|
|
||||||
|
if (min === max) {
|
||||||
|
// shade of gray
|
||||||
|
return {h: 0, s: 0, v: r * 100};
|
||||||
|
}
|
||||||
|
|
||||||
|
var d = (r === min) ? g - b : ((b === min) ? r - g : b - r);
|
||||||
|
h = (r === min) ? 3 : ((b === min) ? 1 : 5);
|
||||||
|
h = 60 * (h - d/(max - min));
|
||||||
|
s = (max - min) / max;
|
||||||
|
v = max;
|
||||||
|
return {"h": h, "s": s * 100, "v": v * 100};
|
||||||
|
}
|
||||||
|
,
|
||||||
|
hsv2rgb: function(obj) {
|
||||||
|
// H: 0-360; S,V: 0-100; RGB: 0-255
|
||||||
|
var r, g, b;
|
||||||
|
var sfrac = obj.s / 100;
|
||||||
|
var vfrac = obj.v / 100;
|
||||||
|
|
||||||
|
if(sfrac === 0){
|
||||||
|
var vbyte = Math.round(vfrac*255);
|
||||||
|
return { r: vbyte, g: vbyte, b: vbyte };
|
||||||
|
}
|
||||||
|
|
||||||
|
var hdb60 = (obj.h % 360) / 60;
|
||||||
|
var sector = Math.floor(hdb60);
|
||||||
|
var fpart = hdb60 - sector;
|
||||||
|
var c = vfrac * (1 - sfrac);
|
||||||
|
var x1 = vfrac * (1 - sfrac * fpart);
|
||||||
|
var x2 = vfrac * (1 - sfrac * (1 - fpart));
|
||||||
|
switch(sector){
|
||||||
|
case 0:
|
||||||
|
r = vfrac; g = x2; b = c; break;
|
||||||
|
case 1:
|
||||||
|
r = x1; g = vfrac; b = c; break;
|
||||||
|
case 2:
|
||||||
|
r = c; g = vfrac; b = x2; break;
|
||||||
|
case 3:
|
||||||
|
r = c; g = x1; b = vfrac; break;
|
||||||
|
case 4:
|
||||||
|
r = x2; g = c; b = vfrac; break;
|
||||||
|
case 5:
|
||||||
|
default:
|
||||||
|
r = vfrac; g = c; b = x1; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { "r": Math.round(255 * r), "g": Math.round(255 * g), "b": Math.round(255 * b) };
|
||||||
|
}
|
||||||
|
,
|
||||||
getVDevServices: function(vdev){
|
getVDevServices: function(vdev){
|
||||||
var typeKey = ZWayServerPlatform.getVDevTypeKey(vdev);
|
var typeKey = ZWayServerPlatform.getVDevTypeKey(vdev);
|
||||||
var services = [], service;
|
var services = [], service;
|
||||||
switch (typeKey) {
|
switch (typeKey) {
|
||||||
case "thermostat":
|
case "thermostat":
|
||||||
services.push(new Service.Thermostat(vdev.metrics.title));
|
services.push(new Service.Thermostat(vdev.metrics.title, vdev.id));
|
||||||
break;
|
|
||||||
case "sensorMultilevel.Temperature":
|
|
||||||
services.push(new Service.TemperatureSensor(vdev.metrics.title));
|
|
||||||
break;
|
|
||||||
case "switchMultilevel":
|
|
||||||
services.push(new Service.Lightbulb(vdev.metrics.title));
|
|
||||||
break;
|
|
||||||
case "battery.Battery":
|
|
||||||
services.push(new Service.BatteryService(vdev.metrics.title));
|
|
||||||
break;
|
break;
|
||||||
case "switchBinary":
|
case "switchBinary":
|
||||||
services.push(new Service.Switch(vdev.metrics.title));
|
services.push(new Service.Switch(vdev.metrics.title, vdev.id));
|
||||||
|
break;
|
||||||
|
case "switchRGBW":
|
||||||
|
case "switchMultilevel":
|
||||||
|
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));
|
services.push(new Service.GarageDoorOpener(vdev.metrics.title, vdev.id));
|
||||||
|
break;
|
||||||
|
case "sensorMultilevel.Temperature":
|
||||||
|
services.push(new Service.TemperatureSensor(vdev.metrics.title, vdev.id));
|
||||||
|
break;
|
||||||
|
case "battery.Battery":
|
||||||
|
services.push(new Service.BatteryService(vdev.metrics.title, vdev.id));
|
||||||
break;
|
break;
|
||||||
case "sensorMultilevel.Luminiscence":
|
case "sensorMultilevel.Luminiscence":
|
||||||
services.push(new Service.LightSensor(vdev.metrics.title));
|
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 =[];
|
||||||
@@ -267,11 +392,19 @@ 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 = {};
|
||||||
map[(new Characteristic.On).UUID] = ["switchBinary","switchMultilevel"];
|
map[(new Characteristic.On).UUID] = ["switchBinary","switchMultilevel"];
|
||||||
map[(new Characteristic.Brightness).UUID] = ["switchMultilevel"];
|
map[(new Characteristic.Brightness).UUID] = ["switchMultilevel"];
|
||||||
|
map[(new Characteristic.Hue).UUID] = ["switchRGBW"];
|
||||||
|
map[(new Characteristic.Saturation).UUID] = ["switchRGBW"];
|
||||||
map[(new Characteristic.CurrentTemperature).UUID] = ["sensorMultilevel.Temperature","thermostat"];
|
map[(new Characteristic.CurrentTemperature).UUID] = ["sensorMultilevel.Temperature","thermostat"];
|
||||||
map[(new Characteristic.TargetTemperature).UUID] = ["thermostat"];
|
map[(new Characteristic.TargetTemperature).UUID] = ["thermostat"];
|
||||||
map[(new Characteristic.TemperatureDisplayUnits).UUID] = ["sensorMultilevel.Temperature","thermostat"]; //TODO: Always a fixed result
|
map[(new Characteristic.TemperatureDisplayUnits).UUID] = ["sensorMultilevel.Temperature","thermostat"]; //TODO: Always a fixed result
|
||||||
@@ -309,8 +442,8 @@ ZWayServerAccessory.prototype = {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
configureCharacteristic: function(cx, vdev){
|
configureCharacteristic: function(cx, vdev, service){
|
||||||
var that = this;
|
var accessory = this;
|
||||||
|
|
||||||
// Add this combination to the maps...
|
// Add this combination to the maps...
|
||||||
if(!this.platform.cxVDevMap[vdev.id]) this.platform.cxVDevMap[vdev.id] = [];
|
if(!this.platform.cxVDevMap[vdev.id]) this.platform.cxVDevMap[vdev.id] = [];
|
||||||
@@ -324,12 +457,17 @@ ZWayServerAccessory.prototype = {
|
|||||||
cx.value = cx.zway_getValueFromVDev(vdev);
|
cx.value = cx.zway_getValueFromVDev(vdev);
|
||||||
cx.on('get', function(callback, context){
|
cx.on('get', function(callback, context){
|
||||||
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||||
callback(false, that.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;
|
||||||
@@ -381,6 +519,64 @@ ZWayServerAccessory.prototype = {
|
|||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(cx instanceof Characteristic.Hue){
|
||||||
|
cx.zway_getValueFromVDev = function(vdev){
|
||||||
|
debug("Derived value " + accessory.rgb2hsv(vdev.metrics.color).h + " for hue.");
|
||||||
|
return accessory.rgb2hsv(vdev.metrics.color).h;
|
||||||
|
};
|
||||||
|
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('set', function(hue, callback){
|
||||||
|
var scx = service.getCharacteristic(Characteristic.Saturation);
|
||||||
|
var vcx = service.getCharacteristic(Characteristic.Brightness);
|
||||||
|
if(!scx || !vcx){
|
||||||
|
debug("Hue without Saturation and Brightness is not supported! Cannot set value!")
|
||||||
|
callback(true, cx.value);
|
||||||
|
}
|
||||||
|
var rgb = this.hsv2rgb({ h: hue, s: scx.value, v: vcx.value });
|
||||||
|
this.command(vdev, "exact", { red: rgb.r, green: rgb.g, blue: rgb.b }).then(function(result){
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
return cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cx instanceof Characteristic.Saturation){
|
||||||
|
cx.zway_getValueFromVDev = function(vdev){
|
||||||
|
debug("Derived value " + accessory.rgb2hsv(vdev.metrics.color).s + " for saturation.");
|
||||||
|
return accessory.rgb2hsv(vdev.metrics.color).s;
|
||||||
|
};
|
||||||
|
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('set', function(saturation, callback){
|
||||||
|
var hcx = service.getCharacteristic(Characteristic.Hue);
|
||||||
|
var vcx = service.getCharacteristic(Characteristic.Brightness);
|
||||||
|
if(!hcx || !vcx){
|
||||||
|
debug("Saturation without Hue and Brightness is not supported! Cannot set value!")
|
||||||
|
callback(true, cx.value);
|
||||||
|
}
|
||||||
|
var rgb = this.hsv2rgb({ h: hcx.value, s: saturation, v: vcx.value });
|
||||||
|
this.command(vdev, "exact", { red: rgb.r, green: rgb.g, blue: rgb.b }).then(function(result){
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
return cx;
|
||||||
|
}
|
||||||
|
|
||||||
if(cx instanceof Characteristic.CurrentTemperature){
|
if(cx instanceof Characteristic.CurrentTemperature){
|
||||||
cx.zway_getValueFromVDev = function(vdev){
|
cx.zway_getValueFromVDev = function(vdev){
|
||||||
return vdev.metrics.level;
|
return vdev.metrics.level;
|
||||||
@@ -393,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,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);
|
||||||
@@ -494,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){
|
||||||
@@ -508,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){
|
||||||
@@ -528,7 +728,7 @@ ZWayServerAccessory.prototype = {
|
|||||||
|
|
||||||
if(cx instanceof Characteristic.StatusLowBattery){
|
if(cx instanceof Characteristic.StatusLowBattery){
|
||||||
cx.zway_getValueFromVDev = function(vdev){
|
cx.zway_getValueFromVDev = function(vdev){
|
||||||
return vdev.metrics.level <= that.platform.batteryLow ? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
return vdev.metrics.level <= accessory.platform.batteryLow ? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
||||||
};
|
};
|
||||||
cx.value = cx.zway_getValueFromVDev(vdev);
|
cx.value = cx.zway_getValueFromVDev(vdev);
|
||||||
cx.on('get', function(callback, context){
|
cx.on('get', function(callback, context){
|
||||||
@@ -550,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){
|
||||||
@@ -560,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;
|
||||||
@@ -580,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){
|
||||||
@@ -591,14 +826,29 @@ ZWayServerAccessory.prototype = {
|
|||||||
success = false;
|
success = false;
|
||||||
debug("ERROR! Failed to configure required characteristic \"" + service.characteristics[i].displayName + "\"!");
|
debug("ERROR! Failed to configure required characteristic \"" + service.characteristics[i].displayName + "\"!");
|
||||||
}
|
}
|
||||||
cx = this.configureCharacteristic(cx, vdev);
|
cx = this.configureCharacteristic(cx, vdev, service);
|
||||||
}
|
}
|
||||||
for(var i = 0; i < service.optionalCharacteristics.length; i++){
|
for(var i = 0; i < service.optionalCharacteristics.length; i++){
|
||||||
var cx = service.optionalCharacteristics[i];
|
var cx = service.optionalCharacteristics[i];
|
||||||
var vdev = this.getVDevForCharacteristic(cx);
|
var vdev = this.getVDevForCharacteristic(cx, vdev);
|
||||||
if(!vdev) continue;
|
if(!vdev) continue;
|
||||||
cx = this.configureCharacteristic(cx, vdev);
|
|
||||||
if(cx) service.addCharacteristic(cx);
|
//NOTE: Questionable logic, but if the vdev has already been used for the same
|
||||||
|
// characteristic type elsewhere, lets not duplicate it just for the sake of an
|
||||||
|
// optional characteristic. This eliminates the problem with RGB+W+W bulbs
|
||||||
|
// having the HSV controls shown again, but might have unintended consequences...
|
||||||
|
var othercx, othercxs = this.platform.cxVDevMap[vdev.id];
|
||||||
|
if(othercxs) for(var j = 0; j < othercxs.length; j++) if(othercxs[j].UUID === cx.UUID) othercx = othercxs[j];
|
||||||
|
if(othercx)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cx = this.configureCharacteristic(cx, vdev, service);
|
||||||
|
try {
|
||||||
|
if(cx) service.addCharacteristic(cx);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
debug('Adding Characteristic "' + cx.displayName + '" failed with message "' + ex.message + '". This may be expected.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@@ -606,17 +856,30 @@ 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...
|
||||||
|
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 xservice = this.getVDevServices(xvdev);
|
||||||
|
services = services.concat(xservice);
|
||||||
|
}
|
||||||
|
|
||||||
if(this.platform.splitServices){
|
if(this.platform.splitServices){
|
||||||
if(this.devDesc.types["battery.Battery"]){
|
if(this.devDesc.types["battery.Battery"]){
|
||||||
@@ -655,7 +918,7 @@ ZWayServerAccessory.prototype = {
|
|||||||
extraCxs = []; // to wipe out any already setup cxs.
|
extraCxs = []; // to wipe out any already setup cxs.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.configureCharacteristic(cx, vdev2);
|
this.configureCharacteristic(cx, vdev2, service);
|
||||||
extraCxs.push(cx);
|
extraCxs.push(cx);
|
||||||
}
|
}
|
||||||
for(var j = 0; j < extraCxs.length; j++)
|
for(var j = 0; j < extraCxs.length; j++)
|
||||||
|
|||||||
Reference in New Issue
Block a user