UNPKG

homebridge-edomoticz

Version:

homebridge-plugin for Domoticz https://github.com/nfarina/homebridge

1,198 lines (1,116 loc) 124 kB
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&param=udevice&idx=" + this.idx; url = url + "&nvalue=0&svalue=" + setpoint; } else if (this.subType == "Zone") { url = this.platform.apiBaseURL + "type=command&param=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&param=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&param=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);