mirror of
https://github.com/mtan93/homebridge.git
synced 2026-03-18 21:03:17 +00:00
@@ -11,6 +11,7 @@ function ZWayServerPlatform(log, config){
|
||||
this.url = config["url"];
|
||||
this.login = config["login"];
|
||||
this.password = config["password"];
|
||||
this.opt_in = config["opt_in"];
|
||||
this.name_overrides = config["name_overrides"];
|
||||
this.batteryLow = config["battery_low_level"] || 15;
|
||||
this.pollInterval = config["poll_interval"] || 2;
|
||||
@@ -90,10 +91,10 @@ ZWayServerPlatform.prototype = {
|
||||
//Note: Order matters!
|
||||
var primaryDeviceClasses = [
|
||||
"thermostat",
|
||||
"sensorMultilevel.Temperature",
|
||||
"switchMultilevel",
|
||||
"switchBinary",
|
||||
"sensorBinary.Door/Window"
|
||||
"sensorBinary.Door/Window",
|
||||
"sensorMultilevel.Temperature"
|
||||
];
|
||||
|
||||
var that = this;
|
||||
@@ -110,11 +111,18 @@ ZWayServerPlatform.prototype = {
|
||||
for(var i = 0; i < devices.length; i++){
|
||||
var vdev = devices[i];
|
||||
if(vdev.tags.indexOf("Homebridge:Skip") >= 0) { debug("Tag says skip!"); continue; }
|
||||
if(this.opt_in && vdev.tags.indexOf("Homebridge:Include") < 0) continue;
|
||||
var gdid = vdev.id.replace(/^(.*?)_zway_(\d+-\d+)-\d.*/, '$1_$2');
|
||||
var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, primary: undefined});
|
||||
var gd = groupedDevices[gdid] || (groupedDevices[gdid] = {devices: [], types: {}, extras: {}, primary: undefined});
|
||||
gd.devices.push(vdev);
|
||||
gd.types[ZWayServerPlatform.getVDevTypeKey(vdev)] = 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(gd.types[tk] === undefined){
|
||||
gd.types[tk] = gd.devices.length - 1;
|
||||
} else {
|
||||
gd.extras[tk] = gd.extras[tk] || [];
|
||||
gd.extras[tk].push(gd.devices.length - 1);
|
||||
}
|
||||
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) {
|
||||
@@ -172,6 +180,11 @@ ZWayServerPlatform.prototype = {
|
||||
if(this.cxVDevMap[upd.id]){
|
||||
var vdev = this.vDevStore[upd.id];
|
||||
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;
|
||||
var cxs = this.cxVDevMap[upd.id];
|
||||
for(var j = 0; j < cxs.length; j++){
|
||||
@@ -222,30 +235,87 @@ 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){
|
||||
var typeKey = ZWayServerPlatform.getVDevTypeKey(vdev);
|
||||
var services = [], service;
|
||||
switch (typeKey) {
|
||||
case "thermostat":
|
||||
services.push(new Service.Thermostat(vdev.metrics.title));
|
||||
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));
|
||||
case "thermostat":
|
||||
services.push(new Service.Thermostat(vdev.metrics.title, vdev.id));
|
||||
break;
|
||||
case "switchBinary":
|
||||
services.push(new Service.Switch(vdev.metrics.title));
|
||||
services.push(new Service.Switch(vdev.metrics.title, vdev.id));
|
||||
break;
|
||||
case "switchMultilevel":
|
||||
services.push(new Service.Lightbulb(vdev.metrics.title, vdev.id));
|
||||
break;
|
||||
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;
|
||||
case "sensorMultilevel.Luminiscence":
|
||||
services.push(new Service.LightSensor(vdev.metrics.title));
|
||||
services.push(new Service.LightSensor(vdev.metrics.title, vdev.id));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -272,6 +342,8 @@ ZWayServerAccessory.prototype = {
|
||||
this.uuidToTypeKeyMap = map = {};
|
||||
map[(new Characteristic.On).UUID] = ["switchBinary","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.TargetTemperature).UUID] = ["thermostat"];
|
||||
map[(new Characteristic.TemperatureDisplayUnits).UUID] = ["sensorMultilevel.Temperature","thermostat"]; //TODO: Always a fixed result
|
||||
@@ -309,8 +381,8 @@ ZWayServerAccessory.prototype = {
|
||||
return null;
|
||||
}
|
||||
,
|
||||
configureCharacteristic: function(cx, vdev){
|
||||
var that = this;
|
||||
configureCharacteristic: function(cx, vdev, service){
|
||||
var accessory = this;
|
||||
|
||||
// Add this combination to the maps...
|
||||
if(!this.platform.cxVDevMap[vdev.id]) this.platform.cxVDevMap[vdev.id] = [];
|
||||
@@ -324,7 +396,7 @@ ZWayServerAccessory.prototype = {
|
||||
cx.value = cx.zway_getValueFromVDev(vdev);
|
||||
cx.on('get', function(callback, context){
|
||||
debug("Getting value for " + vdev.metrics.title + ", characteristic \"" + cx.displayName + "\"...");
|
||||
callback(false, that.name);
|
||||
callback(false, accessory.name);
|
||||
});
|
||||
cx.writable = false;
|
||||
return cx;
|
||||
@@ -381,6 +453,76 @@ ZWayServerAccessory.prototype = {
|
||||
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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if(cx instanceof Characteristic.CurrentTemperature){
|
||||
cx.zway_getValueFromVDev = function(vdev){
|
||||
return vdev.metrics.level;
|
||||
@@ -528,7 +670,7 @@ ZWayServerAccessory.prototype = {
|
||||
|
||||
if(cx instanceof Characteristic.StatusLowBattery){
|
||||
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.on('get', function(callback, context){
|
||||
@@ -591,14 +733,29 @@ ZWayServerAccessory.prototype = {
|
||||
success = false;
|
||||
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++){
|
||||
var cx = service.optionalCharacteristics[i];
|
||||
var vdev = this.getVDevForCharacteristic(cx);
|
||||
var vdev = this.getVDevForCharacteristic(cx, vdev);
|
||||
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;
|
||||
}
|
||||
@@ -617,6 +774,12 @@ ZWayServerAccessory.prototype = {
|
||||
var services = [informationService];
|
||||
|
||||
services = services.concat(this.getVDevServices(this.devDesc.devices[this.devDesc.primary]));
|
||||
|
||||
// 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]];
|
||||
services = services.concat(this.getVDevServices(xvdev));
|
||||
}
|
||||
|
||||
if(this.platform.splitServices){
|
||||
if(this.devDesc.types["battery.Battery"]){
|
||||
@@ -655,7 +818,7 @@ ZWayServerAccessory.prototype = {
|
||||
extraCxs = []; // to wipe out any already setup cxs.
|
||||
break;
|
||||
}
|
||||
this.configureCharacteristic(cx, vdev2);
|
||||
this.configureCharacteristic(cx, vdev2, service);
|
||||
extraCxs.push(cx);
|
||||
}
|
||||
for(var j = 0; j < extraCxs.length; j++)
|
||||
|
||||
Reference in New Issue
Block a user