homebridge-edomoticz
Version:
homebridge-plugin for Domoticz https://github.com/nfarina/homebridge
1,198 lines (1,116 loc) • 124 kB
JavaScript
var request = require("request");
var Constants = require('./constants.js');
var Helper = require('./helper.js').Helper;
var Domoticz = require('./domoticz.js').Domoticz;
var eDomoticzServices = require('./services.js').eDomoticzServices;
const { performance } = require('perf_hooks');
module.exports = eDomoticzAccessory;
var tvAccessories = {};
var tvInputAccessories = {};
function eDomoticzAccessory(platform, platformAccessory, IsScene, status, idx, name, uuid, haveDimmer, maxDimLevel, subType, Type, batteryRef, swType, swTypeVal, hwId, hwType, image, eve, hwtimeout, descript) {
if ((haveDimmer) || (swType == "Dimmer")) {
if ((hwType !== 51) && (swType !== "On/Off")) {
this.haveDimmer = true;
this.maxDimLevel = maxDimLevel;
} else {
this.haveDimmer = false;
}
} else {
this.haveDimmer = false;
}
this.services = [];
this.published = false;
this.platform = platform;
this.IsScene = IsScene; // Domoticz Scenes ignored for now...
this.status = status;
this.idx = idx;
this.name = name;
this.eve = eve;
this.subType = subType;
this.swType = swType;
this.image = image;
this.swTypeVal = swTypeVal;
this.hwtimeout = hwtimeout;
this.isSwitch = (typeof this.swTypeVal !== 'undefined' && this.swTypeVal >= 0 && this.name.indexOf("Occupied") == -1);
switch (hwType) {
case 67: // Domoticz Security Panel
this.swTypeVal = Constants.DeviceTypeSecuritySystem;
break;
default:
break;
}
this.Type = Type;
this.batteryRef = batteryRef;
this.CounterToday = 1;
this.onValue = "On";
this.offValue = "Off";
this.cachedValues = {};
this.hwId = hwId;
this.hwType = hwType;
this.descript = descript;
this.powerOnBySetLevelTime = 0;
// Initialize default values, e.g. to get the "factor"
var voidCallback = function () {};
switch (true) {
case this.swTypeVal == Constants.DeviceTypeDimmer:
case this.swTypeVal == Constants.DeviceTypeBlindsPercentage:
case this.swTypeVal == Constants.DeviceTypeBlindsPercentageInverted:
case this.swTypeVal == Constants.DeviceTypeBlindsPlusStop:
{
if (this.swTypeVal == Constants.DeviceTypeBlindsPercentage) {
this.isPercentageBlind = Constants.DeviceTypeBlindsPercentage;
}
if (this.swTypeVal == Constants.DeviceTypeBlindsPlusStop) {
this.isPercentageBlind = Constants.DeviceTypeBlindsPlusStop;
}
if (this.swTypeVal == Constants.DeviceTypeBlindsPercentageInverted) {
this.isInvertedBlind = Constants.DeviceTypeBlindsPercentageInverted;
}
this.getdValue(voidCallback);
break;
}
default:
break;
}
this.platformAccessory = platformAccessory;
if (!this.platformAccessory) {
this.platformAccessory = new platform.api.platformAccessory(this.name, uuid);
}
this.platformAccessory.reachable = true;
if (this.swTypeVal == Constants.DeviceTypeMedia) {
this.platformAccessory.category = platform.api.hap.Categories.TELEVISION;
}
this.publishServices();
}
eDomoticzAccessory.prototype = {
identify: function (callback) {
callback();
},
publishServices: function () {
var services = this.getServices();
for (var i = 0; i < services.length; i++) {
this.publishService(services[i]);
}
this.published = true;
},
publishService: function (service) {
var existingService = this.platformAccessory.services.find(function (eService) {
return eService.UUID == service.UUID && eService.subtype == service.subtype;
});
if (!existingService) {
this.platformAccessory.addService(service, this.name);
}
},
getService: function (name, subtype) {
var service = false;
try {
if (subtype) {
service = this.platformAccessory.getServiceByUUIDAndSubType(name, subtype);
} else {
service = this.platformAccessory.getService(name);
}
} catch (e) {
service = false;
}
if (!service) {
var targetService = new name();
service = this.platformAccessory.services.find(function (existingService) {
return existingService.UUID == targetService.UUID && existingService.subtype == targetService.subtype;
});
}
return service;
},
getCharacteristic: function (service, name) {
var characteristic = false;
try {
characteristic = service.getCharacteristic(name);
} catch (e) {
//console.log("^ For: " + service.displayName);
characteristic = false;
}
if (!characteristic) {
var targetCharacteristic = new name();
characteristic = service.characteristics.find(function (existingCharacteristic) {
return existingCharacteristic.UUID == targetCharacteristic.UUID && existingCharacteristic.subtype == targetCharacteristic.subtype;
});
}
return characteristic;
},
gracefullyAddCharacteristic: function (service, characteristicType) {
var characteristic = this.getCharacteristic(service, characteristicType);
if (characteristic) {
return characteristic;
}
return service.addCharacteristic(new characteristicType());
},
setPowerState: function (powerOn, callback, context) {
if (context && context == "eDomoticz-MQTT"
|| (this.cachedValues[Characteristic.On.UUID] == powerOn && powerOn) // Prevents message loop while dimming lights.
|| (this.powerOnBySetLevelTime > 0 && (performance.now() - this.powerOnBySetLevelTime) < 500 && powerOn) // Ignore a power ON right after a setlevel to allow turning ON a light using a dimmer.
) {
this.powerOnBySetLevelTime = 0;
callback();
return;
}
this.powerOnBySetLevelTime = 0;
Domoticz.updateDeviceStatus(this, "switchlight", {
"switchcmd": (powerOn ? "On" : "Off")
}, function (success) {
this.cachedValues[Characteristic.On.UUID] = powerOn;
callback();
}.bind(this));
},
getPowerState: function (callback) {
var cachedValue = this.cachedValues[Characteristic.On.UUID];
if (typeof cachedValue !== 'undefined') {
callback(null, cachedValue);
}
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
if (this.swTypeVal == Constants.DeviceTypePushOn) {
value = (s.Data == "Off") ? false : true;
} else {
value = (s.Status == "Off") ? false : true;
}
}.bind(this));
if (typeof cachedValue === 'undefined') {
callback(null, value);
}
this.cachedValues[Characteristic.On.UUID] = value;
}.bind(this));
},
setActiveState: function (state, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
Domoticz.updateDeviceStatus(this, "switchlight", {
"switchcmd": (state ? "On" : "Off")
}, function (success) {
this.cachedValues[Characteristic.Active.UUID] = state;
callback();
}.bind(this));
},
getActiveState: function (callback) {
var cachedValue = this.cachedValues[Characteristic.Active.UUID];
if (typeof cachedValue !== 'undefined') {
callback(null, cachedValue);
}
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = (s.Status == "Off") ? false : true;
}.bind(this));
if (typeof cachedValue === 'undefined') {
callback(null, value);
}
this.cachedValues[Characteristic.Active.UUID] = value;
}.bind(this)); },
getRainfall: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.Rain);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
setdValue: function (level, callback, context) {
this.cachedValues[Characteristic.Brightness.UUID] = level;
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
if (!(this.factor)) {
Domoticz.deviceStatus(this, function (json) {
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
this.factor = 100 / s.MaxDimLevel;
}.bind(this));
}.bind(this));
}
var dim = this.platform.config.dimFix == 1 ? Math.floor(level / this.factor) + 1 : Math.floor(level / this.factor);
this.powerOnBySetLevelTime = !this.cachedValues[Characteristic.On.UUID] ? performance.now() : 0;
Domoticz.updateDeviceStatus(this, "switchlight", {
"switchcmd": "Set Level",
"level": dim
}, function (success) {
callback();
}.bind(this));
},
getdValue: function (callback) {
var cachedValue = (this.isPercentageBlind || this.isInvertedBlind) ? this.cachedValues[Characteristic.CurrentPosition.UUID] : this.cachedValues[Characteristic.Brightness.UUID];
if (typeof cachedValue !== 'undefined') {
callback(null, cachedValue);
}
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
this.factor = 100 / s.MaxDimLevel;
value = Math.floor(s.LevelInt * this.factor);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
if (typeof cachedValue === 'undefined') {
callback(null, value);
}
if (value > 0) {
this.cachedValues[Characteristic.Brightness.UUID] = value;
}
}.bind(this));
},
getColorTempValue: function (service, callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sv = this.getService(Service.Lightbulb);
var chr = this.getCharacteristic(sv, Characteristic.ColorTemperature);
var sArray = Helper.sortByKey(json.result, "Name");
var error = !1;
sArray.map(function (s) {
try {
var tValue = JSON.parse(s.Color).t
value = chr.props.minValue + (
(chr.props.maxValue - chr.props.minValue) / 255 * tValue
)
this.platform.log("Data Received for " + this.name + " (WW Light): " + value);
} catch(e) {
this.platform.forceLog(this.name + '(WW Light): This device does not comply to Domoticz lighting standard for a WW light. Ignoring "Color" and setting White.');
value = 140;
}
}.bind(this));
callback(null, value);
}.bind(this));
},
setColorTempValue: function (service, value, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
var sv = this.getService(Service.Lightbulb);
var chr = this.getCharacteristic(sv, Characteristic.ColorTemperature);
Domoticz.updateDeviceStatus(this, "setkelvinlevel", {
"kelvin": (value - chr.props.minValue) / (chr.props.maxValue - chr.props.minValue) * 100
}, function (success) {
this.platform.log("Data Set for " + this.name + " (WW Light): " + value);
callback();
}.bind(this));
},
getHueValue: function (type, callback) {
if (type == 'Hue') {
callback(null, (this.hueValue !== undefined ? this.hueValue : 0));
} else if (type == 'Saturation') {
callback(null, (this.saturationValue !== undefined ? this.saturationValue : 0));
} else {
callback(null, 0);
}
},
setHueValue: function (type, value, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
if (type == 'Hue') {
this.hueValue = value;
this.hueSemaphore = (this.hueSemaphore === undefined ? 0 : this.hueSemaphore + 1);
} else if (type == 'Saturation') {
this.saturationValue = value;
this.hueSemaphore = (this.hueSemaphore === undefined ? 0 : this.hueSemaphore + 1);
}
if (this.hueValue !== undefined && this.saturationValue !== undefined && this.hueSemaphore !== undefined && this.hueSemaphore > 0) {
this.hueSemaphore = undefined;
var c = this.cachedValues[Characteristic.Brightness.UUID],
b = 100; // retain current brightness...
if (typeof c !== 'undefined') {
b = this.platform.config.dimFix == 1 ? Math.floor(c / this.factor) + 1 : Math.floor(c / this.factor);
}
Domoticz.updateDeviceStatus(this, "setcolbrightnessvalue", {
"hex": Helper.HSVtoRGB([this.hueValue, this.saturationValue, b])
}, function (success) {
callback();
}.bind(this));
} else {
callback();
}
},
getValue: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.Data);
value = Helper.oneDP(value);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getQualValue: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = s.Quality;
switch (value) {
case "Unknown":
{
value = Characteristic.AirQuality.UNKNOWN;
break;
}
case "Excellent":
{
value = Characteristic.AirQuality.EXCELLENT;
break;
}
case "Good":
{
value = Characteristic.AirQuality.GOOD;
break;
}
case "Fair":
{
value = Characteristic.AirQuality.FAIR;
break;
}
case "Mediocre":
case "Inferior":
{
value = Characteristic.AirQuality.INFERIOR;
break;
}
case "Bad":
case "Poor":
{
value = Characteristic.AirQuality.POOR;
break;
}
default:
{
value = Characteristic.AirQuality.FAIR;
break;
}
}
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getDoorbellSensorValue: function(callback) {
callback(null, false);
},
getStringValue: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
if (s.SwitchTypeVal == Constants.DeviceTypeContact || s.SwitchTypeVal == Constants.DeviceTypeDoorContact) {
if (s.Data == "Closed") {
value = Characteristic.ContactSensorState.CONTACT_DETECTED;
} else {
value = Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
}
} else if (s.SwitchTypeVal == Constants.DeviceTypeSmoke) {
if (s.Data == "Off" || s.Data == "Normal") {
value = Characteristic.SmokeDetected.SMOKE_NOT_DETECTED;
} else {
value = Characteristic.SmokeDetected.SMOKE_DETECTED;
}
} else if (s.SwitchTypeVal == Constants.DeviceTypeMotion) {
if (s.Data == "Off") {
value = false;
} else {
value = true;
}
} else if (this.Type == "Lux") { //Lux
value = parseInt(s.Data, 10);
value = (value == 0) ? 0.0001 : value;
} else if (this.subType == "Waterflow" || (this.name.indexOf("Gas") > -1 && this.Type == "General" && this.subType == "kWh")) {
value = Helper.cleanFloat(s.Data);
} else if (this.subType == "RFXMeter counter" || this.subType == "Percentage" || this.subType == "kWh" || this.subType == "Energy" || this.subType == "Solar Radiation" || this.subType == "UVN800" || this.subType == "UVN128,UV138" || this.subType == "Visibility") {
value = (s.Counter !== undefined) ? Helper.cleanFloat(s.Counter) : Helper.cleanFloat(s.Data);
} else if (this.subType == "Text") {
value = s.Data.toString();
value = encodeURIComponent(value);
} else {
value = s.Data.toString();
value = encodeURIComponent(value);
}
}.bind(this));
callback(null, value);
}.bind(this));
},
getYLTodayValue: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.CounterToday);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getYLTotalValue: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.Counter);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getWindSpeed: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.Speed);
value = Helper.oneDP(value);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getWindChill: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.Chill);
value = Helper.oneDP(value);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getWindDirection: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = s.Direction.toString() + " (" + s.DirectionStr.toString() + ")";
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getCPower: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = (this.Type == "Usage" && this.subType == "Electric") ? Helper.cleanFloat(s.Data) : Helper.cleanFloat(s.Usage);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getState: function (callback) {
value = 1;
this.platform.log("Static Data for " + this.name + ": " + value);
callback(null, value);
},
getTemperature: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
var heat = (this.subType == "Zone") ? true : false;
var therm = (this.subType == "SetPoint") ? true : false;
value = ((heat) || (therm)) ? Helper.oneDP(Helper.cleanFloat(s.SetPoint)) : Helper.oneDP(Helper.cleanFloat(s.Temp));
if (s.HaveTimeout=='true') {
value = null;
}
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getTemperatureAlternative: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
var heat = (this.subType == "Zone") ? true : false;
var therm = (this.subType == "SetPoint") ? true : false;
value = Helper.oneDP(Helper.cleanFloat(s.Temp));
if (s.HaveTimeout=='true') {
value = null;
}
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getTimeout: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
var reachable = (s.HaveTimeout === true) ? '1' : '0';
value = reachable;
}.bind(this));
this.platform.log("Data Received for " + this.name + " Timeout: " + value);
callback(null, value);
}.bind(this));
},
setPoint: function (setpoint, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
var url = "";
if (this.subType == "SetPoint") {
url = this.platform.apiBaseURL + "type=command¶m=udevice&idx=" + this.idx;
url = url + "&nvalue=0&svalue=" + setpoint;
} else if (this.subType == "Zone") {
url = this.platform.apiBaseURL + "type=command¶m=setused&idx=" + this.idx + "&setpoint=";
url = url + setpoint + "&mode=PermanentOverride&used=true";
}
this.platform.log("Setting thermostat SetPoint to " + setpoint);
Domoticz.updateWithURL(this, url, function (success) {
callback(null, setpoint);
}.bind(this));
},
setTempOverride: function (setuntil, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
var url = "";
var temp;
var now = new Date();
var newnow, isonow;
var mode;
if (setuntil < 1) {
mode = "Auto";
} else if (setuntil > 480) {
mode = "PermanentOverride";
} else {
mode = "TemporaryOverride";
newnow = new Date(now.getTime() + (setuntil * 60 * 1000));
isonow = newnow.toISOString();
}
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
var heat = (this.Type == "Heating" && this.subType == "Zone") ? true : false;
var therm = ((this.Type == "Thermostat" || this.Type == "Setpoint") && this.subType == "SetPoint") ? true : false;
temp = (heat || therm) ? Helper.oneDP(Helper.cleanFloat(s.SetPoint)) : Helper.oneDP(Helper.cleanFloat(s.Temp));
url = this.platform.apiBaseURL + "type=command¶m=setused&idx=" + this.idx + "&setpoint=";
url = url + temp + "&mode=" + mode;
url = (mode == "TemporaryOverride") ? url + "&until=" + isonow + "&used=true" : url + "&used=true";
this.platform.log("Setting thermostat SetPoint to " + temp + ", mode to " + mode);
Domoticz.updateWithURL(this, url, function (success) {
callback(null, setuntil);
}.bind(this));
}.bind(this));
}.bind(this), function (error) {
callback();
});
},
getTempOverride: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
var d1 = new Date(s.Until);
var now = new Date().getTime();
var diff = d1 - now;
value = (diff / (60 * 1000));
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getHumidity: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.Humidity);
value = Helper.oneDP(value);
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getPressure: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
var val = Helper.cleanFloat(s.Barometer);
val = Math.ceil(val);
value = val;
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
getLowBatteryStatus: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = Helper.cleanFloat(s.BatteryLevel);
}.bind(this));
if (value > 20) {
callback(null, 0);
} else {
callback(null, 1);
}
}.bind(this));
},
getBlindStatus: function (callback) {
if (this.isPercentageBlind) {
this.getdValue(callback);
return;
}
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = s.Data.toString();
}.bind(this));
if (value == "Open") {
callback(null, 100);
} else {
callback(null, 0);
}
}.bind(this));
},
setBlindStatus: function (blindService, pos, callback, context) {
var shouldOpen = (pos <= 50);
if (this.isInvertedBlind) {
shouldOpen = !shouldOpen;
}
var finish = function (position) {
callback();
if (!this.isPercentageBlind) {
this.getCharacteristic(blindService, Characteristic.CurrentPosition).setValue(position, false, this);
}
}.bind(this);
if (context && context == "eDomoticz-MQTT") {
finish(pos);
return;
}
if (this.isPercentageBlind && pos > 0 && pos < 100) {
this.setdValue(pos, function () {
finish(pos);
});
return;
}
if (this.platform.config.legacyBlinds == 1) {
var command = (shouldOpen ? "On" : "Off");
} else {
var command = (shouldOpen ? "Close" : "Open");
}
Domoticz.updateDeviceStatus(this, "switchlight", {
"switchcmd": command
}, function (success) {
finish(pos);
}.bind(this));
},
getBlindPStatus: function (callback) {
callback(null, Characteristic.PositionState.STOPPED);
},
getLockStatus: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = (s.Data == "Open" || s.Data == "Unlocked") ? Characteristic.LockCurrentState.UNSECURED : Characteristic.LockCurrentState.SECURED;
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
setLockStatus: function (doorstate, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
var command = (doorstate == Characteristic.LockTargetState.UNSECURED);
if (this.swTypeVal == Constants.DeviceTypeDoorLock) {
command = !command;
}
Domoticz.updateDeviceStatus(this, "switchlight", {
"switchcmd": (command ? "On" : "Off")
}, function (success) {
callback();
}.bind(this));
},
setLockInvertedStatus: function (doorstate, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
var command = (doorstate == Characteristic.LockTargetState.SECURED);
if (this.swTypeVal == Constants.DeviceTypeDoorLockInverted) {
command = !command;
}
Domoticz.updateDeviceStatus(this, "switchlight", {
"switchcmd": (command ? "On" : "Off")
}, function (success) {
callback();
}.bind(this));
},
getDoorStatus: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = (s.Data == "Open" || s.Data == "Unlocked") ? Characteristic.CurrentDoorState.OPEN : Characteristic.CurrentDoorState.CLOSED;
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
setDoorStatus: function (doorstate, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
var command = (doorstate == Characteristic.TargetDoorState.OPEN);
if (this.swTypeVal == Constants.DeviceTypeDoorLock) {
command = !command;
}
Domoticz.updateDeviceStatus(this, "switchlight", {
"switchcmd": (command ? "On" : "Off")
}, function (success) {
callback();
}.bind(this));
},
getSecuritySystemStatus: function (callback) {
var cachedValue = this.cachedValues[Characteristic.SecuritySystemCurrentState.UUID];
if (typeof cachedValue !== 'undefined') {
callback(null, cachedValue);
}
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
switch (s.Data) {
case "Arm Home":
value = Characteristic.SecuritySystemCurrentState.STAY_ARM;
break;
case "Arm Away":
value = Characteristic.SecuritySystemCurrentState.AWAY_ARM;
break;
case "Normal":
default:
value = Characteristic.SecuritySystemCurrentState.DISARMED;
break;
}
}.bind(this));
if (typeof cachedValue === 'undefined') {
callback(null, value);
}
this.cachedValues[Characteristic.SecuritySystemCurrentState.UUID] = value;
}.bind(this));
},
setSecuritySystemStatus: function (securityService, alarmState, callback, context) {
if (context && (context == "eDomoticz-MQTT" || context == "callback-self")) {
if (callback) {
callback();
}
return;
}
Domoticz.settings(this, function (settings) {
var secStatus = "0";
var targetState = alarmState;
switch (alarmState) {
case Characteristic.SecuritySystemCurrentState.STAY_ARM:
case Characteristic.SecuritySystemCurrentState.NIGHT_ARM:
secStatus = "1";
if (Characteristic.SecuritySystemCurrentState.NIGHT_ARM) {
targetState = Characteristic.SecuritySystemCurrentState.STAY_ARM;
}
break;
case Characteristic.SecuritySystemCurrentState.AWAY_ARM:
secStatus = "2";
break;
case Characteristic.SecuritySystemCurrentState.DISARMED:
default:
secStatus = "0";
targetState = Characteristic.SecuritySystemCurrentState.DISARMED;
break;
}
var url = this.platform.apiBaseURL + "type=command¶m=setsecstatus&secstatus=" + secStatus + "&seccode=" + settings.SecPassword;
Domoticz.updateWithURL(this, url, function (success) {
this.cachedValues[Characteristic.SecuritySystemCurrentState.UUID] = targetState;
callback();
this.getCharacteristic(securityService, Characteristic.SecuritySystemCurrentState).setValue(targetState, false, "callback-self");
setTimeout(function () {
this.getCharacteristic(securityService, Characteristic.SecuritySystemTargetState).setValue(targetState, false, "callback-self");
}.bind(this), 200);
}.bind(this));
}.bind(this));
},
getSelectorValue: function (callback) {
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
switch (s.Level){
case "10":
value = Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS;
break;
case "20":
value = Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS;
break;
case "30":
value = Characteristic.ProgrammableSwitchEvent.LONG_PRESS;
break;
}
}.bind(this));
this.platform.log("Data Received for " + this.name + ": " + value);
callback(null, value);
}.bind(this));
},
sendTelevisionRemoteKey: function (key, callback) {
if(this.mediaCommandMapping && key in this.mediaCommandMapping) {
Domoticz.updateDeviceStatus(this, "kodimediacommand", {
"action": this.mediaCommandMapping[key],
}, function (success) {
callback();
}.bind(this));
} else {
callback(new Error("Unsupported remote key!"));
}
},
setTelevisionVolume: function (action, callback) {
var domoticzAction;
if (action == Characteristic.VolumeSelector.INCREMENT) {
domoticzAction = "VolumeUp";
} else if (action == Characteristic.VolumeSelector.DECREMENT) {
domoticzAction = "VolumeDown";
} else {
callback(new Error("Unsupported volume action!"));
return;
}
Domoticz.updateDeviceStatus(this, "kodimediacommand", {
"action": domoticzAction
}, function (success) {
callback();
}.bind(this));
},
setTelevisionMuteState: function (state, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
Domoticz.updateDeviceStatus(this, "kodimediacommand", {
"action": "Mute",
}, function (success) {
this.cachedValues[Characteristic.Mute.UUID] = state;
callback();
}.bind(this));
},
getTelevisionMuteState: function (callback) {
var cachedValue = this.cachedValues[Characteristic.Mute.UUID];
if (typeof cachedValue !== 'undefined') {
callback(null, cachedValue);
}
Domoticz.deviceStatus(this, function (json) {
var value;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = (s.Data.indexOf("(muted)") > -1);
}.bind(this));
if (typeof cachedValue === 'undefined') {
callback(null, value);
}
this.cachedValues[Characteristic.Mute.UUID] = value;
}.bind(this));
},
setActiveIdentifier: function (identifier, callback, context) {
if (context && context == "eDomoticz-MQTT") {
callback();
return;
}
if (this.descript in tvInputAccessories) {
Domoticz.updateDeviceStatus(tvInputAccessories[this.descript], "switchlight", {
"switchcmd": "Set Level",
"level": identifier * 10
}, function (success) {
this.cachedValues[Characteristic.ActiveIdentifier.UUID] = identifier;
callback();
}.bind(this));
}
},
getActiveIdentifier: function (callback) {
var cachedValue = this.cachedValues[Characteristic.ActiveIdentifier.UUID];
if (typeof cachedValue !== 'undefined') {
callback(null, cachedValue);
}
if (this.descript in tvInputAccessories) {
Domoticz.deviceStatus(tvInputAccessories[this.descript], function (json) {
var value = 0;
var sArray = Helper.sortByKey(json.result, "Name");
sArray.map(function (s) {
value = s.Level / 10;
}.bind(this));
if (typeof cachedValue === 'undefined') {
callback(null, value);
}
this.cachedValues[Characteristic.ActiveIdentifier.UUID] = value;
}.bind(this));
} else if (typeof cachedValue === 'undefined') {
this.cachedValues[Characteristic.ActiveIdentifier.UUID] = 0;
callback(null, 0);
}
},
handleMQTTMessage: function (message, callback) {
this.platform.log("MQTT Message received for %s.\nName:\t\t%s\nDevice:\t\t%s,%s\nIs Switch:\t%s\nSwitchTypeVal:\t%s\nMQTT Message:\n%s", this.name, this.name, this.Type, this.subType, this.isSwitch, this.swTypeVal, JSON.stringify(message, null, 4));
if ((this.Type == "P1 Smart Meter" && this.swTypeVal == 0 && this.subType == "Energy") || (this.Type == "P1 Smart Meter" && this.swTypeVal == 1 && this.subType == "Gas") || (this.Type == "General" && this.swTypeVal == 2 && this.subType == "Counter Incremental") || (this.name.indexOf("Occupied") > -1) || (this.Type == "General" && this.swTypeVal == 1 && this.subType == "Visibility") || (this.Type == "General" && this.swTypeVal === 0 && this.subType == "kWh") || (this.Type == "General" && this.subType == "Solar Radiation" && this.swTypeVal === 0) || (this.Type == "YouLess Meter" && this.swTypeVal === 0) || (this.name.indexOf("Location") > -1) || (this.Type == "RFXMeter")) {
this.swTypeVal = false;
this.isSwitch = false;
//cludgey fix for a P1 SmartMeter Virtual Sensor being ID'd as a doorbell in Domoticz, and Incremental Counters being id'd as contact switches
//and other such Domoticz-generated oddities
}
if (this.isSwitch) {
switch (true) {
case this.swTypeVal == Constants.DeviceTypeSwitch || this.swTypeVal == Constants.DeviceTypePushOn:
{
var service = false;
if (this.image !== undefined && this.swTypeVal !== Constants.DeviceTypePushOn) {
if (this.image.indexOf("Fan") > -1) {
service = this.getService(Service.Fan);
} else if (this.image.indexOf("Light") > -1) {
service = this.getService(Service.Lightbulb);
} else if (this.image.indexOf("WallSocket") > -1) {
service = this.getService(Service.Outlet);
} else {
service = this.getService(Service.Switch);
}
} else {
service = this.getService(Service.Switch);
}
if (!service) {
break;
}
var powerOn = (message.nvalue > 0);
this.cachedValues[Characteristic.On.UUID] = powerOn;
var characteristic = this.getCharacteristic(service, Characteristic.On);
callback(characteristic, powerOn);
break;
}
case this.swTypeVal == Constants.DeviceTypeContact:
case this.swTypeVal == Constants.DeviceTypeDoorContact:
{
var service = this.getService(Service.ContactSensor);
var characteristic = this.getCharacteristic(service, Characteristic.ContactSensorState);
callback(characteristic, message.nvalue);
break;
}
case this.swTypeVal == Constants.DeviceTypeSmoke:
{
var service = this.getService(Service.SmokeSensor);
var characteristic = this.getCharacteristic(service, Characteristic.SmokeDetected);
callback(characteristic, message.nvalue);
break;
}
case this.swTypeVal == Constants.DeviceTypeDimmer:
{
var isFan = this.image && this.image.indexOf("Fan") > -1
var service = this.getService(isFan ? Service.Fan : Service.Lightbulb);
var powerCharacteristic = this.getCharacteristic(service, Characteristic.On);
var dimCharacteristic = this.getCharacteristic(service, isFan ? Characteristic.RotationSpeed : Characteristic.Brightness);
var isOn = (message.nvalue > 0);
var wasOn = this.cachedValues[Characteristic.On.UUID];
var dimCallbackDelay = 1;
if (message.svalue1 == 0 || isOn != wasOn) {
callback(powerCharacteristic, isOn);
this.cachedValues[Characteristic.On.UUID] = isOn;
if (isOn) {
dimCallbackDelay = 200;
}
}
if (isOn && this.factor) {
var handleDimLevelChange = function (level) {
level = Math.floor(level);
if (level > 0) {
setTimeout(function () {
this.cachedValues[Characteristic.Brightness.UUID] = level;
callback(dimCharacteristic, level);
}.bind(this), dimCallbackDelay);
}
}.bind(this);
// Switch is a dimmer but MQTT didn't return level
if (typeof message.svalue1 == 'undefined') {
this.cachedValues[Characteristic.Brightness.UUID] = 0;
dimCharacteristic.getValue(function (sender, level) {
handleDimLevelChange(level);
}.bind(this));
return;
}
var level = message.svalue1 * this.factor;
handleDimLevelChange(level);
}
break;
}
case this.swTypeVal == Constants.DeviceTypeMedia:
{
// TV power state.
var tvService = this.getService(Service.Television);
var activeCharacteristic = this.getCharacteristic(tvService, Characteristic.Active);
var state = (message.nvalue > 0) ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
var oldState = this.cachedValues[Characteristic.Active.UUID];
if (state != oldState) {
this.cachedValues[Characteristic.Active.UUID] = state;
callback(activeCharacteristic, state);
}
// TV mute state.
var tvSpeakerService = this.getService(Service.TelevisionSpeaker);
var mutedCharacteristic = this.getCharacteristic(tvSpeakerService, Characteristic.Mute);
var isMuted = value = (message.svalue1.indexOf("(muted)") > -1);
var oldIsMuted = this.cachedValues[Characteristic.Mute.UUID];
if (isMuted != oldIsMuted) {
this.cachedValues[Characteristic.Mute.UUID] = isMuted;
callback(mutedCharacteristic, isMuted);
}
break;
}
case this.swTypeVal == Constants.DeviceTypeMotion:
{
var service = this.getService(Service.MotionSensor);
var characteristic = this.getCharacteristic(service, Characteristic.MotionDetected);
callback(characteristic, message.nvalue);
break;
}
case this.swTypeVal == Constants.DeviceTypeDoorbell:
{
var service = this.getService(Service.StatelessProgrammableSwitch);
var characteristic = this.getCharacteristic(service, Characteristic.ProgrammableSwitchEvent);
callback(characteristic, Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
var service = this.getService(Service.Doorbell);
var characteristic = this.getCharacteristic(service, Characteristic.ProgrammableSwitchEvent);
callback(characteristic, Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
break;
}
case this.swTypeVal == Constants.DeviceTypeSelector && this.image !== "TV":
{
var service = this.getService(Service.StatelessProgrammableSwitch);
var characteristic = this.getCharacteristic(service, Characteristic.ProgrammableSwitchEvent);
var value;
switch (message.svalue1) {
case "10":
value = Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS;
break;
case "20":
value = Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS;
break;
case "30":
value = Characteristic.ProgrammableSwitchEvent.LONG_PRESS;
break;
}
callback(characteristic, value);