diff --git a/CHANGES.md b/CHANGES.md index c7c8ada..eea975f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,16 +1,21 @@ Release Notes ========== -Version 0.13.1 (Beta 13 Release 1) +Version 0.14 (Beta 14) ========== -* New device type added: [Filter Maintenance](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#filtermaintenance) - I was so excited about this until the dreaded "unsupported" came up. Hopefully one day soon it will work because I even created a special device for this in Device Extensions. That's OK, the device extensions device is super cool anyway :) -* New device type added: [Carbon Dioxide Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#carbondioxidesensor) -* Added checkbox to the plugin menu Item Mover utility to show HBB devices with a special suffix, allowing the user to begin to redefine hold HBB Wrappers and Aliases that now have native support and are not needed anymore -* Added 3rd party API framework and integration libraries (more on this later as it's not something the beta testers will be testing at this point) -* Fixed bug in Homebridge Buddy / Homebridge-Indigo migration utility (in Advanced Plugin menu option) that migrated Alias devices when it was supposed to use the native device instead, now it will NOT migrate HBB Alias devices since that device is no longer needed with the ability to define alias names up front -* Fixed bug where under certain circumstances a user could save a device that didn't have a defined HomeKit device type -* Fixed bug where removing a server would cause it to stay in the ID array and cause errors any time a device that was a part of that server is updated in Indigo by rebuilding the ID index after each form save -* Fixed bug where removing a device would cause it to stay in the ID array and cause errors +* New device type added: [Irrigation System](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#irrigationsystem) - Note that complications have not yet been coded so you cannot yet tie zone valves to this but you can see the running state of your sprinklers here +* New device type added: [Air Purifier](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#airpurifier) +* New device type added: [Carbon Monoxide Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#carbonmonoxidesensor) +* New device type added: [Doorbell](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#doorbell) - Don't get your hopes up, this is totally experimental and is likely going to need a lot of work and it was added simply as a development tool for now +* New device type added: [Faucet](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#faucet) - Yea, I don't know who has automated faucets either, but HomeKit seems to think people do or will in their dystopian future.... It's essentially a switch so maybe you can use it for a cool icon for something else +* New device type added: [Security System](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#securitysystem) - This has been coded to work with DSC alarms because it seems to be the most widely used on Indigo, more will be added in the future. I do not have a DSC so this in the blind, let me know if it works! +* New device type added: [Slat](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#slat) +* New device type added: [Valve](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#valve) - Will be used in complications for irrigation systems when that comes around but for now it's just based on on/off states +* Changed "Unsupported" language to only apply if it's unsupported in Apple Home, the non-Apple Home and Eve because testing showed that almost all "Unsupported" devices work in Eve and non-Apple Home, now items that only workin 3rd party apps will be listed as "3rd Party Only" +* Added stricter conformity to characteristic restrictions (left loose during initial testing) to make sure no value can be greater or less than what HomeKit expects +* Fixed bug where float values would not convert to int values on characteristic validation +* Fixed bug in cache rebuilding that would leave deleted devices or servers in the cache and could cause errors when the plugin would try to access them +* Fixed bug when devices attached to the server are updated but reference a deleted server, the system will now make sure the server ID still exists in Indigo [reported by siclark](http://forums.indigodomo.com/viewtopic.php?p=155668#p155668) Known Issues --------------- @@ -32,6 +37,18 @@ Wish List Previous Release Notes ========== +Version 0.13.1 (Beta 13 Release 1) +--------------- +* New device type added: [Filter Maintenance](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#filtermaintenance) - I was so excited about this until the dreaded "unsupported" came up. Hopefully one day soon it will work because I even created a special device for this in Device Extensions. That's OK, the device extensions device is super cool anyway :) +* New device type added: [Carbon Dioxide Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#carbondioxidesensor) +* Added checkbox to the plugin menu Item Mover utility to show HBB devices with a special suffix, allowing the user to begin to redefine hold HBB Wrappers and Aliases that now have native support and are not needed anymore +* Added 3rd party API framework and integration libraries (more on this later as it's not something the beta testers will be testing at this point) +* Fixed bug in Homebridge Buddy / Homebridge-Indigo migration utility (in Advanced Plugin menu option) that migrated Alias devices when it was supposed to use the native device instead, now it will NOT migrate HBB Alias devices since that device is no longer needed with the ability to define alias names up front +* Fixed bug where under certain circumstances a user could save a device that didn't have a defined HomeKit device type +* Fixed bug where removing a server would cause it to stay in the ID array and cause errors any time a device that was a part of that server is updated in Indigo by rebuilding the ID index after each form save +* Fixed bug where removing a device would cause it to stay in the ID array and cause errors + + Version 0.13.0 (Beta 13) --------------- * This marks the next milestone of inviting the next group of beta testers in since the new plugin install issue has been resolved diff --git a/EPS HomeKit Bridge.indigoPlugin/Contents/Info.plist b/EPS HomeKit Bridge.indigoPlugin/Contents/Info.plist index 6e180cc..216de42 100755 --- a/EPS HomeKit Bridge.indigoPlugin/Contents/Info.plist +++ b/EPS HomeKit Bridge.indigoPlugin/Contents/Info.plist @@ -3,7 +3,7 @@ PluginVersion - 0.13.1 + 0.14.0 ServerApiVersion 2.0 IwsApiVersion diff --git a/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/bin/hb/node-v8.9.4-darwin-x64/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/gen/HomeKitTypes.js b/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/bin/hb/node-v8.9.4-darwin-x64/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/gen/HomeKitTypes.js index 2f6eb15..1d50602 100755 --- a/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/bin/hb/node-v8.9.4-darwin-x64/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/gen/HomeKitTypes.js +++ b/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/bin/hb/node-v8.9.4-darwin-x64/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/gen/HomeKitTypes.js @@ -1637,7 +1637,7 @@ Characteristic.RemainingDuration = function() { Characteristic.call(this, 'Remaining Duration', '000000D4-0000-1000-8000-0026BB765291'); this.setProps({ format: Characteristic.Formats.UINT32, - maxValue: 3600, + maxValue: 43200, minValue: 0, minStep: 1, perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY] @@ -1894,7 +1894,7 @@ Characteristic.SetDuration = function() { Characteristic.call(this, 'Set Duration', '000000D3-0000-1000-8000-0026BB765291'); this.setProps({ format: Characteristic.Formats.UINT32, - maxValue: 3600, + maxValue: 43200, minValue: 0, minStep: 1, perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY] diff --git a/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/lib/homekit.py b/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/lib/homekit.py index 2476164..5b28e07 100755 --- a/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/lib/homekit.py +++ b/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/lib/homekit.py @@ -56,6 +56,8 @@ def printClassLookupDict (self): serviceList += "* [{}](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#{})\n".format(unicode(obj.desc), cls[0].replace("service_", "").lower()) d += "**Device**: {}\n\n".format(unicode(obj.desc)) + + if "wiki" in dir(obj): d += "**Notes**: {}\n\n".format(obj.wiki) requiredItems = "" optionalItems = "" @@ -258,6 +260,9 @@ def detectHomeKitType (self, objId): elif "supportsCoolSetpoint" in dir(dev): return "service_Thermostat" + elif "zoneCount" in dir(dev): + return "service_IrrigationSystem" + else: # Fallback but only if there is an onstate, otherwise we return an unknown if "onState" in dir(dev): @@ -691,7 +696,14 @@ def __init__ (self, factory, hktype, desc, objId, serverId, deviceCharacteristic self.indigoType = "Unable to detect" self.pluginType = "Built-In" self.invertOnState = False # This is set by the HTTP utility during processing if the user passed it in the stash + + # For timer baseed operations + self.recurringUpdate = False # When true, the API will schedule a thread to refresh every X seconds + self.recurringSeconds = 0 + + self.jsoninit = False # Just while we are dialing in the JSON dict file, this needs to be removed when we convert all HK types to the new method + # Get the indigo class for this object if objId in indigo.devices: @@ -1250,6 +1262,19 @@ def calculateDefaultActionsForState (self, state, characteristic): else: invalidType = True + elif state == "activeZone": + cmd = "sprinkler.setActiveZone" + if method == "TF" or method == "01": + self.actions.append (HomeKitAction(characteristic, "equal", falseValue, cmd, [self.objId, 0], 0, {self.objId: "state_activeZone"})) + self.actions.append (HomeKitAction(characteristic, "equal", trueValue, cmd, [self.objId, 1], 0, {self.objId: "state_activeZone"})) + + elif method == "RANGE": + self.actions.append (HomeKitAction(characteristic, "equal", minValue, cmd, [self.objId, "=value="], 0, {self.objId: "state_activeZone"})) + self.actions.append (HomeKitAction(characteristic, "between", minValue + minStep, cmd, [self.objId, "=value="], maxValue, {self.objId: "state_activeZone"})) + + else: + invalidType = True + else: # Whatever else, if we didn't specify it, will get a dummy action associated with it and it could cause errors if the characteristic is # not read-only, but we need this so the plugin will monitor for any changes to the state @@ -1298,10 +1323,19 @@ def setAttributeValue (self, attribute, value): if vtype == "int" and atype == "float": obj.value = float(obj.value) converted = True + if vtype == "float" and atype == "int": + obj.value = int(round(obj.value)) + converted = True if not converted: self.logger.warning ("Unable to set the value of {} on {} to {} because that attribute requires {} and it was given {}".format(attribute, self.alias.value, unicode(value), atype, vtype)) return False + + + # Now that we have made sure the value type matches, make sure it conforms to the min/max/valid values + if type(value) != bool and type(value) != str and type(value) != unicode: + if "minValue" in dir(obj) and obj.value < obj.minValue: obj.value = obj.minValue + if "maxValue" in dir(obj) and obj.value > obj.maxValue: obj.value = obj.maxValue except Exception as e: self.logger.error (ext.getException(e)) @@ -1360,10 +1394,10 @@ def special_lowbattery (self, classes, sourceDict, getter, characteristic, isOpt if lowbattery > 0: lowbattery = lowbattery / 100 if obj.batteryLevel < ((100 * lowbattery) + 1): self.setAttributeValue (characteristic, 1) - self.characterDict[characteristic] = 1 + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, 0) - self.characterDict[characteristic] = 0 + self.characterDict[characteristic] = getattr (self, characteristic).value # So we get notified of any changes, add a trigger for this in actions, it won't do anything other than monitor self.actions.append (HomeKitAction(characteristic, "equal", False, "device.turnOff", [self.objId], 0, {self.objId: "attr_batteryLevel"})) @@ -1380,7 +1414,7 @@ def special_deReplaceFilter (self, classes, sourceDict, getter, characteristic, obj = indigo.devices[self.objId] if "sensorValue" in dir(obj) and obj.sensorValue is not None: self.setAttributeValue (characteristic, 1) - self.characterDict[characteristic] = 1 + self.characterDict[characteristic] = getattr (self, characteristic).value self.actions.append (HomeKitAction(characteristic, "between", 0, "homekit.runPluginAction", [indigo.devices[self.objId].pluginId, None, ["filterSensorChange", self.objId]], 100, {self.objId: "attr_sensorValue"})) @@ -1397,17 +1431,17 @@ def special_inuse (self, classes, sourceDict, getter, characteristic, isOptional # It supports energy reporting if obj.energyCurLevel > 0: self.setAttributeValue (characteristic, True) - self.characterDict[characteristic] = True + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, False) - self.characterDict[characteristic] = False + self.characterDict[characteristic] = getattr (self, characteristic).value else: if "onState" in dir(obj) and obj.onState: self.setAttributeValue (characteristic, True) - self.characterDict[characteristic] = True + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, False) - self.characterDict[characteristic] = False + self.characterDict[characteristic] = getattr (self, characteristic).value except Exception as e: self.logger.error (ext.getException(e) + "\nFor object id {} alias '{}'".format(str(self.objId), self.alias.value)) @@ -1422,13 +1456,13 @@ def special_invertedOnState (self, classes, sourceDict, getter, characteristic, if "onState" in dir(obj): if obj.onState: self.setAttributeValue (characteristic, False) - self.characterDict[characteristic] = False + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, True) - self.characterDict[characteristic] = True + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, False) - self.characterDict[characteristic] = False + self.characterDict[characteristic] = getattr (self, characteristic).value self.actions.append (HomeKitAction(characteristic, "equal", False, "device.turnOn", [self.objId], 0, {self.objId: "attr_onState"})) self.actions.append (HomeKitAction(characteristic, "between", True, "device.turnOff", [self.objId], 100, {self.objId: "attr_onState"})) @@ -1453,13 +1487,13 @@ def special_onStateToFullBrightness (self, classes, sourceDict, getter, characte if "onState" in dir(obj): if obj.onState: self.setAttributeValue (characteristic, onValue) - self.characterDict[characteristic] = onValue + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, offValue) - self.characterDict[characteristic] = offValue + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, offValue) - self.characterDict[characteristic] = offValue + self.characterDict[characteristic] = getattr (self, characteristic).value if self.invertOnState: # Remove all default actions or we'll end up just appending these on top and they won't get fired @@ -1488,16 +1522,16 @@ def special_thermHVACMode (self, classes, sourceDict, getter, characteristic, is if "hvacMode" in dir(obj): if unicode(obj.hvacMode) == "Heat" or unicode(obj.hvacMode) == "ProgramHeat": self.setAttributeValue (characteristic, 1) - self.characterDict[characteristic] = 1 + self.characterDict[characteristic] = getattr (self, characteristic).value elif unicode(obj.hvacMode) == "Cool" or unicode(obj.hvacMode) == "ProgramCool": self.setAttributeValue (characteristic, 2) - self.characterDict[characteristic] = 2 + self.characterDict[characteristic] = getattr (self, characteristic).value elif unicode(obj.hvacMode) == "HeatCool" or unicode(obj.hvacMode) == "ProgramHeatCool": self.setAttributeValue (characteristic, 3) - self.characterDict[characteristic] = 3 + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, 0) - self.characterDict[characteristic] = 0 + self.characterDict[characteristic] = getattr (self, characteristic).value #self.actions.append (HomeKitAction(characteristic, "equal", falseValue, cmd, [self.objId, 0], 0, {self.objId: "attr_brightness"})) @@ -1523,10 +1557,10 @@ def special_thermTemperatureSetPoint (self, classes, sourceDict, getter, charact if svr.pluginProps["tempunits"] == "f": value = (value - 32) / 1.8000 self.setAttributeValue (characteristic, round(value, 2)) - self.characterDict[characteristic] = round(value, 2) + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, round(float(obj.coolSetpoint), 2)) - self.characterDict[characteristic] = round(float(obj.coolSetpoint), 2) + self.characterDict[characteristic] = getattr (self, characteristic).value if unicode(obj.hvacMode) == "Heat" or unicode(obj.hvacMode) == "ProgramHeat": self.actions.append (HomeKitAction(characteristic, "between", 0.0, "homekit.commandSetTargetThermostatTemperature", [self.objId, self.serverId, "=value="], 100.0, {self.objId: "attr_heatSetpoint"})) @@ -1558,19 +1592,19 @@ def special_wsTemperature (self, classes, sourceDict, getter, characteristic, is value = (value - 32) / 1.8000 self.setAttributeValue (characteristic, round(value, 2)) - self.characterDict[characteristic] = round(value, 2) + self.characterDict[characteristic] = getattr (self, characteristic).value # Dummy action just so we get status updates for temperature self.actions.append (HomeKitAction(characteristic, "equal", "STUB", "STUB", [self.objId, 0], 0, {self.objId: "state_temperature_F"})) else: self.setAttributeValue (characteristic, float(obj.states["temperature_C"])) - self.characterDict[characteristic] = float(obj.states["temperature_C"]) + self.characterDict[characteristic] = getattr (self, characteristic).value # Dummy action just so we get status updates for temperature self.actions.append (HomeKitAction(characteristic, "equal", "STUB", "STUB", [self.objId, 0], 0, {self.objId: "state_temperature_C"})) else: self.setAttributeValue (characteristic, float(obj.states["temperature_C"])) - self.characterDict[characteristic] = float(obj.states["temperature_C"]) + self.characterDict[characteristic] = getattr (self, characteristic).value # Dummy action just so we get status updates for temperature self.actions.append (HomeKitAction(characteristic, "equal", "STUB", "STUB", [self.objId, 0], 0, {self.objId: "state_temperature_C"})) @@ -1595,13 +1629,13 @@ def special_wuTemperature (self, classes, sourceDict, getter, characteristic, is value = (value - 32) / 1.8000 self.setAttributeValue (characteristic, round(value, 2)) - self.characterDict[characteristic] = round(value, 2) + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, float(obj.states["temp"])) - self.characterDict[characteristic] = float(obj.states["temp"]) + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, float(obj.states["temp"])) - self.characterDict[characteristic] = float(obj.states["temp"]) + self.characterDict[characteristic] = getattr (self, characteristic).value # Dummy action just so we get status updates for temperature self.actions.append (HomeKitAction(characteristic, "equal", "STUB", "STUB", [self.objId, 0], 0, {self.objId: "state_temp"})) @@ -1625,13 +1659,13 @@ def special_thermTemperature (self, classes, sourceDict, getter, characteristic, value = (value - 32) / 1.8000 self.setAttributeValue (characteristic, round(value, 2)) - self.characterDict[characteristic] = round(value, 2) + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, float(obj.states["temperatureInput1"])) - self.characterDict[characteristic] = float(obj.states["temperatureInput1"]) + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, float(obj.states["temperatureInput1"])) - self.characterDict[characteristic] = float(obj.states["temperatureInput1"]) + self.characterDict[characteristic] = getattr (self, characteristic).value # Dummy action just so we get status updates for temperature self.actions.append (HomeKitAction(characteristic, "equal", "STUB", "STUB", [self.objId, 0], 0, {self.objId: "state_temperatureInput1"})) @@ -1651,17 +1685,142 @@ def special_serverCorFSetting (self, classes, sourceDict, getter, characteristic if "tempunits" in obj.pluginProps: if obj.pluginProps["tempunits"] == "f": self.setAttributeValue (characteristic, 1) - self.characterDict[characteristic] = 1 + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, 1) - self.characterDict[characteristic] = 1 + self.characterDict[characteristic] = getattr (self, characteristic).value else: self.setAttributeValue (characteristic, 1) - self.characterDict[characteristic] = 1 + self.characterDict[characteristic] = getattr (self, characteristic).value + + except Exception as e: + self.logger.error (ext.getException(e) + "\nFor object id {} alias '{}'".format(str(self.objId), self.alias.value)) + + + # + # Sprinkler program mode + # + def special_sprinklerProgramMode (self, classes, sourceDict, getter, characteristic, isOptional = False): + try: + if self.serverId == 0: return + + obj = indigo.devices[self.objId] + if "activeZone" in dir(obj): + if obj.activeZone is None or obj.activeZone == 0: + self.setAttributeValue (characteristic, 0) + self.characterDict[characteristic] = getattr (self, characteristic).value + + else: + if len(obj.zoneScheduledDurations) == 0: + # Manually running + self.setAttributeValue (characteristic, 2) + self.characterDict[characteristic] = getattr (self, characteristic).value + + else: + # Program running + self.setAttributeValue (characteristic, 1) + self.characterDict[characteristic] = getattr (self, characteristic).value + else: + self.setAttributeValue (characteristic, 1) + self.characterDict[characteristic] = getattr (self, characteristic).value + + except Exception as e: + self.logger.error (ext.getException(e) + "\nFor object id {} alias '{}'".format(str(self.objId), self.alias.value)) + + # + # Sprinkler remaining duration + # + def special_sprinklerRemainingDuration (self, classes, sourceDict, getter, characteristic, isOptional = False): + try: + if self.serverId == 0: return + + obj = indigo.devices[self.objId] + if "activeZone" in dir(obj): + if obj.activeZone is None or obj.activeZone == 0: + self.recurringUpdate = False + self.setAttributeValue (characteristic, 0) + self.characterDict[characteristic] = 0 + + else: + self.recurringUpdate = True + self.recurringSeconds = 5 # Update every second during runtime + + totalScheduledTime = 0 + secondsRunTimeRemaining = 0 + + if len(obj.zoneScheduledDurations) == 0: + # Manual run, use max times + totalScheduledTime = obj.zoneMaxDurations[obj.activeZone - 1] + + else: + # Scheduled run, use schedule times + totalScheduledTime = obj.zoneScheduledDurations[obj.activeZone - 1] + + # Convert scheduled time to seconds, since Indigo runs in minutes + totalScheduledTime = totalScheduledTime * 60 + + # Calculate number of seconds that have transpired since the last time the device was updated, it should only update when we + # make changes that impact this routine anyway + seconds = dtutil.dateDiff ("seconds", indigo.server.getTime(), obj.lastChanged) + + # The remaining runtime should be total time less seconds since last change + secondsRunTimeRemaining = int(totalScheduledTime - seconds) + + self.setAttributeValue (characteristic, secondsRunTimeRemaining) + self.characterDict[characteristic] = getattr (self, characteristic).value + + if getattr (self, characteristic).value == 0: + # They ran past the time, could be an Indigo problem, stop the auto refreshing as it's always going to be zero anyway + self.recurringUpdate = False + + else: + pass # Do nothing, it's an optional characteristic so if we don't populate it then it just simply won't display + + + except Exception as e: + self.logger.error (ext.getException(e) + "\nFor object id {} alias '{}'".format(str(self.objId), self.alias.value)) + + + # + # DSC Alarm Plugin Keypad + # + def special_dscKeypadState (self, classes, sourceDict, getter, characteristic, isOptional = False): + try: + if self.serverId == 0: return + + obj = indigo.devices[self.objId] + if "ArmedState" in obj.states: + if obj.states["ArmedState.disarmed"]: + self.setAttributeValue (characteristic, 3) + self.characterDict[characteristic] = getattr (self, characteristic).value + + if obj.states["ArmedState.stay"]: + self.setAttributeValue (characteristic, 0) + self.characterDict[characteristic] = getattr (self, characteristic).value + + if obj.states["ArmedState.away"]: + self.setAttributeValue (characteristic, 1) + self.characterDict[characteristic] = getattr (self, characteristic).value + + if obj.states["state.tripped"]: + self.setAttributeValue (characteristic, 4) + self.characterDict[characteristic] = getattr (self, characteristic).value + else: + self.setAttributeValue (characteristic, 3) + self.characterDict[characteristic] = getattr (self, characteristic).value + + self.actions.append (HomeKitAction(characteristic, "equal", 0, "homekit.runPluginAction", [indigo.devices[self.objId].pluginId, None, ["actionArmStay", self.objId]], 100, {self.objId: "state_ArmedState.stay"})) + self.actions.append (HomeKitAction(characteristic, "equal", 1, "homekit.runPluginAction", [indigo.devices[self.objId].pluginId, None, ["actionArmAway", self.objId]], 100, {self.objId: "state_ArmedState.away"})) + self.actions.append (HomeKitAction(characteristic, "equal", 2, "homekit.runPluginAction", [indigo.devices[self.objId].pluginId, None, ["actionArmStay", self.objId]], 100, {self.objId: "state_ArmedState.stay"})) + self.actions.append (HomeKitAction(characteristic, "equal", 3, "homekit.runPluginAction", [indigo.devices[self.objId].pluginId, None, ["actionDisarm", self.objId]], 100, {self.objId: "state_ArmedState.disarmed"})) + + self.actions.append (HomeKitAction(characteristic, "equal", 99, "STUB", [indigo.devices[self.objId].pluginId, None, ["actionDisarm", self.objId]], 100, {self.objId: "state_state.tripped"})) except Exception as e: self.logger.error (ext.getException(e) + "\nFor object id {} alias '{}'".format(str(self.objId), self.alias.value)) + + ################################################################################ # HOMEKIT ACTIONS # @@ -1979,6 +2138,39 @@ def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActi if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) #self.logger.warning ('{} has no automatic conversion to HomeKit and will not be usable unless custom mapped'.format(self.alias.value)) +# ============================================================================== +# AIR PURIFIER +# ============================================================================== +class service_AirPurifier (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "AirPurifier" + desc = "Air Purifier" + + super(service_AirPurifier, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.required = ["Active", "CurrentAirPurifierState", "TargetAirPurifierState"] + self.optional = ["LockPhysicalControls", "Name", "SwingMode", "RotationSpeed"] + + self.requiredv2 = {} + self.requiredv2["Active"] = {"*": "attr_onState"} + self.requiredv2["CurrentAirPurifierState"] = {"*": "attr_onState"} + self.requiredv2["TargetAirPurifierState"] = {"*": "attr_onState"} + + self.optionalv2 = {} + self.optionalv2["LockPhysicalControls"] = {} + self.optionalv2["Name"] = {} + self.optionalv2["SwingMode"] = {} + self.optionalv2["RotationSpeed"] = {"*": "attr_brightness"} + + super(service_AirPurifier, self).setAttributesv2 () + #super(service_LockMechanism, self).setAttributes () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + # ============================================================================== # BATTERY SERVICE # ============================================================================== @@ -1989,7 +2181,9 @@ class service_BatteryService (Service): # def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): type = "BatteryService" - desc = "Battery Service (Unsupported)" + desc = "Battery Service (3rd Party Only)" + + self.wiki = "This service is unsupported by the native Apple Home application but is supported, in varying degrees, in 3rd party HomeKit apps. Apps tested with this service that work are the [non-Apple version of Home](https://itunes.apple.com/us/app/home-smart-home-automation/id995994352?mt=8) and [Elgato Eve](https://itunes.apple.com/us/app/elgato-eve/id917695792?mt=8)." super(service_BatteryService, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) @@ -2041,7 +2235,37 @@ def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActi super(service_CarbonDioxideSensor, self).setAttributesv2 () #super(service_MotionSensor, self).setAttributes () - if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + +# ============================================================================== +# CARBON MONOXIDE SENSOR +# ============================================================================== +class service_CarbonMonoxideSensor (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "CarbonMonoxideSensor" + desc = "Carbon Monoxide (CO) Sensor" + + super(service_CarbonMonoxideSensor, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.requiredv2 = {} + self.requiredv2["CarbonMonoxideDetected"] = {"*": "attr_onState"} + + self.optionalv2 = {} + self.optionalv2["StatusActive"] = {} + self.optionalv2["StatusFault"] = {} + self.optionalv2["StatusTampered"] = {} + self.optionalv2["StatusLowBattery"] = {"*": "special_lowbattery"} + self.optionalv2["Name"] = {} + self.optionalv2["CarbonMonoxideLevel"] = {"indigo.SensorDevice": "attr_sensorValue", "indigo.DimmerDevice": "attr_brightness"} + self.optionalv2["CarbonMonoxidePeakLevel"] = {} + + super(service_CarbonMonoxideSensor, self).setAttributesv2 () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) # ============================================================================== # CONTACT SENSOR @@ -2107,7 +2331,35 @@ def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActi super(service_Door, self).setAttributesv2 () #super(service_GarageDoorOpener, self).setAttributes () - if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + +# ============================================================================== +# DOOR BELL +# ============================================================================== +class service_Doorbell (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "Doorbell" + desc = "Doorbell (Experimental & Unsupported)" + + self.wiki = "This service is completely experimental as it relies on an undocumented HomeKit method that is still being decoded, consider this unusable until further notice and only appears in the plugin for development testing" + + super(service_Doorbell, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.requiredv2 = {} + self.requiredv2["ProgrammableSwitchEvent"] = {"*": "attr_onState"} + + self.optionalv2 = {} + self.optionalv2["Brightness"] = {"*": "attr_brightness"} + self.optionalv2["Volume"] = {} + self.optionalv2["Name"] = {} + + super(service_Doorbell, self).setAttributesv2 () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) # ============================================================================== # FAN V2 @@ -2143,6 +2395,31 @@ def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActi if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) +# ============================================================================== +# FAUCET +# ============================================================================== +class service_Faucet (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "Faucet" + desc = "Faucet" + + super(service_Faucet, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.requiredv2 = {} + self.requiredv2["Active"] = {"*": "attr_onState"} + + self.optionalv2 = {} + self.optionalv2["StatusFault"] = {} + self.optionalv2["Name"] = {} + + super(service_Faucet, self).setAttributesv2 () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + # ============================================================================== # FILTER MAINTENANCE # ============================================================================== @@ -2153,7 +2430,9 @@ class service_FilterMaintenance (Service): # def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): type = "FilterMaintenance" - desc = "Filter Maintenance (Unsupported)" + desc = "Filter Maintenance (3rd Party Only)" + + self.wiki = "This service is unsupported by the native Apple Home application but is supported, in varying degrees, in 3rd party HomeKit apps. Apps tested with this service that work are the [non-Apple version of Home](https://itunes.apple.com/us/app/home-smart-home-automation/id995994352?mt=8) and [Elgato Eve](https://itunes.apple.com/us/app/elgato-eve/id917695792?mt=8)." super(service_FilterMaintenance, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) @@ -2237,6 +2516,41 @@ def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActi if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + +# ============================================================================== +# IRRIGATION SYSTEM +# ============================================================================== +class service_IrrigationSystem (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "IrrigationSystem" + desc = "Irrigation System" + + self.wiki = "This service will automatically refresh back to HomeKit every 5 seconds if it is connected to an Indigo irrigation controller and if the controller has an active zone running so it can inform HomeKit of the number of remaining seconds left on the __current zone__." + + super(service_IrrigationSystem, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.required = ["Active", "ProgramMode", "InUse"] + self.optional = ["RemainingDuration", "StatusFault", "Name"] + + self.requiredv2 = {} + self.requiredv2["Active"] = {"*": "attr_onState", "indigo.SprinklerDevice": "state_activeZone"} + self.requiredv2["ProgramMode"] = {"*": "special_sprinklerProgramMode"} + self.requiredv2["InUse"] = {"*": "attr_onState", "indigo.SprinklerDevice": "state_activeZone"} + + self.optionalv2 = {} + self.optionalv2["RemainingDuration"] = {"indigo.SprinklerDevice": "special_sprinklerRemainingDuration"} #- maybe in the future, need to create an ongoing scheduled update to HomeKit for this to be effective + self.optionalv2["StatusFault"] = {} + self.optionalv2["Name"] = {} + + super(service_IrrigationSystem, self).setAttributesv2 () + #super(service_MotionSensor, self).setAttributes () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + # ============================================================================== # LEAK SENSOR @@ -2344,7 +2658,9 @@ class service_Microphone (Service): # def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): type = "Microphone" - desc = "Microphone (Unsupported)" + desc = "Microphone (3rd Party Only)" + + self.wiki = "This service is unsupported by the native Apple Home application but is supported, in varying degrees, in 3rd party HomeKit apps. Apps tested with this service that work are the [non-Apple version of Home](https://itunes.apple.com/us/app/home-smart-home-automation/id995994352?mt=8) and [Elgato Eve](https://itunes.apple.com/us/app/elgato-eve/id917695792?mt=8)." super(service_Microphone, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) @@ -2482,6 +2798,64 @@ def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActi if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) +# ============================================================================== +# SECURITY SYSTEM +# ============================================================================== +class service_SecuritySystem (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "SecuritySystem" + desc = "Security System" + + super(service_SecuritySystem, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.requiredv2 = {} + self.requiredv2["SecuritySystemCurrentState"] = {"*": "attr_onState", "indigo.Device.com.frightideas.indigoplugin.dscAlarm.alarmKeypad": "special_dscKeypadState"} + self.requiredv2["SecuritySystemTargetState"] = {"*": "attr_onState", "indigo.Device.com.frightideas.indigoplugin.dscAlarm.alarmKeypad": "special_dscKeypadState"} + + self.optionalv2 = {} + self.optionalv2["StatusFault"] = {} + self.optionalv2["StatusTampered"] = {} + self.optionalv2["SecuritySystemAlarmType"] = {} + self.optionalv2["Name"] = {} + + super(service_SecuritySystem, self).setAttributesv2 () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + +# ============================================================================== +# SLAT +# ============================================================================== +class service_Slat (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "Slat" + desc = "Slat (3rd Party Only)" + + self.wiki = "This service is unsupported by the native Apple Home application but is supported, in varying degrees, in 3rd party HomeKit apps. Apps tested with this service that work are the [non-Apple version of Home](https://itunes.apple.com/us/app/home-smart-home-automation/id995994352?mt=8) and [Elgato Eve](https://itunes.apple.com/us/app/elgato-eve/id917695792?mt=8)." + + super(service_Slat, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.requiredv2 = {} + self.requiredv2["SlatType"] = {} + self.requiredv2["CurrentSlatState"] = {"*": "attr_onState"} + + self.optionalv2 = {} + self.optionalv2["CurrentTiltAngle"] = {} + self.optionalv2["TargetTiltAngle"] = {} + self.optionalv2["SwingMode"] = {} + self.optionalv2["Name"] = {} + + super(service_Slat, self).setAttributesv2 () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + # ============================================================================== # SMOKE SENSOR # ============================================================================== @@ -2524,7 +2898,9 @@ class service_Speaker (Service): # def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): type = "Speaker" - desc = "Speaker (Unsupported)" + desc = "Speaker (3rd Party Only)" + + self.wiki = "This service is unsupported by the native Apple Home application but is supported, in varying degrees, in 3rd party HomeKit apps. Apps tested with this service that work are the [non-Apple version of Home](https://itunes.apple.com/us/app/home-smart-home-automation/id995994352?mt=8) and [Elgato Eve](https://itunes.apple.com/us/app/elgato-eve/id917695792?mt=8)." super(service_Speaker, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) @@ -2635,6 +3011,41 @@ def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActi #super(service_Thermostat, self).setAttributes () if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + + +# ============================================================================== +# VALVE +# ============================================================================== +class service_Valve (Service): + + # + # Initialize the class + # + def __init__ (self, factory, objId, serverId = 0, characterDict = {}, deviceActions = [], loadOptional = False): + type = "Valve" + desc = "Valve" + + self.wiki = "This service is unsupported by the native Apple Home application but is supported, in varying degrees, in 3rd party HomeKit apps. Apps tested with this service that work are the [non-Apple version of Home](https://itunes.apple.com/us/app/home-smart-home-automation/id995994352?mt=8) and [Elgato Eve](https://itunes.apple.com/us/app/elgato-eve/id917695792?mt=8)." + + super(service_Valve, self).__init__ (factory, type, desc, objId, serverId, characterDict, deviceActions, loadOptional) + + self.requiredv2 = {} + self.requiredv2["Active"] = {"*": "attr_onState"} + self.requiredv2["InUse"] = {"*": "attr_onState"} + self.requiredv2["ValveType"] = {} + + self.optionalv2 = {} + self.optionalv2["SetDuration"] = {} + self.optionalv2["RemainingDuration"] = {} + self.optionalv2["IsConfigured"] = {} + self.optionalv2["ServiceLabelIndex"] = {} + self.optionalv2["StatusFault"] = {} + self.optionalv2["Name"] = {} + + super(service_Valve, self).setAttributesv2 () + + if objId != 0: self.logger.debug ('{} started as a HomeKit {}'.format(self.alias.value, self.desc)) + # ============================================================================== # WINDOW @@ -2786,6 +3197,45 @@ def __init__(self): self.maxValue = 10000 self.minValue = 0 + self.readonly = False + self.notify = True + +# ============================================================================== +# CARBON MONOXIDE DETECTED +# ============================================================================== +class characteristic_CarbonMonoxideDetected: + def __init__(self): + self.value = 0 + self.maxValue = 1 + self.minValue = 0 + + self.validValues = [0, 1] + self.validValuesStr = "[levels normal, levels abnormal]" + + self.readonly = True + self.notify = True + +# ============================================================================== +# CARBON MONOXIDE LEVEL +# ============================================================================== +class characteristic_CarbonMonoxideLevel: + def __init__(self): + self.value = 0.0 + self.maxValue = 100 + self.minValue = 0 + + self.readonly = False + self.notify = True + +# ============================================================================== +# CARBON MONOXIDE PEAK LEVEL +# ============================================================================== +class characteristic_CarbonMonoxidePeakLevel: + def __init__(self): + self.value = 0.0 + self.maxValue = 100 + self.minValue = 0 + self.readonly = False self.notify = True @@ -2857,6 +3307,21 @@ def __init__(self): self.readonly = True self.notify = True + +# ============================================================================== +# CURRENT AIR PURIFIER STATE +# ============================================================================== +class characteristic_CurrentAirPurifierState: + def __init__(self): + self.value = 0 # open [closed, opening, closing, stopped] + self.maxValue = 2 + self.minValue = 0 + + self.validValues = [0, 1, 2] + self.validValuesStr = "[inactive, idle, purifying air]" + + self.readonly = True + self.notify = True # ============================================================================== # CURRENT DOOR STATE @@ -2940,7 +3405,22 @@ def __init__(self): self.minStep = 1 self.readonly = True - self.notify = True + self.notify = True + +# ============================================================================== +# CURRENT SLAT STATE +# ============================================================================== +class characteristic_CurrentSlatState: + def __init__(self): + self.value = 0 + self.maxValue = 2 + self.minValue = 0 + + self.validValues = [0, 1, 2] + self.validValuesStr = "[fixed, jammed, swinging]" + + self.readonly = False + self.notify = True # ============================================================================== # CURRENT TEMPERATURE (Need to update homebridge/hap-nodejs/lib/gen/HomeKitTypes.js to extend this range) @@ -2957,6 +3437,19 @@ def __init__(self): self.readonly = True self.notify = True +# ============================================================================== +# CURRENT TILT ANGLE +# ============================================================================== +class characteristic_CurrentVerticalTiltAngle: + def __init__(self): + self.value = 0 + self.maxValue = 90 + self.minValue = -90 + self.minStep = 1 + + self.readonly = True + self.notify = True + # ============================================================================== # CURRENT VERTICAL TILT ANGLE # ============================================================================== @@ -3036,6 +3529,36 @@ def __init__(self): self.readonly = False self.notify = True +# ============================================================================== +# IN USE +# ============================================================================== +class characteristic_InUse: + def __init__(self): + self.value = 0 + self.maxValue = 1 + self.minValue = 0 + + self.validValues = [0, 1] + self.validValuesStr = "[not in use, in use]" + + self.readonly = True + self.notify = True + +# ============================================================================== +# IS CONFIGURED +# ============================================================================== +class characteristic_IsConfigured: + def __init__(self): + self.value = 0 + self.maxValue = 1 + self.minValue = 0 + + self.validValues = [0, 1] + self.validValuesStr = "[not configured, is configured]" + + self.readonly = False + self.notify = True + # ============================================================================== # LEAK DETECTED # ============================================================================== @@ -3193,6 +3716,49 @@ def __init__(self): self.readonly = True self.notify = True +# ============================================================================== +# PROGRAM MODE +# ============================================================================== +class characteristic_ProgramMode: + def __init__(self): + self.value = 0 + self.maxValue = 2 + self.minValue = 0 + + self.validValues = [0, 1, 2] + self.validValuesStr = "[no program scheduled, program scheduled, manual mode]" + + self.readonly = True + self.notify = True + +# ============================================================================== +# PROGRAMMABLE SWITCH EVENT +# ============================================================================== +class characteristic_ProgrammableSwitchEvent: + def __init__(self): + self.value = 0 + self.maxValue = 2 + self.minValue = 0 + + self.validValues = [0, 1, 2] + self.validValuesStr = "[single press, double press, long press]" + + self.readonly = True + self.notify = True + +# ============================================================================== +# REMAINING DURATION (Need to update homebridge/hap-nodejs/lib/gen/HomeKitTypes.js to extend this range) +# ============================================================================== +class characteristic_RemainingDuration: + def __init__(self): + self.value = 0 + self.maxValue = 43200 # This is in seconds and defaulted to a ridiculous 3600 seconds or 5 minute duration! Updated it to be 12 hours + self.minValue = 0 + self.minStep = 1 + + self.readonly = True + self.notify = True + # ============================================================================== # RESET FILTER INDICATION # ============================================================================== @@ -3249,6 +3815,47 @@ def __init__(self): self.readonly = False self.notify = True +# ============================================================================== +# SERVICE LABEL INDEX +# ============================================================================== +class characteristic_ServiceLabelIndex: + def __init__(self): + self.value = 1 + self.maxValue = 255 + self.minValue = 1 + self.minStep = 1 + + self.readonly = False + self.notify = False + +# ============================================================================== +# SET DURATION (Need to update homebridge/hap-nodejs/lib/gen/HomeKitTypes.js to extend this range) +# ============================================================================== +class characteristic_SetDuration: + def __init__(self): + self.value = 0 + self.maxValue = 43200 # This is in seconds and defaulted to a ridiculous 3600 seconds or 5 minute duration! Updated it to be 12 hours + self.minValue = 0 + self.minStep = 1 + + self.readonly = False + self.notify = True + +# ============================================================================== +# SLAT TYPE +# ============================================================================== +class characteristic_SlatType: + def __init__(self): + self.value = 0 # clockwise [counter-clockwise] + self.maxValue = 1 + self.minValue = 0 + + self.validValues = [0, 1] + self.validValuesStr = "[horizontal, vertical]" + + self.readonly = True + self.notify = True + # ============================================================================== # SMOKE DETECTED # ============================================================================== @@ -3326,7 +3933,7 @@ def __init__(self): # ============================================================================== class characteristic_SwingMode: def __init__(self): - self.value = 0 # disabled [enabled] + self.value = 0 self.maxValue = 1 self.minValue = 0 @@ -3336,6 +3943,66 @@ def __init__(self): self.readonly = False self.notify = True +# ============================================================================== +# SECURITY SYSTEM ALARM TYPE +# ============================================================================== +class characteristic_SecuritySystemAlarmType: + def __init__(self): + self.value = 0 + self.maxValue = 1 + self.minValue = 0 + + self.validValues = [0, 1] + self.validValuesStr = "[no alarm, alarm]" + + self.readonly = True + self.notify = True + +# ============================================================================== +# SECURITY SYSTEM CURRENT STATE +# ============================================================================== +class characteristic_SecuritySystemCurrentState: + def __init__(self): + self.value = 0 + self.maxValue = 4 + self.minValue = 0 + + self.validValues = [0, 1, 2, 3, 4] + self.validValuesStr = "[stay armed, away armed, night armed, disarmed, alarm triggered]" + + self.readonly = True + self.notify = True + +# ============================================================================== +# SECURITY SYSTEM TARGET STATE +# ============================================================================== +class characteristic_SecuritySystemTargetState: + def __init__(self): + self.value = 0 # disabled [enabled] + self.maxValue = 4 + self.minValue = 0 + + self.validValues = [0, 1, 2, 3] + self.validValuesStr = "[stay arm, away arm, night arm, disarm]" + + self.readonly = False + self.notify = True + +# ============================================================================== +# TARGET AIR PURIFIER STATE +# ============================================================================== +class characteristic_TargetAirPurifierState: + def __init__(self): + self.value = 0 + self.maxValue = 2 + self.minValue = 0 + + self.validValues = [0, 1] + self.validValuesStr = "[manual, auto]" + + self.readonly = False + self.notify = True + # ============================================================================== # TARGET DOOR STATE # ============================================================================== @@ -3435,6 +4102,19 @@ def __init__(self): self.readonly = False self.notify = True +# ============================================================================== +# TARGET TILT ANGLE +# ============================================================================== +class characteristic_TargetTiltAngle: + def __init__(self): + self.value = 0 + self.maxValue = 90 + self.minValue = -90 + self.minStep = 1 + + self.readonly = False + self.notify = True + # ============================================================================== # TARGET VERTICAL TILT ANGLE # ============================================================================== @@ -3474,4 +4154,19 @@ def __init__(self): self.minStep = 1 self.readonly = False - self.notify = True \ No newline at end of file + self.notify = True + +# ============================================================================== +# VALVE TYPE +# ============================================================================== +class characteristic_ValveType: + def __init__(self): + self.value = 0 # celsius [fahrenheit] + self.maxValue = 3 + self.minValue = 0 + + self.validValues = [0, 1, 2, 3] + self.validValuesStr = "[generic, irrigation, shower head, water faucet]" + + self.readonly = True + self.notify = True \ No newline at end of file diff --git a/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/plugin.py b/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/plugin.py index c065a90..c76e9ba 100755 --- a/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/plugin.py +++ b/EPS HomeKit Bridge.indigoPlugin/Contents/Server Plugin/plugin.py @@ -93,7 +93,7 @@ def devTest (self): #eps.api.stopServer () #eps.api.run (self.pluginPrefs.get('apiport', '8558')) - x = eps.homekit.getServiceObject (1074591219, 1794022133, "service_CarbonDioxideSensor") + x = eps.homekit.getServiceObject (1551819695, 1794022133, "service_IrrigationSystem") #x.invertOnState = True #if x.invertOnState: x.setAttributesv2() indigo.server.log (unicode(x)) @@ -598,6 +598,8 @@ def onAfter_nonpluginDeviceUpdated (self, origDev, newDev): if youshallnotpass: return + rebuildRequired = False + #indigo.server.log (newDev.name) if newDev.id in self.SERVER_ID: self.logger.debug ("Indigo device {} changed and is linked to HomeKit, checking if that change impacts HomeKit".format(newDev.name)) @@ -605,6 +607,11 @@ def onAfter_nonpluginDeviceUpdated (self, origDev, newDev): for serverId in self.SERVER_ID[devId]: + if serverId not in indigo.devices: + self.logger.debug ("Server ID {} has been removed from Indigo but is still in cache, ignoring this update and removing it from cache to avoid further message or false positives".format(str(serverId))) + rebuildRequired = True + continue + valuesDict = self.serverCheckForJSONKeys (indigo.devices[serverId].pluginProps) includedDevices = json.loads(valuesDict["includedDevices"]) includedActions = json.loads(valuesDict["includedActions"]) @@ -653,6 +660,9 @@ def onAfter_nonpluginDeviceUpdated (self, origDev, newDev): self.logger.debug ("Device {} had an update that HomeKit needs to know about".format(obj.alias.value)) #self.serverSendObjectUpdateToHomebridge (indigo.devices[serverId], newDev.id) self.serverSendObjectUpdateToHomebridge (indigo.devices[serverId], r["jkey"]) + + if rebuildRequired: + self.catalogServerDevices() except Exception as e: self.logger.error (ext.getException(e)) @@ -920,7 +930,9 @@ def buildHKAPIDetails (self, objId, serverId, jkey, runningAction = False): #self.serverSendObjectUpdateToHomebridge (indigo.devices[int(serverId)], r["id"]) #thread.start_new_thread(self.timedCallbackToURL, (serverId, r["id"], 2)) thread.start_new_thread(self.timedCallbackToURL, (serverId, r["jkey"], 2)) - + + if obj.recurringUpdate: + thread.start_new_thread(self.timedCallbackToURL, (serverId, r["jkey"], obj.recurringSeconds)) # Before we return r, use the jstash ID for our ID instead of the Indigo ID @@ -1603,6 +1615,7 @@ def getNextAvailablePort (self, startPort, devId = 0, suppressLogging = False): def catalogServerDevices (self, serverId = 0): try: self.SERVERS = [] + self.SERVER_ID = {} if serverId == 0: for dev in indigo.devices.iter(self.pluginId + ".Server"): diff --git a/README.md b/README.md index 61a3d1e..91aa1e6 100644 --- a/README.md +++ b/README.md @@ -6,27 +6,35 @@ A bridge between HomeKit (via Homebridge) and Indigo. This is a direct replacem Supported Native Devices (Meaning Direct From Indigo to HomeKit Integration) --------------- * Action Groups -* [Battery Service (Unsupported)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#batteryservice) +* [Air Purifier](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#airpurifier) +* [Battery Service (3rd Party Only)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#batteryservice) * [Carbon Dioxide (CO2) Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#carbondioxidesensor) +* [Carbon Monoxide (CO) Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#carbonmonoxidesensor) * [Contact Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#contactsensor) * [Door](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#door) +* [Doorbell (Experimental & Unsupported)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#doorbell) * [Fan Version 2](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#fanv2) -* [Filter Maintenance (Unsupported)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#filtermaintenance) +* [Faucet](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#faucet) +* [Filter Maintenance (3rd Party Only)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#filtermaintenance) * [Garage Door Opener](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#garagedooropener) * [Humidity Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#humiditysensor) +* [Irrigation System](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#irrigationsystem) * [Leak Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#leaksensor) * [Light Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#lightsensor) * [Lightbulb](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#lightbulb) * [Lock Mechanism](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#lockmechanism) -* [Microphone (Unsupported)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#microphone) +* [Microphone (3rd Party Only)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#microphone) * [Motion Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#motionsensor) * [Occupancy Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#occupancysensor) * [Outlet](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#outlet) +* [Security System](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#securitysystem) +* [Slat (3rd Party Only)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#slat) * [Smoke Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#smokesensor) -* [Speaker (Unsupported)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#speaker) +* [Speaker (3rd Party Only)](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#speaker) * [Switch](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#switch) * [Temperature Sensor](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#temperaturesensor) * [Thermostat](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#thermostat) +* [Valve](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#valve) * [Window](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#window) * [Window Covering](https://github.com/Colorado4Wheeler/HomeKit-Bridge/wiki/HomeKit-Model-Reference#windowcovering)