class Plugin(indigo.PluginBase): ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = self.pluginPrefs.get("showDebugInfo", False) self.debugLog(u"Debugging enabled") def __del__(self): indigo.PluginBase.__del__(self) def startup(self): indigo.server.log(u"Starting LIFX Bridge") self.updater = GitHubPluginUpdater(self) self.updater.checkForUpdate() self.next_update_check = time.time() + float( self.pluginPrefs.get('updateFrequency', 24)) * 60.0 * 60.0 self.refreshDeviceList() # Need to subscribe to device changes here so we can call the refreshDeviceList method # in case there was a change or deletion of a device that's published indigo.devices.subscribeToChanges() try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) except socket.error, msg: self.debugLog('Failed to create socket. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]) return try: self.sock.bind(('', DEFAULT_LIFX_PORT)) self.sock.settimeout(1) except socket.error, msg: self.debugLog('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]) return
def testUpdateCheck(self): indigo.server.log('-- BEGIN testUpdateCheck --') self.updater.checkForUpdate() self.updater.checkForUpdate('0.0.0') emptyUpdater = GitHubPluginUpdater('jheddings', 'indigo-ghpu') emptyUpdater.checkForUpdate() emptyUpdater.checkForUpdate('0.0.0') emptyUpdater.checkForUpdate(str(self.pluginVersion)) indigo.server.log('-- END testUpdateCheck --')
def testUpdateCheck(self): indigo.server.log('-- BEGIN testUpdateCheck --') self.updater.checkForUpdate() self.updater.checkForUpdate('0.0.0') emptyUpdater = GitHubPluginUpdater() emptyUpdater.checkForUpdate() emptyUpdater.checkForUpdate('0.0.0') emptyUpdater.checkForUpdate(str(self.pluginVersion)) indigo.server.log('-- END testUpdateCheck --')
class Plugin(indigo.PluginBase): ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): super(Plugin, self).__init__(pluginId, pluginDisplayName, pluginVersion, pluginPrefs) #indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.errorState = False self.pluginName = u'Six50Joe' # Set plugin preferences self.setUpdatePluginPrefs() ######################################## def __del__(self): indigo.PluginBase.__del__(self) ######################################## def startup(self): self.logger.debug(u"startup called") self.logger.debug(u'Getting plugin updater') self.updater = GitHubPluginUpdater(self) ######################################## def updatePlugin(self): self.logger.info(u'Initiating plugin update') updateResult = self.updater.update() return updateResult ######################################## def checkPluginUpdates(self, notify = False, performUpdate = False): self.logger.debug(u'Checking for plugin updates') updateAvailable = self.updater.checkForUpdate() if updateAvailable: if notify and len(self.checkForUpdatesEmail) > 0: self.logger.debug(u'Notifying that new plugin version is available via e-mail') indigo.server.sendEmailTo(self.checkForUpdatesEmail, subject=u'Indigo %s plugin update available' % (self.pluginName), body=u'A new update of %s plugin is available and can be updated from the plugin menu within Indigo' % (self.pluginName)) if performUpdate: self.updatePlugin() return updateAvailable # Catch changes to config prefs def closedPrefsConfigUi(self, valuesDict, userCancelled): self.extDebug(u'CALL closedPrefsConfigUi, valuesDict: %s' % unicode(valuesDict)) self.extDebug(u'CALL closedPrefsConfigUi, userCancelled: %s' % unicode(userCancelled)) self.setUpdatePluginPrefs(True) # FIX DO VALIDATION self.pluginConfigErrorState = False ######################################## def shutdown(self): self.logger.debug(u"shutdown called") # moved to stopThread in runConcurrentThread, don't believe the below saved prefs at all times # if self.keepStats: # self.logger.debug(u'Saving z-wave node statistics') # self.pluginPrefs[u'nodeStats'] = self.store(self.nodeStats) ######################################## def wakeUp(self): indigo.PluginBase.wakeUp(self) self.logger.debug("wakeUp method called") ######################################## def prepareToSleep(self): indigo.PluginBase.prepareToSleep(self) self.logger.debug("prepareToSleep method called") ######################################## def extDebug(self, msg): if self.extensiveDebug: self.debugLog(msg) ######################################## # If runConcurrentThread() is defined, then a new thread is automatically created # and runConcurrentThread() is called in that thread after startup() has been called. # # runConcurrentThread() should loop forever and only return after self.stopThread # becomes True. If this function returns prematurely then the plugin host process # will log an error and attempt to call runConcurrentThread() again after several seconds. def runConcurrentThread(self): try: counter = 0 while True: self.logger.debug(u'runConcurrentThread') counter += 1 self.sleep(3613) except self.StopThread: self.logger.debug(u'runConcurrentThread self.StopThread') if self.keepStats: self.logger.debug(u'Saving z-wave node statistics before quitting plugin') self.pluginPrefs[u'nodeStats'] = self.store(self.nodeStats) # if self.errorState: # # FIX, find some way to shutdown if there are errors # self.logger.error(u'Plugin in error state, stopping concurrent thread') # pass ##### # Set or update plugin preferences # running : True if plugin is already running and prefs are to be changed # def setUpdatePluginPrefs(self, running = False): self.logger.debug(u'CALL setUpdatePluginPrefs, running: %s' % unicode(running)) # Set log levels if running: setText = u'Changing' else: setText = u'Setting' self.indigo_log_handler.setLevel(self.pluginPrefs.get(u'logLevel', u'INFO')) self.plugin_file_handler.setLevel(u'DEBUG') self.extensiveDebug = self.pluginPrefs.get(u'extensiveDebug', False) self.logger.info(u'%s log level to %s' % (setText, self.pluginPrefs.get(u'logLevel', u'INFO'))) self.logger.debug(u'Extensive debug logging set to %s' % unicode(self.extensiveDebug)) self.logger.debug(u'%s file handler log level to DEBUG' % (setText)) # DEBUG : All debug info # INFO : Informational messages relevant to the user # WARNING : More critical information to the user, warnings # ERROR : Errors not critical for plugin execution # CRITICAL : Errors critical for plugin execution, plugin will stop self.checkForUpdatesInterval = self.pluginPrefs.get(u'checkForUpdatesInterval', 24) if self.checkForUpdatesInterval == u'': self.checkForUpdates = False else: self.checkForUpdates = True try: self.checkForUpdatesInterval = int(self.checkForUpdatesInterval) except: self.logger.error(u'Invalid plugin prefs value for update check frequency, defaulting to 24 hours') self.checkForUpdatesInterval = 24 self.checkForUpdatesEmail = self.pluginPrefs.get(u'checkForUpdatesEmail', '') #self.autoUpdate = self.pluginPrefs.get(u'autoUpdate', False) # FIX, Indigo will ask for confirmation before plugin is updates, so best to leave as manual menu option self.autoUpdate = False self.keepStats = self.pluginPrefs.get(u'keepStats', False) if self.keepStats: self.nodeStats = self.load(self.pluginPrefs.get(u'nodeStats', self.store(dict()))) # see def emptyNodeStatList() for description # Update older versions of nodeStats for l in self.nodeStats: # after adding outNR if len(l) == 9: l = l[0:1] + [0] + l[2:] # Check plugin dependencies self.checkDependantPlugins() ##### # Check for dependant plugins, if they are enabled and correct version is installed etc. # def checkDependantPlugins(self): self.logger.debug(u'Checking status of other plugins this plugin depends on') # [<plugin pref for enable of plugin>, <plugin id>, <Friendly name>, <min plugin version>] pluginList = [] for plug in pluginList: usePlugin = self.pluginPrefs.get(plug[0], False) self.logger.debug(u'Checking plugin "%s"' % plug[2]) try: if not usePlugin: raise try: indigoPlug = indigo.server.getPlugin(plug[1]) if not indigoPlug.isRunning(): raise ImportError(u'Plugin "%s" is not running, please install/enable, and re-enable in %s plugin preferences' % (plug[2], self.pluginName)) if len(indigoPlug.pluginVersion) == 0 or indigoPlug.pluginVersion < plug[3]: raise ImportError(u'Plugin "%s" version %s is less than %s requires (%s), please update the plugin.\nUse of this plugin in %s has been disabled' % (plug[2], indigoPlug.pluginVersion, self.pluginName, plug[3], self.pluginName)) # Plugin specific checks: if plug[1] == u'com.flyingdiver.indigoplugin.betteremail': smtpDevId = self.pluginPrefs.get(u'plugin-betteremail-smtpdevice', u'') if len(smtpDevId) == 0: raise ImportError(u'You need to specify a valid "%s" SMTP device in %s plugin preferences' % plug[2], self.pluginName) else: try: smtpDev = indigo.devices[int(smtpDevId)] if not smtpDev.enabled: raise except: raise ImportError(u'The specified "%s" SMTP device could not be loaded, please check\n%s plugin preferences and that the SMTP device is enabled' % (self.pluginName)) except ImportError as e: self.logger.error(e) raise except: self.logger.error(u'Could not load plugin "%s". Please install and re-enable in %s plugin preferences' % (plug[2], self.pluginName)) raise except: # Common code for disabling use of the plugin if usePlugin: # configured to use plugin, so some error happened self.logger.debug(u'Disabling use of plugin "%s" in plugin prefs' % plug[2]) if len(indigoPlug.pluginSupportURL) > 0: self.logger.info(u'"%s" support/install link: %s' % (plug[2], indigoPlug.pluginSupportURL)) else: self.logger.debug(u'Not configured to use plugin "%s"' % plug[2]) self.pluginPrefs[plug[0]] = False if plug[1] in self.dependantPlugins: del self.dependantPlugins[plug[1]] else: # Plugin is successfully initialized, add to dict of enabled plugins self.logger.debug(u'Plugin "%s" successfully checked' % plug[2]) self.dependantPlugins[plug[1]] = indigoPlug def ping(self, ipOrUrl): rc = subprocess.call("/sbin/ping -t 1 -c 1 %s" \ % (ipOrUrl), shell=True, stdout=subprocess.PIPE) if rc == 0: return True else: return False ######################################## def getPresenceDevices(self, filter="", valuesDict=None, typeId="", targetId=0): list = [] for v in indigo.variables: if v.name.startswith("S50_PRESDT") \ and not v.name.endswith("_reached"): list.append({'name' : v.name, 'value' : v.value}) return list ######################################## def getPresenceDeviceList(self, filter="", valuesDict=None, typeId="", targetId=0): devList = self.getPresenceDevices() list = [] for dev in devList: list.append([dev['name'], dev['value']]) return list ######################################## # UI List generators and callbackmethods ######################################## ######################################## # Actions ######################################## def doPingAddressAtPort(self, ipOrUrl, port, numRetries, retrySecs, var=None): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(retrySecs) for r in range(1, numRetries + 1): try: s.connect((ipOrUrl, port)) s.shutdown(2) self.logger.debug("%s is reachable at port %d" % (ipOrUrl, port)) if var: indigo.variable.updateValue(var, value=unicode("true")) return True except Exception as e: self.logger.debug(str(e)) self.logger.debug("%s is NOT reachable at port %d, retry %d" % (ipOrUrl, port, r)) time.sleep(retrySecs) if var: indigo.variable.updateValue(var, value=unicode("false")) return False def pingAddress(self, action, port=None): props = action.props varName = props[u'resultVarName'] var = None if len(varName) > 0: var = indigo.variables[varName] numRetries = int(props[u'numRetries']) retrySecs = int(props[u'retrySecs']) ipOrUrl = props[u'ipOrUrl'] if not port: port = int(props[u'port']) else: port = int(port) self.doPingAddressAtPort(ipOrUrl, port, numRetries, retrySecs, var) def pingOtherHouse(self, action): self.pingAddress(action, 8176) def checkDevicePresence(self, action): devList = self.getPresenceDevices() deviceReached = False for d in devList: reachable = self.ping(d['value']) if not deviceReached and reachable: deviceReached = True self.logger.debug("dev: %s: %s" % (d['name'], reachable)) varName = d['name'] + "_reached" var = None if varName not in indigo.variables: var = indigo.variable.create(varName, unicode(reachable)) else: var = indigo.variable.updateValue(varName, unicode(reachable)) presenceVarName = 'DevicesPresent' if presenceVarName not in indigo.variables: var = indigo.variable.create(presenceVarName, unicode(deviceReached)) else: var = indigo.variable.updateValue(presenceVarName, unicode(deviceReached)) def iterate(self, iterable): iterator = iter(iterable) item = iterator.next() for next_item in iterator: yield item, next_item item = next_item yield item, None def readPropaneThresholds(self): global PropaneThresholds PropaneThresholds = {} path = CONFIG_FILE_DIR + "/" + RELAY_THRESHOLDS_FILENAME if os.path.exists(path): inputFile = open(path, 'r') for line in inputFile: (pct, thresh) = line.split(',') PropaneThresholds[pct] = thresh.strip() #for key in sorted(PropaneThresholds.iterkeys()): # self.logger.debug("%s: %s" % (key, PropaneThresholds[key])) lastVal = 0 for item, next_item in self.iterate(sorted(PropaneThresholds.iterkeys(), key=int)): if next_item is not None and \ PropaneThresholds[item] > PropaneThresholds[next_item]: self.logger.warn("WARNING: propane thresholds out of order: %s(%d) < %s(%d)" \ % (item, int(PropaneThresholds[item]), next_item, int(PropaneThresholds[next_item]))) lastVal = PropaneThresholds[item] self.logger.debug("%s - next %s" % (str(item), str(next_item))) def writePropaneThresholds(self): path = CONFIG_FILE_DIR + "/" + RELAY_THRESHOLDS_FILENAME if os.path.exists(path): backup = path + "_" + datetime.datetime.today().strftime('%Y-%m-%d_%H:%M:%S') os.rename(path, backup) outFile = open(path, 'w') for key in sorted(PropaneThresholds.iterkeys(), key=int): outFile.write("%s,%s\n" % (key, PropaneThresholds[key])) def getPropaneSensorReading(self): relay = indigo.devices["Propane - Relay Output"] analog = indigo.devices["Propane - Analog Input"] indigo.device.turnOn(relay, duration=100) indigo.activePlugin.sleep(1) indigo.device.statusRequest(analog) self.logger.info("sensor value is: " + str(analog.sensorValue)) return analog.sensorValue def getPropaneLevel(self, action): props = action.props testValStr = props[u'testSensorVal'] testVal = None if testValStr.isdigit(): testVal = int(float(testValStr)) self.readPropaneThresholds() sensor = None if testVal is None: sensor = self.getPropaneSensorReading() else: sensor = testVal propaneVar = indigo.variables["PropaneLevel"] prevPct = int(float(propaneVar.value)) retry = 0 pct=0 nextPct=0 calcPct=0 level = "" while retry < 3: firstThreshold=True for pct, nextPct in self.iterate(sorted(PropaneThresholds.iterkeys(), key=int)): thresh = int(float(PropaneThresholds[pct])) nextThresh = -1 if nextPct: nextThresh = int(float(PropaneThresholds[nextPct])) self.logger.debug("%s(%d) - next %s(%d)" % (str(pct), thresh, str(nextPct), nextThresh)) # for t, threshold in enumerate(thresholds): calcPct = None if thresh <= sensor: if nextPct is None: # This reading is above the high level = "> %s%%" % (pct) calcPct = pct break if sensor <= nextThresh: # The reading is between two thresholds rangeBottom = thresh rangeTop = nextThresh range = (rangeTop - rangeBottom) span = int(nextPct) - int(pct) increment = 1 if range > span: increment = float(range) / float(span) calcPct = int(pct) + ((sensor - rangeBottom) / increment) self.logger.debug("range: %d span: %d increment: %f calcPct: %d" \ % (range, span, increment, calcPct)) level = "%d%%" % (calcPct) break else: if firstThreshold: # Reading is below the lowest level = "< %s%%" % (pct) calcPct = pct break firstThreshold = False delta = prevPct - int(pct) if delta <= 10: break # Might have been a bad reading; retry time.sleep(2) retry += 1 indigo.server.log("possible bad value, retrying: " + str(pct)) indigo.variable.updateValue(propaneVar, str(calcPct)) propaneStrVar = indigo.variables["PropaneLevelStr"] indigo.variable.updateValue(propaneStrVar, level) sensorVar = indigo.variables["PropaneGaugeReading"] indigo.variable.updateValue(sensorVar, str(sensor)) self.logger.info("Propane level is %s" % level) def calibratePropaneLevel(self, action): props = action.props gaugePctInput = props[u'gaugePct'] self.readPropaneThresholds() testValStr = props[u'testSensorVal'] testVal = None if testValStr.isdigit(): testVal = int(testValStr) gaugePct = None if gaugePctInput.isdigit(): gaugePct = int(props[u'gaugePct']) else: var = indigo.variables['PropaneCalibrationPct'] gaugePct = int(var.value) self.logger.info("Propane calibratrion pct input is: %d" % gaugePct) sensor = None if testVal is None: # Get multiple readings, in case 1st one is bad for x in range(1, 3): sensor = self.getPropaneSensorReading() time.sleep(3) else: sensor = testVal # Insert the new reading into the list, replacing the # cuyrrent pct reading if it already exists PropaneThresholds[str(gaugePct)] = sensor # Now, iterate through the list and remove entries that # cross over the new threshold out of order toRemove = [] firstThreshold = True for pct, nextPct in self.iterate(sorted(PropaneThresholds.iterkeys(), key=int)): thresh = int(float(PropaneThresholds[pct])) if int(pct) < gaugePct and thresh > sensor: toRemove.append(pct) self.logger.info("Existing threshold %d for %d%% is higher than new threshold %d, at %d%%, removing existing" \ % (thresh, int(pct), sensor, gaugePct)) if int(pct) > gaugePct and thresh < sensor: toRemove.append(pct) self.logger.info("Existing threshold %d for %d%% is lower than new threshold %d, at %d%%, removing existing" \ % (thresh, int(pct), sensor, gaugePct)) for pct in toRemove: del PropaneThresholds[pct] self.logger.info("After calibration:") for item, next_item in self.iterate(sorted(PropaneThresholds.iterkeys(), key=int)): prefix = "" if int(item) == gaugePct: prefix = "NEW--> " self.logger.info("PRP: %s %s(%d)" % (prefix, item, int(float(PropaneThresholds[item])))) self.writePropaneThresholds() def archivePriorMonthLogs(self, action=None): var = indigo.variables['LogArchiveDir'] if not var: self.logger.error("LogArchiveDir variable not set") return archiveDir = var.value var = indigo.variables['LogDir'] if not var: self.logger.error("LogDir variable not set") return logDir = var.value dtNow = datetime.datetime.now() dtLastMonth = dtNow + dateutil.relativedelta.relativedelta(months=-1) # dtMatch = "%4d-%02d.*Events.txt" % (dtLastMonth.year, dtLastMonth.day) # files = [f for f in os.listdir(logDir) if re.match(dtMatch, f)] # for f in files: # self.logger.debug(f) yearDir = "%s/%4d" % (archiveDir, dtLastMonth.year) self.logger.debug("YearDir=%s" % yearDir) if (not os.path.isdir(yearDir)): os.mkdir(yearDir) tarPath = "%s/%s.tar" % (yearDir, dtLastMonth.strftime("%Y-%b")) tarPathGz = "%s.gz" % tarPath if (os.path.exists(tarPathGz)): self.logger.info("Last month archive already created; no action taken (%s)" % tarPathGz) return else: self.logger.info("Creating last month archive: %s" % tarPath) tarPath = tarPath.replace(" ","\ ") logDir = logDir.replace(" ","\ ") tarcmd = "cd %s;tar -cvf %s %4d-%02d*" \ % (logDir, tarPath, dtLastMonth.year, dtLastMonth.month) self.logger.debug(tarcmd) rc = subprocess.call(tarcmd, shell=True, stdout=subprocess.PIPE) gzcmd = "gzip %s" % tarPath rc = subprocess.call(gzcmd, shell=True, stdout=subprocess.PIPE) def redrawCharts(self, param1=None): matplotlibPlugin = indigo.server.getPlugin("com.fogbert.indigoplugin.matplotlib") try: result = matplotlibPlugin.executeAction('refreshTheChartsAction') if result is not None: indigo.server.log(result['message']) except Exception as err: indigo.server.log(u"Exception occurred: {0}".format(err)) def mailAttachment(self, title, volume, path): path = path.replace("/", ":") if not path.startswith(":"): path = ":" + path if not path.endswith(":"): path = path + ":" path = volume + path applescript=''' tell application "Finder" set folderPath to folder "%s" set theFile to file "indigo_logs.tar" in folderPath as alias set fileName to name of theFile end tell set theSubject to fileName set theBody to "Indigo logs collection: " & fileName set theAddress to "*****@*****.**" set theAttachment to theFile set theSender to "*****@*****.**" tell application "Mail" set theNewMessage to make new outgoing message with properties {subject:theSubject, content:theBody, visible:false} tell theNewMessage set visibile to true set sender to theSender make new to recipient at end of to recipients with properties {address:theAddress} try make new attachment with properties {file name:theAttachment} at after the last word of the last paragraph set message_attachment to 0 on error errmess -- oops log errmess -- log the error set message_attachment to 1 end try delay 15 send end tell end tell ''' % path args = [item for x in [("-e",l.strip()) for l in applescript.split('\n') if l.strip() != ''] for item in x] self.logger.debug(args) proc = subprocess.Popen(["osascript"] + args ,stdout=subprocess.PIPE ) out = proc.stdout.read().strip() self.logger.debug(str(out)) return out def mailRecentLogs(self, action=None): var = indigo.variables['LogDir'] if not var: self.logger.error("LogDir variable not set") return logDir = var.value var = indigo.variables['LogArchiveDir'] if not var: self.logger.error("LogArchiveDir variable not set") return archiveDir = var.value dtNow = datetime.datetime.now() priorDate = dtNow + dateutil.relativedelta.relativedelta(days=-14) self.logger.debug("Prior date=%s" % (priorDate)) files = [f for f in os.listdir(logDir) if re.match(".*Events.txt", f)] arcName = "indigo_logs.tar" #archiveDir = archiveDir.replace(" ","\ ") # cmd = "tar -cvf %s/%s" % (archiveDir, arcName) # self.logger.debug(cmd) # rc = subprocess.call(cmd, # shell=True, # stdout=subprocess.PIPE) # if rc != 0: # self.logger.error("Couldn't create log archive file") if (os.path.exists("%s/%s" % (archiveDir, arcName))): os.remove("%s/%s" % (archiveDir, arcName)) for f in files: m = re.match("(\d\d\d\d)-(\d\d)-(\d\d).*", f) if m: year = int(m.group(1)) month = int(m.group(2)) day = int(m.group(3)) fdt = datetime.datetime(year=year, month=month, day=day) self.logger.debug("Year=%d month=%d day=%d" % (year, month, day)) if fdt > priorDate: cmd = "cd \"%s\";tar -rvf \"%s/%s\" \"%s\"" % (logDir, archiveDir, arcName, f) # self.logger.debug(cmd) rc = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE) if rc != 0: self.logger.error("Couldn't update log archive file") self.mailAttachment("Recent Indigo Logs", "Macintosh HD", archiveDir) def updateElecUsageVar(self, var, value, thresholds, desc): "Setl the status string according to the usage threshold" if (value < thresholds[0]): indigo.variable.updateValue(var, desc[0]) elif (value < thresholds[1]): indigo.variable.updateValue(var, desc[1]) elif (value < thresholds[2]): indigo.variable.updateValue(var, desc[2]) else: indigo.variable.updateValue(var, desc[3]) return def checkElectric(self, action): "Get current electric usage values and update status" props = action.props clamp1Dev = props[u'clamp1Dev'] clamp2Dev = props[u'clamp2Dev'] thresh1 = int(props[u'thresh1']) thresh2 = int(props[u'thresh2']) thresh3 = int(props[u'thresh3']) thresh4 = int(props[u'thresh4']) thresholds = [thresh1, thresh2, thresh3, thresh4] descriptions = ["minimal", "normal", "High", "VERY HIGH"] clamp1 = indigo.devices[clamp1Dev] clamp2 = indigo.devices[clamp2Dev] c1Val = clamp1.displayStateValRaw c2Val = clamp2.displayStateValRaw c1Var = indigo.variables["Clamp1"] c2Var = indigo.variables["Clamp2"] indigo.variable.updateValue(c1Var, str(c1Val)) indigo.variable.updateValue(c2Var, str(c2Val)) e1Var = indigo.variables["ElecUsage1Status"] e2Var = indigo.variables["ElecUsage2Status"] self.logger.debug("Setting clamp1 var to %s, clamp2 to %s" % (c1Val, c2Val)) self.updateElecUsageVar(e1Var, c1Val, thresholds, descriptions) self.updateElecUsageVar(e2Var, c2Val, thresholds, descriptions) # indigo.server.log(str(e1Var)) def hasHeartbeat(self, deviceName, withinSecs=180, withinMinutes=0, withinHours=0, withinDays=0): device = indigo.devices[deviceName] now = datetime.datetime.now() lastChanged = device.lastChanged if False: self.logger.debug("Device heartbeat at: %s, seconds since=%d" % \ (lastChanged.strftime("%m/%d/%Y %H:%M:%S"), \ (lastChanged - now).total_seconds())) isResponding = True delta = now - lastChanged threshold = withinSecs + \ (withinMinutes * 60) + \ (withinHours * 60 * 60) + \ (withinDays * 24 * 60 * 60) self.logger.debug("Threshold seconds=%d)" % threshold) if delta.total_seconds() > threshold: isResponding = False statusVarName = deviceName + "_responding" # Variable names can't have spaces statusVarName = statusVarName.replace(" ", "_") statusVarName = statusVarName.replace("/", "-") statusVar = None # Create folders or variables if they have been somehow deleted. if 'DeviceUpdate' not in indigo.variables.folders: indigo.variables.folder.create('DeviceUpdate') if statusVarName not in indigo.variables: indigo.variable.create(statusVarName, '', folder='DeviceUpdate') statusVar = indigo.variables[statusVarName] indigo.variable.updateValue(statusVar, str(isResponding)) def checkDeviceHeartbeat(self, action): "See if the device has responded to Indigo recently" props = action.props deviceName = props[u'deviceName'] withinSecs = int(props[u'withinSecs']) withinMinutes = int(props[u'withinMinutes']) withinHours = int(props[u'withinHours']) withinDays = int(props[u'withinDays']) self.hasHeartbeat(deviceName, withinSecs, withinMinutes, withinHours, withinDays) def flowMeterUpdate(self, action): "Update the latest flow meter readings" props = action.props deviceName = props[u'deviceName'] # test to see if flow stats folder exists by Name if not (FLW_FOLDER_NAME in indigo.variables.folders): indigo.server.log("folder named '%s' exists" % FLW_FOLDER_NAME) newFolder = indigo.variables.folder.create(FLW_FOLDER_NAME) if FLW_NAM_LAST_READING not in indigo.variables: indigo.variable.create(FLW_NAM_LAST_READING, '0', folder=FLW_FOLDER_NAME) readingVar = indigo.variables[FLW_NAM_LAST_READING] lastValue = float(readingVar.value) device = indigo.devices[deviceName] sensorValue = device.sensorValue indigo.variable.updateValue(readingVar, str(sensorValue)) currentUsage = sensorValue - lastValue if FLW_NAM_CURRENT_USAGE not in indigo.variables: indigo.variable.create(FLW_NAM_CURRENT_USAGE, '0', folder=FLW_FOLDER_NAME) currentUsageVar = indigo.variables[FLW_NAM_CURRENT_USAGE] indigo.variable.updateValue(currentUsageVar, str(currentUsage)) now = datetime.datetime.now() nowStr = now.strftime("%m/%d/%Y %H:%M:%S") if FLW_NAM_UPDATED_AT not in indigo.variables: indigo.variable.create(FLW_NAM_UPDATED_AT, '0', folder=FLW_FOLDER_NAME) updatedAtVar = indigo.variables[FLW_NAM_UPDATED_AT] indigo.variable.updateValue(updatedAtVar, nowStr)
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater(self) self.apiVersion = "2.0" self.localAddress = "" # create empty device list self.deviceList = {} def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(u"Started device: " + device.name) device.stateListOrDisplayStateIdChanged() self.addDeviceToList (device) def deviceStopComm(self,device): if device.id in self.deviceList: self.debugLog("Stoping device: " + device.name) self.deleteDeviceFromList(device) def deviceCreated(self, device): self.debugLog(u'Created device "' + device.name) pass def addDeviceToList(self,device): if device: if device.id not in self.deviceList: propsAddress = device.pluginProps["address"] propsAddress = propsAddress.strip() propsAddress = propsAddress.replace (' ','') pingNextTime = datetime.datetime.now() - datetime.timedelta(seconds=10) pingInterval = device.pluginProps["pingInterval"] self.deviceList[device.id] = {'ref':device, 'address':propsAddress, 'pingInterval':pingInterval, 'pingNextTime': pingNextTime} def deleteDeviceFromList(self, device): if device: if device.id in self.deviceList: del self.deviceList[device.id] def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.updater.checkForUpdate() def shutdown(self): self.debugLog(u"shutdown called") def getDeviceConfigUiValues(self, pluginProps, typeId, devId): valuesDict = pluginProps errorMsgDict = indigo.Dict() if "pingInterval" not in valuesDict: valuesDict["pingInterval"] = 300 return (valuesDict, errorMsgDict) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") self.debugLog(u"validating IP Address") ipAdr = valuesDict[u'address'] if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress (ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) pingInterval = valuesDict[u'pingInterval'] try: iInterval = int (pingInterval) if iInterval < 1: errorMsgDict = indigo.Dict() errorMsgDict[u'pingInterval'] = u"This needs to be > 0." return False except Exception, e: errorMsgDict = indigo.Dict() errorMsgDict[u'pingInterval'] = u"This needs to be a valid number." return False return (True, valuesDict)
class Plugin(indigo.PluginBase): ######################################## # Main Plugin methods ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = self.pluginPrefs.get(u"showDebugInfo", False) self.debugLog(u"Debugging enabled") def __del__(self): indigo.PluginBase.__del__(self) def startup(self): indigo.server.log(u"Starting Harmony Hub") self.updater = GitHubPluginUpdater(self) self.updater.checkForUpdate() self.updateFrequency = self.pluginPrefs.get('updateFrequency', 24) if self.updateFrequency > 0: self.next_update_check = time.time() + float(self.updateFrequency) * 60.0 * 60.0 self.hubDict = dict() self.triggers = { } def shutdown(self): indigo.server.log(u"Shutting down Harmony Hub") def runConcurrentThread(self): try: while True: # All hub messages are done in callbacks. No polling. # Plugin Update check if self.updateFrequency > 0: if time.time() > self.next_update_check: self.updater.checkForUpdate() self.next_update_check = time.time() + float(self.pluginPrefs['updateFrequency']) * 60.0 * 60.0 self.sleep(1.0) except self.stopThread: pass #################### # def getDeviceConfigUiValues(self, pluginProps, typeId, devId): # valuesDict = indigo.Dict(pluginProps) # errorsDict = indigo.Dict() # return (valuesDict, errorsDict) #################### def triggerStartProcessing(self, trigger): self.debugLog("Adding Trigger %s (%d) - %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) assert trigger.id not in self.triggers self.triggers[trigger.id] = trigger def triggerStopProcessing(self, trigger): self.debugLog("Removing Trigger %s (%d)" % (trigger.name, trigger.id)) assert trigger.id in self.triggers del self.triggers[trigger.id] def triggerCheck(self, device, eventType): # Execute the trigger if it's the right type and for the right hub device for triggerId, trigger in sorted(self.triggers.iteritems()): self.debugLog("\tChecking Trigger %s (%s), Type: %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) if trigger.pluginProps["hubID"] != str(device.id): self.debugLog("\t\tSkipping Trigger %s (%s), wrong hub: %s" % (trigger.name, trigger.id, device.id)) else: if trigger.pluginTypeId != eventType: self.debugLog("\t\tSkipping Trigger %s (%s), wrong type: %s" % (trigger.name, trigger.id, eventType)) else: self.debugLog("\t\tExecuting Trigger %s (%s) on Device %s (%s)" % (trigger.name, trigger.id, device.name ,device.id)) indigo.trigger.execute(trigger) #################### def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"validatePrefsConfigUi called") errorMsgDict = indigo.Dict() try: poll = int(valuesDict['updateFrequency']) if (poll < 0) or (poll > 24): raise except: errorMsgDict['updateFrequency'] = u"Update frequency is invalid - enter a valid number (between 0 and 24)" if len(errorMsgDict) > 0: return (False, valuesDict, errorMsgDict) return (True, valuesDict) ######################################## def closedPrefsConfigUi(self, valuesDict, userCancelled): if not userCancelled: self.debug = valuesDict.get("showDebugInfo", False) if self.debug: self.debugLog(u"Debug logging enabled") else: self.debugLog(u"Debug logging disabled") ######################################## # Called for each enabled Device belonging to plugin # Verify connectivity to servers and start polling IMAP/POP servers here # def deviceStartComm(self, device): self.debugLog(u'Called deviceStartComm(self, device): %s (%s)' % (device.name, device.id)) instanceVers = int(device.pluginProps.get('devVersCount', 0)) self.debugLog(device.name + u": Device Current Version = " + str(instanceVers)) if instanceVers >= kCurDevVersCount: self.debugLog(device.name + u": Device Version is up to date") elif instanceVers < kCurDevVersCount: newProps = device.pluginProps else: self.errorLog(u"Unknown device version: " + str(instanceVers) + " for device " + device.name) if device.id not in self.hubDict: if device.deviceTypeId == "harmonyHub": self.debugLog(u"%s: Starting harmonyHub device (%s)" % (device.name, device.id)) hubClient = HubClient(self, device) if (hubClient.ready): self.hubDict[device.id] = hubClient else: self.debugLog(u"%s: Error starting harmonyHub device (%s), disabling..." % (device.name, device.id)) indigo.device.enable(device, value=False) indigo.device.updateStateOnServer(key="serverStatus", value="Disabled") indigo.device.updateStateImageOnServer(indigo.kStateImageSel.SensorOff) else: self.errorLog(u"Unknown server device type: " + device.deviceTypeId) else: self.debugLog(device.name + u": Duplicate Device ID" ) ######################################## # Terminate communication with servers # def deviceStopComm(self, device): self.debugLog(u'Called deviceStopComm(self, device): %s (%s)' % (device.name, device.id)) try: hub = self.hubDict[device.id] hub.client.disconnect(send_close=True) self.hubDict.pop(device.id, None) except: pass ######################################## def validateDeviceConfigUi(self, valuesDict, typeId, devId): errorsDict = indigo.Dict() if len(errorsDict) > 0: return (False, valuesDict, errorsDict) return (True, valuesDict) ######################################## def validateActionConfigUi(self, valuesDict, typeId, devId): errorsDict = indigo.Dict() try: pass except: pass if len(errorsDict) > 0: return (False, valuesDict, errorsDict) return (True, valuesDict) ######################################## # Plugin Actions object callbacks def startActivity(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] hub = self.hubDict[hubDevice.id] activityID = pluginAction.props["activity"] activityLabel = hub.activityList[activityID]["label"] self.debugLog(hubDevice.name + u": Start Activity - " + activityLabel) try: hub.client.start_activity(int(activityID)) except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.startActivity") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.startActivity") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.startActivity: " + str(e)) else: hub.current_activity_id = activityID # hubDevice.updateStateOnServer(key="currentActivityNum", value=activityID) # hubDevice.updateStateOnServer(key="currentActivityName", value=activityLabel) # self.triggerCheck(hubDevice, "activityNotification") def powerOff(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] hub = self.hubDict[hubDevice.id] self.debugLog(hubDevice.name + u": Power Off") try: hub.client.start_activity(-1) except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.startActivity") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.startActivity") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.startActivity: " + str(e)) else: hub.current_activity_id = "-1" # hubDevice.updateStateOnServer(key="currentActivityNum", value="-1") # hubDevice.updateStateOnServer(key="currentActivityName", value="PowerOff") # self.triggerCheck(hubDevice, "activityNotification") def volumeMute(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] if not hubDevice.enabled: self.debugLog(hubDevice.name + u": Can't send Volume commands when hub is not enabled") return hub = self.hubDict[hubDevice.id] if (int(hub.current_activity_id) <= 0): self.debugLog(hubDevice.name + u": Can't send Volume commands when no Activity is running") return soundDev = hub.activityList[hub.current_activity_id]["soundDev"] self.debugLog(hubDevice.name + u": sending Mute to " + soundDev) try: hub.client.send_command(soundDev, "Mute") except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.send_command") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.send_command") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.send_command: " + str(e)) def volumeDown(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] if not hubDevice.enabled: self.debugLog(hubDevice.name + u": Can't send Volume commands when hub is not enabled") return hub = self.hubDict[hubDevice.id] if (int(hub.current_activity_id) <= 0): self.debugLog(hubDevice.name + u": Can't send Volume commands when no Activity is running") return soundDev = hub.activityList[hub.current_activity_id]["soundDev"] self.debugLog(hubDevice.name + u": sending VolumeDown to " + soundDev) try: hub.client.send_command(soundDev, "VolumeDown") except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.send_command") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.send_command") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.send_command: " + str(e)) def volumeUp(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] if not hubDevice.enabled: self.debugLog(hubDevice.name + u": Can't send Volume commands when hub is not enabled") return hub = self.hubDict[hubDevice.id] if (int(hub.current_activity_id) <= 0): self.debugLog(hubDevice.name + u": Can't send Volume commands when no Activity is running") return soundDev = hub.activityList[hub.current_activity_id]["soundDev"] self.debugLog(hubDevice.name + u": sending VolumeUp to " + soundDev) try: hub.client.send_command(soundDev, "VolumeUp") except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.send_command") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.send_command") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.send_command: " + str(e)) def sendActivityCommand(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] if not hubDevice.enabled: self.debugLog(hubDevice.name + u": Can't send Volume commands when hub is not enabled") return hub = self.hubDict[hubDevice.id] if (int(hub.current_activity_id) <= 0): self.debugLog(hubDevice.name + u": Can't send Activity commands when no Activity is running") return command = pluginAction.props["command"] activity = pluginAction.props["activity"] device = pluginAction.props["device"] self.debugLog(hubDevice.name + u": sendActivityCommand: " + command + " to " + device + " for " + activity) try: hub.client.send_command(device, command) except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.send_command") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.send_command") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.send_command: " + str(e)) def sendDeviceCommand(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] if not hubDevice.enabled: self.debugLog(hubDevice.name + u": Can't send Volume commands when hub is not enabled") return hub = self.hubDict[hubDevice.id] command = pluginAction.props["command"] device = pluginAction.props["device"] self.debugLog(hubDevice.name + u": sendDeviceCommand: " + command + " to " + device) try: hub.client.send_command(device, command) except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.send_command") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.send_command") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.send_command: " + str(e)) def sendCommand(self, pluginAction): hubDevice = indigo.devices[pluginAction.deviceId] if not hubDevice.enabled: self.debugLog(hubDevice.name + u": Can't send Volume commands when hub is not enabled") return hub = self.hubDict[hubDevice.id] command = pluginAction.props["command"] device = pluginAction.props["device"] self.debugLog(hubDevice.name + u": sendCommand: " + command + " to " + device) try: hub.client.send_command(device, command) except sleekxmpp.exceptions.IqTimeout: self.debugLog(hubDevice.name + u": Time out in hub.client.send_command") except sleekxmpp.exceptions.IqError: self.debugLog(hubDevice.name + u": IqError in hub.client.send_command") except Exception as e: self.debugLog(hubDevice.name + u": Error in hub.client.send_command: " + str(e)) ######################################## # Menu Methods ######################################## def syncHub(self, valuesDict, typeId): self.debugLog(u"Syncing Hub") hubID = int(valuesDict['hubID']) client = self.hubDict[hubID].client client.sync() return (True, valuesDict) def dumpConfig(self, valuesDict, typeId): hubID = int(valuesDict['hubID']) config = self.hubDict[hubID].config self.debugLog(json.dumps(config, sort_keys=True, indent=4, separators=(',', ': '))) return (True, valuesDict) def parseConfig(self, valuesDict, typeId): hubID = int(valuesDict['hubID']) config = self.hubDict[hubID].config for activity in config["activity"]: if activity["id"] == "-1": # skip Power Off continue self.debugLog(u"Activity: %s, id: %s, order: %i, type: %s, isAVActivity: %s, isTuningDefault: %s" % (activity['label'], activity['id'], activity['activityOrder'], activity['type'], str(activity['isAVActivity']), str(activity['isTuningDefault']))) for group in activity["controlGroup"]: self.debugLog(u"\tControl Group %s:" % group['name']) for function in group['function']: self.debugLog(u"\t\tFunction %s, label: %s, action %s:" % (function['name'], function['label'], function['action'])) for device in config["device"]: self.debugLog(u"Device: %s, id: %s, type: %s, Manufacturer: %s, Model: %s" % (device['label'], device['id'], device['type'], device['manufacturer'], device['model'])) for group in device["controlGroup"]: self.debugLog(u"\tControl Group %s:" % group['name']) for function in group['function']: self.debugLog(u"\t\tFunction %s, label: %s, action %s:" % (function['name'], function['label'], function['action'])) return (True, valuesDict) def checkForUpdates(self): self.updater.checkForUpdate() def updatePlugin(self): self.updater.update() def forceUpdate(self): self.updater.update(currentVersion='0.0.0') def toggleDebugging(self): if self.debug: self.debugLog(u"Turning off debug logging") self.pluginPrefs["showDebugInfo"] = False else: self.debugLog(u"Turning on debug logging") self.pluginPrefs["showDebugInfo"] = True self.debug = not self.debug ######################################## # ConfigUI methods ######################################## def activityListGenerator(self, filter, valuesDict, typeId, targetId): retList = [] for id,info in self.hubDict[targetId].activityList.iteritems(): if id != -1: retList.append((id, info["label"])) retList.sort(key=lambda tup: tup[1]) return retList def deviceListGenerator(self, filter, valuesDict, typeId, targetId): retList = [] config = self.hubDict[targetId].config for device in config["device"]: retList.append((device['id'], device["label"])) retList.sort(key=lambda tup: tup[1]) return retList def commandGroupListGenerator(self, filter, valuesDict, typeId, targetId): retList = [] if not valuesDict: return retList config = self.hubDict[targetId].config if typeId == "sendActivityCommand": for activity in config["activity"]: if activity["id"] != valuesDict['activity']: continue self.debugLog(u"commandGroupListGenerator Activity: %s, id: %s" % (activity['label'], activity['id'])) for group in activity["controlGroup"]: retList.append((group['name'], group["name"])) elif typeId == "sendDeviceCommand": for device in config["device"]: if device["id"] != valuesDict['device']: continue self.debugLog(u"commandGroupListGenerator Device: %s, id: %s" % (device['label'], device['id'])) for group in device["controlGroup"]: retList.append((group['name'], group["name"])) else: self.debugLog(u"commandGroupListGenerator Error: Unknown typeId (%s)" % typeId) retList.sort(key=lambda tup: tup[1]) return retList def commandListGenerator(self, filter, valuesDict, typeId, targetId): retList = [] if not valuesDict: return retList config = self.hubDict[targetId].config if typeId == "sendActivityCommand": for activity in config["activity"]: if activity["id"] != valuesDict['activity']: continue self.debugLog(u"commandListGenerator Activity: %s, id: %s" % (activity['label'], activity['id'])) for group in activity["controlGroup"]: if group["name"] != valuesDict['group']: continue for function in group['function']: retList.append((function['name'], function["label"])) elif typeId == "sendDeviceCommand": for device in config["device"]: if device["id"] != valuesDict['device']: continue self.debugLog(u"commandListGenerator Device: %s, id: %s" % (device['label'], device['id'])) for group in device["controlGroup"]: if group["name"] != valuesDict['group']: continue for function in group['function']: retList.append((function['name'], function["label"])) else: self.debugLog(u"commandGroupListGenerator Error: Unknown typeId (%s)" % typeId) retList.sort(key=lambda tup: tup[1]) return retList # doesn't do anything, just needed to force other menus to dynamically refresh def menuChanged(self, valuesDict, typeId, devId): return valuesDict def validateActionConfigUi(self, valuesDict, typeId, actionId): errorDict = indigo.Dict() if typeId == "startActivity": self.debugLog(u"validateActionConfigUi startActivity") elif typeId == "sendCommand": self.debugLog(u"validateActionConfigUi sendCommand") if valuesDict['device'] == "": errorDict["device"] = "Device must be entered" if valuesDict['command'] == "": errorDict["command"] = "Command must be entered" elif typeId == "sendActivityCommand": self.debugLog(u"validateActionConfigUi sendActivityCommand") config = self.hubDict[actionId].config for activity in config["activity"]: if activity["id"] != valuesDict['activity']: continue for group in activity["controlGroup"]: if group["name"] != valuesDict['group']: continue for function in group['function']: if function['name'] != valuesDict['command']: continue action = json.loads(function["action"]) valuesDict['device'] = action["deviceId"] if valuesDict['activity'] == "": errorDict["activity"] = "Activity must be selected" if valuesDict['group'] == "": errorDict["group"] = "Command Group must be selected" if valuesDict['command'] == "": errorDict["command"] = "Command must be selected" elif typeId == "sendDeviceCommand": self.debugLog(u"validateActionConfigUi sendDeviceCommand") if valuesDict['device'] == "": errorDict["device"] = "Device must be selected" if valuesDict['group'] == "": errorDict["group"] = "Command Group must be selected" if valuesDict['command'] == "": errorDict["command"] = "Command must be selected" else: self.debugLog(u"validateActionConfigUi Error: Unknown typeId (%s)" % typeId) if len(errorDict) > 0: return (False, valuesDict, errorDict) else: return (True, valuesDict) def pickHub(self, filter=None, valuesDict=None, typeId=0, targetId=0): retList =[] for id, hub in self.hubDict.items(): hubDevice = indigo.devices[id] retList.append((id,hubDevice.name)) retList.sort(key=lambda tup: tup[1]) return retList
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater(self) #'tenallero', 'Indigo-XBMC', self) # Port self.listenPortDef = 8189 self.listenPort = 0 self.apiVersion = "2.0" self.localAddress = "" # Pooling self.pollingInterval = 0 self.requestID = 0 # Flag buttonRequest is processing self.reqRunning = 0 # create empty device list self.deviceList = {} self.sock = None self.socketBufferSize = 512 self.socketStop = False def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(device.name + ": Starting device") device.stateListOrDisplayStateIdChanged() self.addDeviceToList (device) def addDeviceToList(self,device): if device: if device.id not in self.deviceList: propsAddress = '' propsAddress = device.pluginProps["address"] propsAddress = propsAddress.strip() propsAddress = propsAddress.replace (' ','') self.deviceList[device.id] = {'ref':device,'address':propsAddress, 'lastTimeAlive':datetime.datetime.now()} def deleteDeviceFromList(self, device): if device: if device.id in self.deviceList: del self.deviceList[device.id] def deviceStopComm(self,device): if device.id in self.deviceList: self.debugLog(device.name + ": Stoping device") self.deleteDeviceFromList(device) def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.requestID = 0 # Obtain local address. # This will identify a XBMC device running in same machine than Indigo s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("gmail.com",80)) self.localAddress = s.getsockname()[0] s.close() self.debugLog("Local IP address: " + self.localAddress) self.updater.checkForUpdate() def shutdown(self): self.debugLog(u"shutdown called") def deviceCreated(self, device): self.debugLog(u"Created device of type \"%s\"" % device.deviceTypeId) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") ipAdr = valuesDict[u'address'] if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress (ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) tcpPort = valuesDict[u'port'] try: iPort = int(tcpPort) if iPort <= 0: errorMsgDict = indigo.Dict() errorMsgDict[u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) except Exception, e: errorMsgDict = indigo.Dict() errorMsgDict[u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) if (valuesDict[u'useAuthentication']): if not(valuesDict[u'username']>""): errorMsgDict = indigo.Dict() errorMsgDict[u'username'] = u"Must be filled." return (False, valuesDict, errorMsgDict) if not(valuesDict[u'password']>""): errorMsgDict = indigo.Dict() errorMsgDict[u'password'] = u"Must be filled." return (False, valuesDict, errorMsgDict) return (True, valuesDict)
class Plugin(indigo.PluginBase): ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): super(Plugin, self).__init__(pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = pluginPrefs.get("chkDebug", False) self.updater = GitHubPluginUpdater(self) self.updater.checkForUpdate(str(self.pluginVersion)) self.lastUpdateCheck = datetime.datetime.now() self.pollingInterval = 60 self.APIKey = pluginPrefs.get("txtAPIKey", None) self.currentEventN = "0" if "EVENTS" in self.pluginPrefs: self.EVENTS = json.loads(self.pluginPrefs["EVENTS"]) else: self.EVENTS = {} ######################################## def startup(self): self.debugLog(u"startup called") def checkForUpdates(self): self.updater.checkForUpdate() def updatePlugin(self): self.updater.update() def shutdown(self): self.pluginPrefs["EVENTS"] = json.dumps(self.EVENTS) self.debugLog(u"shutdown called") def deviceStartComm(self, dev): self.debugLog(u"deviceStartComm: %s" % (dev.name, )) ######################################## def validateDeviceConfigUi(self, valuesDict, typeId, devId): return (True, valuesDict) def updateConfig(self, valuesDict): return valuesDict def closedPrefsConfigUi(self, valuesDict, userCancelled): if not userCancelled: self.APIKey = valuesDict["txtAPIKey"] self.debug = valuesDict["chkDebug"] def eventConfigCallback(self, valuesDict, typeId=""): self.currentEventN = str(valuesDict["selectEvent"]) if self.currentEventN == "0": errorDict = valuesDict return valuesDict if not self.currentEventN in self.EVENTS: self.EVENTS[self.currentEventN] = copy.deepcopy(emptyEVENT) valuesDict["eventType"] = str( self.EVENTS[self.currentEventN]["eventType"]) valuesDict["txtOCR"] = str(self.EVENTS[self.currentEventN]["txtOCR"]) valuesDict["txtLabel"] = str( self.EVENTS[self.currentEventN]["txtLabel"]) valuesDict["txtLogo"] = str(self.EVENTS[self.currentEventN]["txtLogo"]) valuesDict["txtNotLabel"] = str( self.EVENTS[self.currentEventN]["txtNotLabel"]) valuesDict["txtLabelScore"] = str( self.EVENTS[self.currentEventN]["txtLabelScore"]) valuesDict["txtFaceScore"] = str( self.EVENTS[self.currentEventN]["txtFaceScore"]) valuesDict["txtLogoScore"] = str( self.EVENTS[self.currentEventN]["txtLogoScore"]) valuesDict["noFace"] = self.EVENTS[self.currentEventN]["noFace"] valuesDict["enableDisable"] = self.EVENTS[ self.currentEventN]["enableDisable"] self.updatePrefs = True return valuesDict def getMenuActionConfigUiValues(self, menuId): #indigo.server.log(u'Called getMenuActionConfigUiValues(self, menuId):') #indigo.server.log(u' (' + unicode(menuId) + u')') valuesDict = indigo.Dict() valuesDict["selectEvent"] = "0" valuesDict["eventType"] = "0" valuesDict["enableDisable"] = "0" errorMsgDict = indigo.Dict() return (valuesDict, errorMsgDict) def sendImageToGoogleForAnnotation(self, image, ocr, label, face, logo): request = {"requests": []} if image[:4].lower() == "http": request["requests"].append( {"image": { "source": { "imageUri": image } }}) else: try: with io.open(image, 'rb') as image_file: content = image_file.read() except: self.logger.error( "Error opening image to send to Google Vision") return request["requests"].append( {"image": { "content": b64encode(content) }}) request["requests"][0]["features"] = [] if ocr: request["requests"][0]["features"].append({ "type": "TEXT_DETECTION", "maxResults": MAX_RESULTS }) if label: request["requests"][0]["features"].append({ "type": "LABEL_DETECTION", "maxResults": MAX_RESULTS }) if face: request["requests"][0]["features"].append({ "type": "FACE_DETECTION", "maxResults": MAX_RESULTS }) if logo: request["requests"][0]["features"].append({ "type": "LOGO_DETECTION", "maxResults": MAX_RESULTS }) # self.logger.debug(json.dumps(request)) try: response = requests.post( url="https://vision.googleapis.com/v1/images:annotate?" + "key=" + self.APIKey, headers={"Content-Type": CONTENT_TYPE}, data=json.dumps(request)) self.logger.debug( 'Response HTTP Status Code: {status_code}'.format( status_code=response.status_code)) self.logger.debug('Response HTTP Response Body: {content}'.format( content=response.content)) except requests.exceptions.RequestException: self.logger.error('HTTP Request failed') return response.json() def sendImageToGoogleVisionAction(self, pluginAction, dev): if pluginAction.props["locationOption"] == "static": image = pluginAction.props["location"] else: image = indigo.variables[int( pluginAction.props["locationVariable"])].value indigo.server.log("sending " + image + " to Google Vision API") processOCR = False processLabel = False processFace = False processLogo = False for i in self.EVENTS: evnt = self.EVENTS[i] if pluginAction.props["event" + str(i)]: if evnt["eventType"] == "OCR": processOCR = True elif evnt["eventType"] == "Face": processFace = True elif evnt["eventType"] == "Label": processLabel = True elif evnt["eventType"] == "Logo": processLogo = True result = self.sendImageToGoogleForAnnotation(image, processOCR, processLabel, processFace, processLogo) self.logger.debug(json.dumps(result)) buildstr = "" facecounter = 0 resultsFound = False if "labelAnnotations" in result["responses"][0]: resultsFound = True for lbl in result["responses"][0]["labelAnnotations"]: buildstr += lbl["description"] + " (score:" + str( lbl["score"]) + "), " indigo.server.log("Label Results: " + buildstr[:-2]) buildstr = "" if "textAnnotations" in result["responses"][0]: resultsFound = True for ocr in result["responses"][0]["textAnnotations"]: if "locale" in ocr: buildstr += ocr["description"].replace( '\n', '') + " (language:" + ocr["locale"] + "), " else: buildstr += ocr["description"].replace('\n', '') + ", " indigo.server.log("OCR Results: " + buildstr[:-2]) buildstr = "" if "faceAnnotations" in result["responses"][0]: resultsFound = True for face in result["responses"][0]["faceAnnotations"]: facecounter += 1 buildstr += "Face " + str( facecounter) + " with confidence of " + str( face["detectionConfidence"]) + ". " buildstr += "joyLikelihood: " + str( face["joyLikelihood"]) + ", " buildstr += "sorrowLikelihood: " + str( face["sorrowLikelihood"]) + ", " buildstr += "angerLikelihood: " + str( face["angerLikelihood"]) + ", " buildstr += "surpriseLikelihood: " + str( face["surpriseLikelihood"]) + ", " buildstr += "underExposedLikelihood: " + str( face["underExposedLikelihood"]) + ", " buildstr += "blurredLikelihood: " + str( face["blurredLikelihood"]) + ", " buildstr += "headwearLikelihood: " + str( face["headwearLikelihood"]) + "; " buildstr = "Found a total of " + str( facecounter) + " face(s). " + buildstr indigo.server.log("Face Results: " + buildstr[:-2]) buildstr = "" if "logoAnnotations" in result["responses"][0]: resultsFound = True for logo in result["responses"][0]["logoAnnotations"]: if "locale" in logo: buildstr += logo["description"] + " (score:" + str( logo["score"] ) + ", language: " + logo["locale"] + "), " else: buildstr += logo["description"] + " (score:" + str( logo["score"]) + "), " indigo.server.log("Logo Results: " + buildstr[:-2]) buildstr = "" if not resultsFound: indigo.server.log("No results found in image.") for trigger in indigo.triggers.iter("self"): eventID = trigger.pluginTypeId[5:].strip() # self.logger.debug("size of self.EVENTS: " + str(len(self.EVENTS)) + " , eventID: " + eventID) if int(eventID) <= len(self.EVENTS): eventType = self.EVENTS[eventID]["eventType"] else: self.logger.error( "Trigger '" + trigger.name + "'' is configured for a disabled Google Vision event, skipping..." ) continue if not self.EVENTS[eventID]["enableDisable"]: self.logger.error( "Trigger '" + trigger.name + "'' is configured for a disabled Google Vision event, skipping..." ) continue if not pluginAction.props["event" + eventID]: self.logger.debug("Trigger '" + trigger.name + "' is not applicable for event " + eventID + ", skipping...") continue self.logger.debug("Evaluating trigger '" + trigger.name + "' (eventID: " + eventID + ", eventType: " + eventType + ")") if eventType == "OCR": ocrSearch = self.EVENTS[eventID]["txtOCR"] if "textAnnotations" in result["responses"][0]: for ocr in result["responses"][0]["textAnnotations"]: if ocrSearch.lower() in ocr["description"].lower(): indigo.trigger.execute(trigger) break elif eventType == "Face": if facecounter == 0 and self.EVENTS[eventID]["noFace"]: indigo.trigger.execute(trigger) elif facecounter == 0: continue else: for face in result["responses"][0]["faceAnnotations"]: if face["detectionConfidence"] >= float( self.EVENTS[eventID]["txtFaceScore"]): indigo.trigger.execute(trigger) break elif eventType == "Label": foundLabel = False foundNotLabel = False self.logger.debug("Trigger '" + trigger.name + "' Looking for labels: " + self.EVENTS[eventID]["txtLabel"] + "; and not for labels:" + self.EVENTS[eventID]["txtNotLabel"]) if "labelAnnotations" in result["responses"][0]: for lbl in result["responses"][0]["labelAnnotations"]: if len(self.EVENTS[eventID]["txtLabel"]) > 0: for lblSearch in self.EVENTS[eventID][ "txtLabel"].replace(" ", "").split(","): # self.logger.debug("Trigger '" + trigger.name + "' Evaluating label " + lblSearch) if lblSearch.lower( ) == lbl["description"].lower( ) and lbl["score"] >= float( self.EVENTS[eventID]["txtLabelScore"]): self.logger.debug( "Trigger '" + trigger.name + "' Found label of interest: " + lblSearch) foundLabel = True if len(self.EVENTS[eventID]["txtNotLabel"]) > 0: for lblNotSearch in self.EVENTS[eventID][ "txtNotLabel"].replace(" ", "").split(","): if lblNotSearch.lower( ) == lbl["description"].lower( ) and lbl["score"] >= float( self.EVENTS[eventID]["txtLabelScore"]): self.logger.debug( "Found anti-label of interest: " + lblNotSearch) foundNotLabel = True break if (len(self.EVENTS[eventID]["txtLabel"]) > 0 and foundLabel ) or (len(self.EVENTS[eventID]["txtNotLabel"]) > 0 and not foundNotLabel): indigo.trigger.execute(trigger) elif eventType == "Logo": foundLogo = False self.logger.debug("Looking for logos: " + self.EVENTS[eventID]["txtLogo"]) if "logoAnnotations" in result["responses"][0]: for logo in result["responses"][0]["logoAnnotations"]: if len(self.EVENTS[eventID]["txtLogo"]) > 0: for logoSearch in self.EVENTS[eventID][ "txtLogo"].replace(" ", "").split(","): if logoSearch.lower( ) == logo["description"].lower( ) and logo["score"] >= float( self.EVENTS[eventID]["txtLogoScore"]): self.logger.debug( "Found logo of interest: " + logoSearch) foundLogo = True if foundLogo: indigo.trigger.execute(trigger) ######################################## def buttonConfirmDevicesCALLBACK(self, valuesDict, typeId=""): errorDict = indigo.Dict() self.currentEventN = str(valuesDict["selectEvent"]) if self.currentEventN == "0" or self.currentEventN == "": return valuesDict if not self.currentEventN in self.EVENTS: self.EVENTS[self.currentEventN] = copy.deepcopy(emptyEVENT) if valuesDict["DeleteEvent"]: valuesDict["DeleteEvent"] = False valuesDict["eventType"] = "OCR" valuesDict["txtOCR"] = "" valuesDict["txtLabel"] = "" valuesDict["txtLogo"] = "" valuesDict["txtNotLabel"] = "" valuesDict["txtLabelScore"] = .90 valuesDict["txtLogoScore"] = .90 valuesDict["txtFaceScore"] = .90 valuesDict["enableDisable"] = False valuesDict["noFace"] = False self.EVENTS[self.currentEventN] = copy.deepcopy(emptyEVENT) self.currentEventN = "0" valuesDict["selectEvent"] = "0" valuesDict["EVENT"] = json.dumps(self.EVENTS) return valuesDict ##### not delete if valuesDict["enableDisable"] != "": self.EVENTS[self.currentEventN]["enableDisable"] = valuesDict[ "enableDisable"] else: self.EVENTS[self.currentEventN]["enableDisable"] = emptyEVENT[ "enableDisable"] valuesDict["enableDisable"] = emptyEVENT["enableDisable"] errorDict["enableDisable"] = emptyEVENT["enableDisable"] self.EVENTS[self.currentEventN]["eventType"] = valuesDict["eventType"] self.EVENTS[self.currentEventN]["txtOCR"] = valuesDict["txtOCR"] self.EVENTS[self.currentEventN]["txtLabel"] = valuesDict["txtLabel"] self.EVENTS[self.currentEventN]["txtLogo"] = valuesDict["txtLogo"] self.EVENTS[ self.currentEventN]["txtNotLabel"] = valuesDict["txtNotLabel"] self.EVENTS[ self.currentEventN]["txtLabelScore"] = valuesDict["txtLabelScore"] self.EVENTS[ self.currentEventN]["txtLogoScore"] = valuesDict["txtLogoScore"] self.EVENTS[ self.currentEventN]["txtFaceScore"] = valuesDict["txtFaceScore"] self.EVENTS[self.currentEventN]["noFace"] = valuesDict["noFace"] self.EVENTS[ self.currentEventN]["enableDisable"] = valuesDict["enableDisable"] valuesDict["EVENTS"] = json.dumps(self.EVENTS) if len(errorDict) > 0: return valuesDict, errorDict return valuesDict
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater( self) #'tenallero', 'Indigo-UniPi', self) self.apiVersion = "2.0" # Pooling self.pollingInterval = 0 # create empty device list self.boardList = {} self.relayList = {} self.digitalInputList = {} self.digitalCounterList = {} self.analogInputList = {} self.analogOutputList = {} self.tempSensorList = {} self.humiditySensorList = {} self.websocketEnabled = True def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(u"Started " + device.deviceTypeId + ": " + device.name) self.deviceUpdateAddress(device) device.stateListOrDisplayStateIdChanged() self.addDeviceToList(device) def deviceCreated(self, device): self.debugLog(u"Created device of type \"%s\"" % device.deviceTypeId) def addDeviceToList(self, device): if device.deviceTypeId == u"UniPiBoard": #self.updateDeviceState (device,'state' ,'off') device.updateStateOnServer(key='onOffState', value=False) device.updateStateImageOnServer(indigo.kStateImageSel.SensorOff) propsAddress = '' propsPort = '' if device.id not in self.boardList: propsAddress = device.pluginProps["address"] propsPort = device.pluginProps["port"] propsAddress = propsAddress.strip() propsAddress = propsAddress.replace(' ', '') propsPort = propsPort.replace(' ', '') if int(propsPort) <= 0: propsPort = "80" if propsPort == "80": wsurl = "ws://" + propsAddress + "/ws" else: wsurl = "ws://" + propsAddress + ":" + propsPort + "/ws" self.boardList[device.id] = { 'ref': device, 'address': propsAddress, 'port': propsPort, 'wsurl': wsurl, 'lastTimeSensor': datetime.datetime.now(), 'websocketok': False } self.startupWebSockets(device) elif device.deviceTypeId == u"UniPiRelay": if device.id not in self.relayList: self.relayList[device.id] = { 'ref': device, 'unipiSel': int(device.pluginProps["unipiSel"]), 'circuit': int(device.pluginProps["circuit"]) } elif device.deviceTypeId == u"UniPiDigitalInput": if device.id not in self.digitalInputList: self.digitalInputList[device.id] = { 'ref': device, 'unipiSel': int(device.pluginProps["unipiSel"]), 'circuit': int(device.pluginProps["circuit"]) } elif device.deviceTypeId == u"UniPiDigitalCounter": device.updateStateImageOnServer( indigo.kStateImageSel.EnergyMeterOn) if device.id not in self.digitalCounterList: self.digitalCounterList[device.id] = { 'ref': device, 'unipiSel': int(device.pluginProps["unipiSel"]), 'circuit': int(device.pluginProps["circuit"]), 'lastTimeSensor': datetime.datetime.now(), 'pulseHistor': [] } elif device.deviceTypeId == u"UniPiAnalogInput": if device.id not in self.analogInputList: self.analogInputList[device.id] = { 'ref': device, 'unipiSel': int(device.pluginProps["unipiSel"]), 'circuit': int(device.pluginProps["circuit"]) } elif device.deviceTypeId == u"UniPiAnalogOutput": if device.id not in self.analogOutputList: self.analogOutputList[device.id] = { 'ref': device, 'unipiSel': int(device.pluginProps["unipiSel"]), 'circuit': int(device.pluginProps["circuit"]) } elif device.deviceTypeId == u"UniPiTempSensor": device.updateStateImageOnServer( indigo.kStateImageSel.TemperatureSensorOn) if device.id not in self.tempSensorList: self.tempSensorList[device.id] = { 'ref': device, 'unipiSel': int(device.pluginProps["unipiSel"]), 'circuit': device.pluginProps["circuit"], 'logLastValue': 0.0 } elif device.deviceTypeId == u"UniPiHumiditySensor": device.updateStateImageOnServer( indigo.kStateImageSel.HumiditySensorOn) if device.id not in self.humiditySensorList: self.humiditySensorList[device.id] = { 'ref': device, 'unipiSel': int(device.pluginProps["unipiSel"]), 'circuit': device.pluginProps["circuit"], 'logLastValue': 0.0 } def deviceUpdateAddress(self, device): newAddress = '' if device.deviceTypeId == u"UniPiRelay": newAddress = 'DO-' + str(device.pluginProps["circuit"]) elif device.deviceTypeId == u"UniPiDigitalInput": newAddress = 'DI-' + str(device.pluginProps["circuit"]) elif device.deviceTypeId == u"UniPiDigitalCounter": newAddress = 'DI-' + str(device.pluginProps["circuit"]) elif device.deviceTypeId == u"UniPiAnalogInput": newAddress = 'AI-' + str(device.pluginProps["circuit"]) elif device.deviceTypeId == u"UniPiAnalogOutput": newAddress = 'AO-' + str(device.pluginProps["circuit"]) elif device.deviceTypeId == u"UniPiTempSensor": newAddress = '1Wire-' + str(device.pluginProps["circuit"]) return if newAddress: if not device.pluginProps["address"] == newAddress: devProps = device.pluginProps device["address"] = newAddress device.replacePluginPropsOnServer(devProps) def didDeviceCommPropertyChange(self, origDev, newDev): return False def deviceStopComm(self, device): indigo.server.log(u"Stopping device \"%s\" of type \"%s\"" % (device.name, device.deviceTypeId)) self.deleteDeviceFromList(device) def deviceDeleted(self, device): indigo.server.log(u"Deleted device \"%s\" of type \"%s\"" % (device.name, device.deviceTypeId)) self.deleteDeviceFromList(device) def deleteDeviceFromList(self, device): if device.deviceTypeId == u"UniPiBoard": if device.id in self.boardList: self.debugLog("Stoping UniPi board device: " + device.name) del self.boardList[device.id] if device.deviceTypeId == u"UniPiRelay": if device.id in self.relayList: del self.relayList[device.id] if device.deviceTypeId == u"UniPiDigitalInput": if device.id in self.digitalInputList: del self.digitalInputList[device.id] if device.deviceTypeId == u"UniPiDigitalCounter": if device.id in self.digitalCounterList: del self.digitalCounterList[device.id] if device.deviceTypeId == u"UniPiAnalogInput": if device.id in self.analogInputList: del self.analogInputList[device.id] if device.deviceTypeId == u"UniPiAnalogOutput": if device.id in self.analogOutputList: del self.analogOutputList[device.id] if device.deviceTypeId == u"UniPiTempSensor": if device.id in self.tempSensorList: del self.tempSensorList[device.id] if device.deviceTypeId == u"UniPiHumiditySensor": if device.id in self.humiditySensorList: del self.humiditySensorList[device.id] def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.updater.checkForUpdate() def shutdown(self): self.debugLog(u"shutdown called") def getDeviceConfigUiValues(self, pluginProps, typeId, devId): valuesDict = pluginProps errorMsgDict = indigo.Dict() if self._devTypeIdIsMirrorDevice(typeId): if "unipiSel" not in valuesDict: valuesDict["unipiSel"] = 0 if "circuit" not in valuesDict: valuesDict["circuit"] = 0 return (valuesDict, errorMsgDict) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") if typeId == u"UniPiBoard": self.debugLog(u"validating IP Address") ipAdr = valuesDict[u'address'] if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[ u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress(ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[ u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) self.debugLog(u"validating TCP Port") tcpPort = valuesDict[u'port'] try: iPort = int(tcpPort) if iPort <= 0: errorMsgDict = indigo.Dict() errorMsgDict[ u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) except Exception, e: errorMsgDict = indigo.Dict() errorMsgDict[u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) if typeId == u"UniPiRelay": if int(valuesDict[u'unipiSel']) <= 0: errorMsgDict[u'unipiSel'] = u"Need to choose a Unipi device" return (False, valuesDict, errorMsgDict) if int(valuesDict[u'circuit']) <= 0: errorMsgDict[u'circuit'] = u"Need to choose a relay" return (False, valuesDict, errorMsgDict) pass if typeId == u"UniPiDigitalInput": if int(valuesDict[u'unipiSel']) <= 0: errorMsgDict[u'unipiSel'] = u"Need to choose a Unipi device" return (False, valuesDict, errorMsgDict) if int(valuesDict[u'circuit']) <= 0: errorMsgDict[u'circuit'] = u"Need to choose a digital input" return (False, valuesDict, errorMsgDict) pass if typeId == u"UniPiDigitalCounter": if int(valuesDict[u'unipiSel']) <= 0: errorMsgDict[u'unipiSel'] = u"Need to choose a Unipi device" return (False, valuesDict, errorMsgDict) if int(valuesDict[u'circuit']) <= 0: errorMsgDict[u'circuit'] = u"Need to choose a digital input" return (False, valuesDict, errorMsgDict) pass if typeId == u"UniPiAnalogInput": if int(valuesDict[u'unipiSel']) <= 0: errorMsgDict[u'unipiSel'] = u"Need to choose a Unipi device" return (False, valuesDict, errorMsgDict) if int(valuesDict[u'circuit']) <= 0: errorMsgDict[u'circuit'] = u"Need to choose an analog input" return (False, valuesDict, errorMsgDict) pass if typeId == u"UniPiAnalogOutput": if int(valuesDict[u'unipiSel']) <= 0: errorMsgDict[u'unipiSel'] = u"Need to choose a Unipi device" return (False, valuesDict, errorMsgDict) if int(valuesDict[u'circuit']) <= 0: errorMsgDict[u'circuit'] = u"Need to choose an analog output" return (False, valuesDict, errorMsgDict) pass if typeId == u"UniPiTempSensor": if int(valuesDict[u'unipiSel']) <= 0: errorMsgDict[u'unipiSel'] = u"Need to choose a Unipi device" return (False, valuesDict, errorMsgDict) if valuesDict[u'circuit'].strip() == "": errorMsgDict[u'circuit'] = u"Need to choose a 1-Wire address" return (False, valuesDict, errorMsgDict) pass if typeId == u"UniPiHumiditySensor": if int(valuesDict[u'unipiSel']) <= 0: errorMsgDict[u'unipiSel'] = u"Need to choose a Unipi device" return (False, valuesDict, errorMsgDict) if valuesDict[u'circuit'].strip() == "": errorMsgDict[u'circuit'] = u"Need to choose a 1-Wire address" return (False, valuesDict, errorMsgDict) pass return (True, valuesDict)
class Plugin(indigo.PluginBase): ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): super(Plugin, self).__init__(pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.NuHeat = NuHeat(self) self.debug = pluginPrefs.get("debug", False) self.UserID = None self.Password = None self.deviceList = {} self.loginFailed = False self.restartCount = 0 ###################### def _changeTempSensorValue(self, dev, index, value, keyValueList): # Update the temperature value at index. If index is greater than the "NumTemperatureInputs" # an error will be displayed in the Event Log "temperature index out-of-range" stateKey = u"temperatureInput" + str(index) keyValueList.append({ 'key': stateKey, 'value': value, 'uiValue': "%d °F" % (value) }) self.debugLog(u"\"%s\" updating %s %d" % (dev.name, stateKey, value)) ###################### # Poll all of the states from the thermostat and pass new values to # Indigo Server. def _refreshStatesFromHardware(self, dev, logRefresh, commJustStarted): thermostatId = dev.pluginProps["thermostatId"] self.debugLog(u"Getting data for thermostatId: %s" % thermostatId) thermostat = NuHeat.GetThermostat(self.NuHeat, thermostatId) self.debugLog(u"Device Name: %s" % thermostat.room) self.debugLog(u"***Device SystemSwitch: %s" % thermostat.Mode) try: self.updateStateOnServer(dev, "name", thermostat.room) except: self.de(dev, "name") try: self.updateStateOnServer(dev, "setpointHeat", float(thermostat.SetPointTemp)) except: self.de(dev, "setpointHeat") try: self.updateStateOnServer(dev, "maxHeatSetpoint", thermostat.MaxTemp) except: self.de(dev, "maxHeatSetpoint") try: self.updateStateOnServer(dev, "minHeatSetpoint", thermostat.MinTemp) except: self.de(dev, "minHeatSetpoint") try: self.updateStateOnServer(dev, "online", thermostat.Online) except: pass try: self.updateStateOnServer(dev, "temperatureInput1", thermostat.Temperature) except: pass self.debugLog(u"Heating: %s" % thermostat.Heating) if thermostat.Heating == True: self.updateStateOnServer(dev, "hvacHeaterIsOn", True) else: self.updateStateOnServer(dev, "hvacHeaterIsOn", False) def updateStateOnServer(self, dev, state, value): if dev.states[state] != value: self.debugLog(u"Updating Device: %s, State: %s, Value: %s" % (dev.name, state, value)) dev.updateStateOnServer(state, value) def de(self, dev, value): self.errorLog("[%s] No value found for device: %s, field: %s" % (time.asctime(), dev.name, value)) ###################### # Process action request from Indigo Server to change a cool/heat setpoint. def _handleChangeSetpointAction(self, dev, newSetpoint, Permanent, duration, logActionName, stateKey): self.debugLog(u"_handleChangeSetpointAction - StateKey: %s" % stateKey) sendSuccess = False thermostatId = dev.pluginProps["thermostatId"] self.debugLog(u"Getting data for thermostatId: %s" % thermostatId) self.debugLog(u"NewSetpoint: %s, Permanent: %s, Duration: %s" % (newSetpoint, Permanent, duration)) thermostat = NuHeat.GetThermostat(self.NuHeat, thermostatId) if stateKey == u"setpointHeat": # if newSetpoint < dev.states["maxHeatSetpoint"]: # newSetpoint = dev.states["maxHeatSetpoint"] # elif newSetpoint > dev.states["minHeatSetpoint"]: # newSetpoint = dev.states["minHeatSetpoint"] if Permanent == True: duration = -1 sendSuccess = self.NuHeat.SetThermostatHeatSetpoint( thermostat, newSetpoint, duration) if sendSuccess: # If success then log that the command was successfully sent. indigo.server.log(u"sent \"%s\" %s to %.1f°" % (dev.name, logActionName, float(newSetpoint))) # And then tell the Indigo Server to update the state. if stateKey in dev.states: dev.updateStateOnServer(stateKey, newSetpoint, uiValue="%.1f °F" % float(newSetpoint)) else: # Else log failure but do NOT update state on Indigo Server. indigo.server.log(u"send \"%s\" %s to %.1f° failed" % (dev.name, logActionName, float(newSetpoint)), isError=True) def _handleResumeScheduleAction(self, dev, logActionName, stateKey): self.debugLog(u"_handleResumeScheduleAction - StateKey: %s" % stateKey) sendSuccess = False thermostatId = dev.pluginProps["thermostatId"] self.debugLog(u"Getting data for thermostatId: %s" % thermostatId) thermostat = NuHeat.GetThermostat(self.NuHeat, thermostatId) sendSuccess = self.NuHeat.SetResumeSchedule(thermostat) if sendSuccess: # If success then log that the command was successfully sent. indigo.server.log(u"sent \"%s\" %s" % (dev.name, logActionName)) else: # Else log failure but do NOT update state on Indigo Server. indigo.server.log(u"send \"%s\" %s failed" % (dev.name, logActionName), isError=True) ######################################## def startup(self): self.debugLog(u"NuHeat startup called") self.debug = self.pluginPrefs.get('showDebugInLog', False) self.updater = GitHubPluginUpdater(self) #self.updater.checkForUpdate() self.updateFrequency = float( self.pluginPrefs.get('updateFrequency', 24)) * 60.0 * 60.0 self.debugLog(u"updateFrequency = " + str(self.updateFrequency)) self.next_update_check = time.time() self.login(False) def login(self, force): if self.NuHeat.startup(force) == False: indigo.server.log( u"Login to mynuheat.com site failed. Canceling processing!", isError=True) self.loginFailed = True return else: self.loginFailed = False self.buildAvailableDeviceList() def shutdown(self): self.debugLog(u"shutdown called") ######################################## def runConcurrentThread(self): try: while True: if self.loginFailed == False: if (self.updateFrequency > 0.0) and (time.time() > self.next_update_check): self.next_update_check = time.time( ) + self.updateFrequency self.updater.checkForUpdate() for dev in indigo.devices.iter("self"): if not dev.enabled: continue # Plugins that need to poll out the status from the thermostat # could do so here, then broadcast back the new values to the # Indigo Server. self._refreshStatesFromHardware(dev, False, False) self.restartCount = self.restartCount + 1 if (self.restartCount > 10000): self.restartCount = 0 indigo.server.log( u"Memory Leak Prevention. Restarting Plugin. - This will happen until I find and fix the leak" ) serverPlugin = indigo.server.getPlugin(self.pluginId) serverPlugin.restart(waitUntilDone=False) break self.sleep(20) except self.StopThread: pass # Optionally catch the StopThread exception and do any needed cleanup. ######################################## def validateDeviceConfigUi(self, valuesDict, typeId, devId): indigo.server.log(u"validateDeviceConfigUi \"%s\"" % (valuesDict)) return (True, valuesDict) def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"Vaidating Plugin Configuration") errorsDict = indigo.Dict() if len(errorsDict) > 0: self.errorLog(u"\t Validation Errors") return (False, valuesDict, errorsDict) else: self.debugLog(u"\t Validation Succesful") return (True, valuesDict) return (True, valuesDict) ######################################## def deviceStartComm(self, dev): if self.loginFailed == True: return # Called when communication with the hardware should be established. # Here would be a good place to poll out the current states from the # thermostat. If periodic polling of the thermostat is needed (that # is, it doesn't broadcast changes back to the plugin somehow), then # consider adding that to runConcurrentThread() above. self.initDevice(dev) dev.stateListOrDisplayStateIdChanged() #self._refreshStatesFromHardware(dev, True, True) def deviceStopComm(self, dev): # Called when communication with the hardware should be shutdown. pass ######################################## # Thermostat Action callback ###################### # Main thermostat action bottleneck called by Indigo Server. #Called when the device is changed via UI def actionControlThermostat(self, action, dev): ###### SET HVAC MODE ###### if action.thermostatAction == indigo.kThermostatAction.SetHeatSetpoint: newSetpoint = action.actionValue self._handleChangeSetpointAction(dev, newSetpoint, False, 1, u"change heat setpoint", u"setpointHeat") ###### DECREASE/INCREASE HEAT SETPOINT ###### elif action.thermostatAction == indigo.kThermostatAction.DecreaseHeatSetpoint: newSetpoint = dev.heatSetpoint - action.actionValue self._handleChangeSetpointAction(dev, newSetpoint, False, 1, u"decrease heat setpoint", u"setpointHeat") elif action.thermostatAction == indigo.kThermostatAction.IncreaseHeatSetpoint: newSetpoint = dev.heatSetpoint + action.actionValue self._handleChangeSetpointAction(dev, newSetpoint, False, 1, u"increase heat setpoint", u"setpointHeat") ###### REQUEST STATE UPDATES ###### elif action.thermostatAction in [ indigo.kThermostatAction.RequestStatusAll, indigo.kThermostatAction.RequestMode, indigo.kThermostatAction.RequestEquipmentState, indigo.kThermostatAction.RequestTemperatures, indigo.kThermostatAction.RequestHumidities, indigo.kThermostatAction.RequestDeadbands, indigo.kThermostatAction.RequestSetpoints ]: self._refreshStatesFromHardware(dev, True, False) ######################################## # Actions defined in MenuItems.xml. In this case we just use these menu actions to # simulate different thermostat configurations (how many temperature and humidity # sensors they have). #################### def _actionSetMode(self, pluginAction): self.debugLog(u"\t Setting Mode: %s" % pluginAction.props.get("mode")) dev = indigo.devices[pluginAction.deviceId] self._handleChangeHvacModeAction( dev, map_to_indigo_hvac_mode[pluginAction.props.get("mode")]) def _actionSetpoint(self, pluginAction): self.debugLog( u"\t Set %s - Setpoint: %s" % (pluginAction.pluginTypeId, pluginAction.props.get("Temprature"))) dev = indigo.devices[pluginAction.deviceId] self.debugLog(u"\t Permanent: %s " % (pluginAction.props)) self._handleChangeSetpointAction(dev, pluginAction.props.get("Temprature"), pluginAction.props.get("Timing"), pluginAction.props.get("Duration"), "Action Setpoint", pluginAction.pluginTypeId) def _actionResumeSchedule(self, pluginAction): self.debugLog(u"\t Resume Schedule") dev = indigo.devices[pluginAction.deviceId] self._handleResumeScheduleAction(dev, "Action Resume Schedule", pluginAction.pluginTypeId) def closedPrefsConfigUi(self, valuesDict, userCancelled): if not userCancelled: #Check if Debugging is set try: self.debug = self.pluginPrefs[u'showDebugInLog'] except: self.debug = False try: if (self.UserID != self.pluginPrefs["UserID"]) or \ (self.Password != self.pluginPrefs["Password"]): indigo.server.log("[%s] Replacting Username/Password." % time.asctime()) self.UserID = self.pluginPrefs["UserID"] self.Password = self.pluginPrefs["Password"] except: pass indigo.server.log("[%s] Processed plugin preferences." % time.asctime()) self.login(True) return True def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validateDeviceConfigUi called with valuesDict: %s" % str(valuesDict)) # Set the address valuesDict["ShowCoolHeatEquipmentStateUI"] = True return (True, valuesDict) def initDevice(self, dev): new_props = dev.pluginProps new_props['SupportsHvacFanMode'] = False new_props['SupportsHvacOperationMode'] = False dev.replacePluginPropsOnServer(new_props) self.debugLog("Initializing thermostat device: %s" % dev.name) def buildAvailableDeviceList(self): self.debugLog("Building Available Device List") self.deviceList = self.NuHeat.GetDevices() indigo.server.log("Number of thermostats found: %i" % (len(self.deviceList))) for (k, v) in self.deviceList.iteritems(): indigo.server.log("\t%s (id: %s)" % (v.room, k)) def showAvailableThermostats(self): indigo.server.log("Number of thermostats found: %i" % (len(self.deviceList))) for (id, details) in self.deviceList.iteritems(): indigo.server.log("\t%s (id: %s)" % (details.room, id)) def thermostatList(self, filter, valuesDict, typeId, targetId): self.debugLog("thermostatList called") deviceArray = [] deviceListCopy = deepcopy(self.deviceList) for existingDevice in indigo.devices.iter("self"): for id in self.deviceList: self.debugLog(" comparing %s against deviceList item %s" % (existingDevice.pluginProps["thermostatId"], id)) if existingDevice.pluginProps["thermostatId"] == id: self.debugLog(" removing item %s" % (id)) del deviceListCopy[id] break if len(deviceListCopy) > 0: for (id, value) in deviceListCopy.iteritems(): deviceArray.append((id, value.room)) else: if len(self.deviceList): indigo.server.log("All thermostats found are already defined") else: indigo.server.log( "No thermostats were discovered on the network - select \"Rescan for Thermostats\" from the plugin's menu to rescan" ) self.debugLog(" thermostatList deviceArray:\n%s" % (str(deviceArray))) return deviceArray def thermostatSelectionChanged(self, valuesDict, typeId, devId): self.debugLog("thermostatSelectionChanged") if valuesDict["thermostat"] in self.deviceList: selectedThermostatData = self.deviceList[valuesDict["thermostat"]] valuesDict["address"] = valuesDict["thermostat"] valuesDict["thermostatId"] = valuesDict["thermostat"] valuesDict["name"] = selectedThermostatData.room self.debugLog( " thermostatSelectionChanged valuesDict to be returned:\n%s" % (str(valuesDict))) return valuesDict ########################################## def checkForUpdates(self): self.updater.checkForUpdate() def updatePlugin(self): self.updater.update() def forceUpdate(self): self.updater.update(currentVersion='0.0.0')
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) pfmt = logging.Formatter( '%(asctime)s.%(msecs)03d\t[%(levelname)8s] %(name)20s.%(funcName)-25s%(msg)s', datefmt='%Y-%m-%d %H:%M:%S') self.plugin_file_handler.setFormatter(pfmt) try: self.logLevel = int(self.pluginPrefs[u"logLevel"]) except: self.logLevel = logging.INFO self.indigo_log_handler.setLevel(self.logLevel) self.logger.debug(u"logLevel = " + str(self.logLevel)) self.debugLog(u"Initializing Cisco/Sipura VOIP plugin.") self.timeOutCount = 0 self.deviceNeedsUpdated = '' self.prefServerTimeout = int( self.pluginPrefs.get('configMenuServerTimeout', "15")) self.updater = GitHubPluginUpdater(self) self.configUpdaterInterval = self.pluginPrefs.get( 'configUpdaterInterval', 24) self.configUpdaterForceUpdate = self.pluginPrefs.get( 'configUpdaterForceUpdate', False) self.useNumberConversion = self.pluginPrefs.get( 'useNumberConversion', False) self.folderLocation = self.pluginPrefs.get('folderLocation', '') #self.sipServer = self.pluginPrefs.get('configUpdaterForceUpdate', False) self.deviceId = 0 self.ringing = False self.connected = False self.callType = '' self.connectTime = '' self.kill = True self.checkTime = t.time() self.triggers = {} self.currentNumber = '' self.numberstoConvert = [] def __del__(self): self.debugLog(u"__del__ method called.") indigo.PluginBase.__del__(self) def closedPrefsConfigUi(self, valuesDict, userCancelled): self.debugLog(u"closedPrefsConfigUi() method called.") if userCancelled: self.debugLog(u"User prefs dialog cancelled.") if not userCancelled: self.debugLog(u"User prefs saved.") indigo.server.log(u"Debugging Level: {0}".format(self.logLevel)) self.useNumberConversion = self.pluginPrefs.get( 'useNumberConversion', False) self.folderLocation = self.pluginPrefs.get('folderLocation', '') if self.useNumberConversion: self.loadFileNumbers() else: self.numberstoConvert = [] self.logger.debug(u'Numbers to Convert Removed:' + str(self.numberstoConvert)) return True def loadFileNumbers(self): self.logger.debug(u'loadFile of Number Conversions called') self.folderLocation = self.pluginPrefs.get('folderLocation', '') if self.folderLocation == '': self.logger.info(u'Folder Name cannot be empty') return self.numberstoConvert = [] try: folder = self.folderLocation filename = 'numbersConvert.txt' #f = open(folder+'/'+filename, 'r') with open(folder + '/' + filename, 'rb') as fp: for i in fp.readlines(): tmp = i.split(',') try: self.numberstoConvert.append( (str(tmp[0].strip()), str(tmp[1].strip()))) except Exception as error: self.logger.error( u'Error with numbersConvert.txt file Data Format File: are they comma seperated with each on new line?' ) self.logger.debug(error) pass self.logger.debug(self.numberstoConvert) except Exception as error: self.logger.info(u'Exception within loadFileNumbers:' + str(error)) return def calltoLoadFile(self, valuesDict): self.logger.debug(u'LoadFile Config Button Pressed') self.loadFileNumbers() return # Start 'em up. def deviceStartComm(self, dev): self.debugLog(u"deviceStartComm() method called.") self.debugLog(u'deviceID equals:' + str(dev.id)) self.debugLog(u"Starting Device: {0}:".format(dev.name)) dev.updateStateOnServer("currentNumber", '') dev.updateStateOnServer("deviceStatus", '') dev.updateStateOnServer("callType", '') update_time = t.strftime("%m/%d/%Y at %H:%M") #dev.updateStateOnServer('deviceLastUpdated', value=update_time) dev.updateStateOnServer('callTime', value='') self.deviceId = dev.id dev.updateStateOnServer('deviceOnline', False) self.kill = False if self.useNumberConversion: self.loadFileNumbers() # Shut 'em down. def deviceStopComm(self, dev): self.debugLog(u"deviceStopComm() method called.") indigo.server.log(u"Stopping SIPURA device: " + dev.name) dev.updateStateOnServer('deviceOnline', False) self.kill = True def forceUpdate(self): self.updater.update(currentVersion='0.0.0') def checkForUpdates(self): if self.updater.checkForUpdate() == False: indigo.server.log(u"No Updates are Available") def updatePlugin(self): self.updater.update() def runConcurrentThread(self): # Gra # b the first device (don' want to multithread0 while self.stopThread == False: for dev in indigo.devices.itervalues('self'): if dev.configured and dev.enabled: dev.updateStateOnServer('deviceOnline', True) porttouse = dev.pluginProps['sourcePort'] s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.settimeout(1) self.logger.info(u"Socket Connection Opened on port:" + str(porttouse)) try: s.bind(('', int(porttouse))) except socket.error, msg: self.errorLog(u'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + str(msg[1])) dev.updateStateOnServer('deviceOnline', False) dev.setErrorStateOnServer(u'Offline') self.sleep(5) try: while True and dev.enabled and dev.configured and self.kill == False: # Only support one VOIP device # very likely easier way to do it #self.debugLog(u"MainLoop running: {0}:".format(dev.name)) #How to limit indigo that only one device is allowed? try: self.updateStates(dev) data, addr = s.recvfrom(9024) self.parseData(dev, data, addr) except socket.timeout: # or no data received. #self.debugLog(u"SOCKET Error {0}:".format(e)) pass if self.connected or self.ringing: self.sleep(0.2) else: self.sleep(1) except self.stopThread: self.debugLog( u'Restarting/or error. Stopping SpiruaVOIP thread.' ) s.close() pass self.debugLog(u' No Configured & Enabled Device:') # How to limit indigo that only one device is allowed? self.sleep(60)
class Plugin(indigo.PluginBase): ######################################## # Main Plugin methods ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = self.pluginPrefs.get(u"showDebugInfo", False) self.debugLog(u"Debugging enabled") def startup(self): indigo.server.log(u"Starting SMTPd") self.updater = GitHubPluginUpdater(self) self.updateFrequency = float(self.pluginPrefs.get('updateFrequency', '24')) * 60.0 * 60.0 self.next_update_check = time.time() if "SMTPd" in indigo.variables.folders: myFolder = indigo.variables.folders["SMTPd"] else: myFolder = indigo.variables.folder.create("SMTPd") self.pluginPrefs["folderId"] = myFolder.id self.triggers = { } def shutdown(self): indigo.server.log(u"Shutting down SMTPd") def runConcurrentThread(self): port = int(self.pluginPrefs.get('smtpPort', '2525')) user = self.pluginPrefs.get('smtpUser', 'guest') password = self.pluginPrefs.get('smtpPassword', 'password') portal = Portal(SimpleRealm()) checker = InMemoryUsernamePasswordDatabaseDontUse() checker.addUser(user, password) portal.registerChecker(checker) try: self.smtpFactory = MySMTPFactory(portal) self.listeningPort = reactor.listenTCP(port, self.smtpFactory) reactor.run() except self.StopThread: pass # Optionally catch the StopThread exception and do any needed cleanup. def stopConcurrentThread(self): indigo.PluginBase.stopConcurrentThread(self) reactor.callFromThread(self.listeningPort.stopListening) reactor.callFromThread(reactor.stop) #################### def triggerStartProcessing(self, trigger): self.debugLog("Adding Trigger %s (%d) - %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) assert trigger.id not in self.triggers self.triggers[trigger.id] = trigger def triggerStopProcessing(self, trigger): self.debugLog("Removing Trigger %s (%d)" % (trigger.name, trigger.id)) assert trigger.id in self.triggers del self.triggers[trigger.id] def triggerCheck(self): for triggerId, trigger in sorted(self.triggers.iteritems()): self.debugLog("\tChecking Trigger %s (%s), Type: %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) if trigger.pluginTypeId == 'messageReceived': indigo.trigger.execute(trigger) #################### def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"validatePrefsConfigUi called") errorDict = indigo.Dict() updateFrequency = int(valuesDict['updateFrequency']) if (updateFrequency < 0) or (updateFrequency > 24): errorDict['updateFrequency'] = u"Update frequency is invalid - enter a valid number (between 0 and 24)" smtpPort = int(valuesDict['smtpPort']) if smtpPort < 1024: errorDict['smtpPort'] = u"SMTP Port Number invalid" if len(errorDict) > 0: return (False, valuesDict, errorDict) return (True, valuesDict) ######################################## def closedPrefsConfigUi(self, valuesDict, userCancelled): if not userCancelled: self.debug = valuesDict.get("showDebugInfo", False) if self.debug: self.debugLog(u"Debug logging enabled") else: self.debugLog(u"Debug logging disabled") ######################################## # Menu Methods ######################################## def toggleDebugging(self): self.debug = not self.debug self.pluginPrefs["debugEnabled"] = self.debug indigo.server.log("Debug set to: " + str(self.debug)) def checkForUpdates(self): self.updater.checkForUpdate() def updatePlugin(self): self.updater.update() def forceUpdate(self): self.updater.update(currentVersion='0.0.0')
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater(self) self.ControllerIP = "" self.ControllerPort = "" self.ControllerRel = "V4" self.ControllerSite = "" self.ControllerAuth = False self.ControllerUsername = "" self.ControllerPassword = "" self.ControllerInterval = 0 self.ControllerURL = "" self.ControllerURLAPI = "" self.CurlCommand = "" # create empty device list self.userDeviceList = {} self.wlanDeviceList = {} self.sock = None self.socketBufferSize = 512 self.socketStop = False self.pushoverPlugin = None def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(device.name + ": Starting device") device.stateListOrDisplayStateIdChanged() self.addDeviceToList (device) def addDeviceToList(self,device): if device: if device.deviceTypeId == u"unifiuser": self.addDeviceToListUser(device) elif device.deviceTypeId == u"unifiwlan": self.addDeviceToListWlan(device) def addDeviceToListUser(self,device): propsIPAddress = '' propsMACAddress = '' if device.id not in self.userDeviceList: propsIPAddress = device.pluginProps["ipaddress"].strip().replace (' ','') propsMACAddress = device.pluginProps["macaddress"].strip().replace (' ','') self.userDeviceList[device.id] = {'ref':device, 'ipaddress':propsIPAddress, 'macaddress':propsMACAddress} if propsMACAddress > '': device.pluginProps["address"] = propsMACAddress else: device.pluginProps["address"] = propsIPAddress def addDeviceToListWlan(self,device): if device.id not in self.wlanDeviceList: ssid = device.pluginProps["ssid"].strip() self.wlanDeviceList[device.id] = {'ref':device, 'ssid':ssid} def deleteDeviceFromList(self, device): if device: if device.deviceTypeId == u"unifiuser": if device.id in self.userDeviceList: del self.userDeviceList[device.id] elif device.deviceTypeId == u"unifiwlan": if device.id in self.wlanDeviceList: del self.wlanDeviceList[device.id] def deviceStopComm(self,device): if device.id not in self.userDeviceList: return self.debugLog(device.name + ": Stoping device") self.deleteDeviceFromList(device) def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.requestID = 0 self.pushoverPlugin = indigo.server.getPlugin("io.thechad.indigoplugin.pushover") if not self.pushoverPlugin.isEnabled(): self.debugLog (u"Error: Pushover plugin is not enabled") self.updater.checkForUpdate() def shutdown(self): self.debugLog(u"shutdown called") def deviceCreated(self, device): self.debugLog(u"Created device of type \"%s\"" % device.deviceTypeId) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") if typeId == "unifiuser": ipAdr = valuesDict[u'ipaddress'] macAdr = valuesDict[u'macaddress'] if (ipAdr > "") or (macAdr > ""): pass else: errorMsgDict = indigo.Dict() errorMsgDict[u'ipaddress'] = u"Mac or IP address needed." errorMsgDict[u'macaddress'] = u"Mac or IP address needed." return (False, valuesDict, errorMsgDict) if (ipAdr > ""): if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[u'ipaddress'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress (ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[u'ipaddress'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if (macAdr > ""): #1c:ab:a7:d8:23:d2 if macAdr.count(':') != 5: errorMsgDict = indigo.Dict() errorMsgDict[u'macaddress'] = u"This needs to be a valid MAC address." return (False, valuesDict, errorMsgDict) if typeId == "unifiwlan": ssid = valuesDict[u'ssid'].strip() if not ssid: errorMsgDict = indigo.Dict() errorMsgDict[u'ssid'] = u"SSID needed." return (False, valuesDict, errorMsgDict) return (True, valuesDict) def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"validating Prefs called") ipAdr = valuesDict[u'ipaddress'] if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[u'ipaddress'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress (ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[u'ipaddress'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) tcpPort = valuesDict[u'port'] try: iPort = int(tcpPort) if iPort <= 0: errorMsgDict = indigo.Dict() errorMsgDict[u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) except Exception, e: errorMsgDict = indigo.Dict() errorMsgDict[u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) if (valuesDict[u'useAuthentication']): if not(valuesDict[u'username']>""): errorMsgDict = indigo.Dict() errorMsgDict[u'username'] = u"Must be filled." return (False, valuesDict, errorMsgDict) if not(valuesDict[u'password']>""): errorMsgDict = indigo.Dict() errorMsgDict[u'password'] = u"Must be filled." return (False, valuesDict, errorMsgDict) return (True, valuesDict)
class Plugin(indigo.PluginBase): # --------------------------------------------------------------------------- def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = pluginPrefs.get("debug", False) self.updater = GitHubPluginUpdater(self) # --------------------------------------------------------------------------- def __del__(self): indigo.PluginBase.__del__(self) # --------------------------------------------------------------------------- def checkForUpdates(self): self.updater.checkForUpdate() # --------------------------------------------------------------------------- def updatePlugin(self): self.updater.update() # --------------------------------------------------------------------------- def toggleDebugging(self): self.debug = not self.debug self.pluginPrefs["debug"] = self.debug # --------------------------------------------------------------------------- def validatePrefsConfigUi(self, values): errors = indigo.Dict() # application name is required... appname = values.get("appname", "") if len(appname) == 0: errors["appname"] = "You must provide an application name" # an API key is required and must be valid... apikey = values.get("apikey", "") if len(apikey) == 0: errors["apikey"] = "You must provide your Prowl API key" elif not self.prowlVerify(apikey): errors["apikey"] = "Invalid API key" return ((len(errors) == 0), values, errors) # --------------------------------------------------------------------------- def closedPrefsConfigUi(self, values, canceled): if not canceled: self.debug = values.get("debug", False) # --------------------------------------------------------------------------- def validateActionConfigUi(self, values, typeId, devId): errors = indigo.Dict() # building a description for the Indigio UI... priority = values["priority"] header = "Prowl [" + priority + "]: " # the title is not required, but check substitutions if it is there... title = values.get("title", "") if len(title) > 0: subst = self.substitute(title, validateOnly=True) if subst[0]: header += title + "-" else: errors["title"] = subst[1] # a message is required, and we'll verify substitutions message = values.get("message", "") if len(message) == 0: errors["message"] = "You must provide a message" else: subst = self.substitute(message, validateOnly=True) if not subst[0]: errors["message"] = subst[1] # create the description for Indigo's UI values["description"] = header + message return ((len(errors) == 0), values, errors) # --------------------------------------------------------------------------- def notify(self, action): # perform substitution on the title and message title = self.substitute(action.props.get("title", "")) message = self.substitute(action.props.get("message", "")) # construct the API call body params = urllib.urlencode( { "apikey": self.pluginPrefs.get("apikey", None), "priority": action.props.get("priority", "0"), "event": title, "description": message, "application": self.pluginPrefs.get("appname", "Indigo"), } ) self.debugLog("notify: %s" % params) # Prowl won't accept the POST unless it carries the right content type headers = {"Content-type": "application/x-www-form-urlencoded"} try: conn = httplib.HTTPSConnection("api.prowlapp.com") conn.request("POST", "/publicapi/add", params, headers) resp = conn.getresponse() # so we can see the results in the log... self.processStdResponse(resp) except Exception as e: self.errorLog(str(e)) # --------------------------------------------------------------------------- # verify the given API key is valid with Prowl def prowlVerify(self, apikey): params = urllib.urlencode({"apikey": apikey}) self.debugLog("verify: %s" % params) verified = False try: conn = httplib.HTTPConnection("api.prowlapp.com") conn.request("GET", "/publicapi/verify?" + params) resp = conn.getresponse() verified = self.processStdResponse(resp) except Exception as e: self.errorLog(str(e)) return verified # --------------------------------------------------------------------------- # returns True if the response represents success, False otherwise def processStdResponse(self, resp): self.debugLog("HTTP %d %s" % (resp.status, resp.reason)) root = ElementTree.fromstring(resp.read()) content = root[0] if content.tag == "success": remain = int(content.attrib["remaining"]) self.debugLog("success: %d calls remaining" % remain) elif content.tag == "error": self.errorLog("error: %s" % content.text) else: # just in case something strange comes along... raise Exception("unknown response", content.tag) return resp.status == 200
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater(self) self.apiVersion = "2.0" self.localAddress = "" # create empty device list self.deviceList = {} self.updateableList = {} self.unifiPlugin = None self.beaconPlugin = None def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(u"Started device: " + device.name) device.stateListOrDisplayStateIdChanged() self.addDeviceToList (device) def deviceStopComm(self,device): if device.id in self.deviceList: self.debugLog("Stoping device: " + device.name) self.deleteDeviceFromList(device) def deviceCreated(self, device): indigo.server.log (u"Created new device \"%s\" of type \"%s\"" % (device.name, device.deviceTypeId)) pass def deviceDeleted(self, device): indigo.server.log (u"Deleted device \"%s\" of type \"%s\"" % (device.name, device.deviceTypeId)) if device.id in self.deviceList: del self.deviceList[device.id] def addDeviceToList(self,device): if device: if device.id not in self.deviceList: statusNextTime = datetime.datetime.now() - datetime.timedelta(seconds=10) statusInterval = 600 #device.pluginProps["statusInterval"] self.deviceList[device.id] = { 'ref':device, 'statusInterval':statusInterval, 'statusNextTime': statusNextTime, 'analyze': False, 'analyzeNextTime': statusNextTime, 'lastSeen': 0, 'firstSeen': 0, 'onUnifi': False, 'onGeo1': False, 'onGeo2': False, 'onGeo3': False } self.addDeviceToUpdateable(device) def addDeviceToUpdateable(self,device): unifideviceid = int(device.pluginProps["unifidevice"]) geofencedevice1id = int(device.pluginProps["geofencedevice1"]) geofencedevice2id = int(device.pluginProps["geofencedevice2"]) geofencedevice3id = int(device.pluginProps["geofencedevice3"]) self.updateableList[unifideviceid] = {'parentDeviceId': device.id} self.updateableList[geofencedevice1id] = {'parentDeviceId': device.id} self.updateableList[geofencedevice2id] = {'parentDeviceId': device.id} self.updateableList[geofencedevice3id] = {'parentDeviceId': device.id} def deleteDeviceFromList(self, device): if device: if device.id in self.deviceList: del self.deviceList[device.id] self.deleteDeviceFromUpdateable(device) def deleteDeviceFromUpdateable(self,device): unifideviceid = int(device.pluginProps["unifidevice"]) geofencedevice1id = int(device.pluginProps["geofencedevice1"]) geofencedevice2id = int(device.pluginProps["geofencedevice2"]) geofencedevice3id = int(device.pluginProps["geofencedevice3"]) if unifideviceid in self.updateableList: del self.updateableList[unifideviceid] if geofencedevice1id in self.updateableList: del self.updateableList[geofencedevice1id] if geofencedevice2id in self.updateableList: del self.updateableList[geofencedevice2id] if geofencedevice3id in self.updateableList: del self.updateableList[geofencedevice3id] def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.unifiPlugin = indigo.server.getPlugin("com.tenallero.indigoplugin.unifi") self.beaconPlugin = indigo.server.getPlugin("se.furtenbach.indigo.plugin.beacon") if not self.unifiPlugin.isEnabled(): self.errorLog (u"Error: Unifi plugin is not enabled") if not self.beaconPlugin.isEnabled(): self.errorLog (u"Error: Beacon plugin is not enabled") self.updater.checkForUpdate() indigo.devices.subscribeToChanges() def shutdown(self): self.debugLog(u"shutdown called") def getDeviceConfigUiValues(self, pluginProps, typeId, devId): valuesDict = pluginProps errorMsgDict = indigo.Dict() return (valuesDict, errorMsgDict) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") return (True, valuesDict) def validatePrefsConfigUi(self, valuesDict): return (True, valuesDict) def closedDeviceConfigUi(self, valuesDict, userCancelled, typeId, devId): if userCancelled is False: indigo.server.log ("Device preferences were updated.") device = indigo.devices[devId] self.deleteDeviceFromList (device) self.addDeviceToList (device) def closedPrefsConfigUi ( self, valuesDict, UserCancelled): # If the user saves the preferences, reload the preferences if UserCancelled is False: indigo.server.log ("Preferences were updated, reloading Preferences...") self.loadPluginPrefs() def loadPluginPrefs(self): # set debug option if 'debugEnabled' in self.pluginPrefs: self.debug = self.pluginPrefs['debugEnabled'] else: self.debug = False def menuGetDevsUnifi(self, filter, valuesDict, typeId, elemId): menuList = [] for dev in indigo.devices.iter(filter="com.tenallero.indigoplugin.unifi.unifiuser"): if dev.enabled: menuList.append((dev.id, dev.name)) return menuList def menuGetDevsPing(self, filter, valuesDict, typeId, elemId): menuList = [] for dev in indigo.devices.iter(filter="com.tenallero.indigoplugin.ping.pingdevice"): if dev.enabled: menuList.append((dev.id, dev.name)) return menuList def menuGetDevsGeofence(self, filter, valuesDict, typeId, elemId): menuList = [] for dev in indigo.devices.iter(filter="se.furtenbach.indigo.plugin.beacon.beacon"): if dev.enabled: menuList.append((dev.id, dev.name)) return menuList def deviceUpdated (self, origDev, newDev): if origDev.id in self.updateableList: if not origDev.states['onOffState'] == newDev.states['onOffState']: parentDeviceId = int(self.updateableList[origDev.id]["parentDeviceId"]) if parentDeviceId in self.deviceList: msg = u'device "' + origDev.name + u'" has been updated. Now is ' if newDev.states['onOffState']: msg += u'on.' else: msg += u'off.' self.debugLog(msg) #indigo.server.log (msg) self.deviceList[parentDeviceId]['statusNextTime'] = datetime.datetime.now() - datetime.timedelta(seconds=10) ################################################################### # Concurrent Thread. ################################################################### def runConcurrentThread(self): self.debugLog(u"Starting Concurrent Thread") try: while self.stopThread == False: indigoDevice = None try: todayNow = datetime.datetime.now() for presenceDevice in self.deviceList: if self.deviceList[presenceDevice]['statusInterval'] > 0: statusNextTime = self.deviceList[presenceDevice]['statusNextTime'] if statusNextTime <= todayNow: statusInterval = self.deviceList[presenceDevice]['statusInterval'] statusNextTime = todayNow + datetime.timedelta(seconds=int(statusInterval)) self.deviceList[presenceDevice]['statusNextTime'] = statusNextTime indigoDevice = self.deviceList[presenceDevice]['ref'] self.deviceList[presenceDevice]['analyze'] = True self.deviceList[presenceDevice]['analyzeNextTime'] = todayNow + datetime.timedelta(seconds=1) self.debugLog(u'ConcurrentThread. Sent "' + indigoDevice.name + '" status request') self.deviceRequestStatus(indigoDevice) self.debugLog(u'ConcurrentThread. Received "' + indigoDevice.name + '" status') if self.deviceList[presenceDevice]['analyze']: analyzeNextTime = self.deviceList[presenceDevice]['analyzeNextTime'] if analyzeNextTime <= todayNow: indigoDevice = self.deviceList[presenceDevice]['ref'] self.debugLog(u'ConcurrentThread. Analyzing "' + indigoDevice.name + '"') self.deviceList[presenceDevice]['analyze'] = False self.deviceAnalyzeStatus(indigoDevice) except Exception,e: self.errorLog (u"Error: " + str(e)) pass self.sleep(0.3) except self.StopThread: pass except Exception, e: self.errorLog (u"Error: " + str(e)) pass
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater(self) # Timeout self.reqTimeout = 8 # Pooling self.pollingInterval = 2 # Flag buttonRequest is processing self.reqRunning = False # create empty device list self.deviceList = {} # install authenticating opener self.passman = urllib2.HTTPPasswordMgrWithDefaultRealm() authhandler = urllib2.HTTPBasicAuthHandler(self.passman) opener = urllib2.build_opener(authhandler) urllib2.install_opener(opener) def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(device.name + ": Starting device") if device.id not in self.deviceList: self.deviceList[device.id] = { 'ref': device, 'lastTimeSensor': datetime.datetime.now() } if device.pluginProps.has_key( "useAuthentication" ) and device.pluginProps["useAuthentication"]: self.passman.add_password( None, u"http://" + device.pluginProps["address"], device.pluginProps["username"], device.pluginProps["password"]) self.sensorUpdateFromRequest(device) def deviceStopComm(self, device): if device.id not in self.deviceList: return self.debugLog(device.name + ": Stoping device") del self.deviceList[device.id] def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.reqRunning = False socket.setdefaulttimeout(self.reqTimeout) #self.debugLog("Pooling Interval: " + str(self.pollingInterval)) #self.debugLog("Request Timeout: " + str(self.reqTimeout)) self.updater.checkForUpdate() def shutdown(self): self.debugLog(u"shutdown called") def deviceCreated(self, device): self.debugLog(u"Created device of type \"%s\"" % device.deviceTypeId) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") ipAdr = valuesDict[u'address'] if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress(ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if (valuesDict[u'useAuthentication']): if not (valuesDict[u'username'] > ""): errorMsgDict = indigo.Dict() errorMsgDict[u'username'] = u"Must be filled." return (False, valuesDict, errorMsgDict) if not (valuesDict[u'password'] > ""): errorMsgDict = indigo.Dict() errorMsgDict[u'password'] = u"Must be filled." return (False, valuesDict, errorMsgDict) return (True, valuesDict) def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"validating Prefs called") return (True, valuesDict) def closedDeviceConfigUi(self, valuesDict, userCancelled, typeId, devId): if userCancelled is False: indigo.server.log("Device preferences were updated.") def closedPrefsConfigUi(self, valuesDict, UserCancelled): # If the user saves the preferences, reload the preferences if UserCancelled is False: indigo.server.log( "Preferences were updated, reloading Preferences...") self.loadPluginPrefs() def loadPluginPrefs(self): # set debug option if 'debugEnabled' in self.pluginPrefs: self.debug = self.pluginPrefs['debugEnabled'] else: self.debug = False self.pollingInterval = 0 self.reqTimeout = 0 #if self.pluginPrefs.has_key("pollingInterval"): # self.pollingInterval = int(self.pluginPrefs["pollingInterval"]) #if self.pollingInterval <= 0: # self.pollingInterval = 30 #if self.pluginPrefs.has_key("reqTimeout"): # self.reqTimeout = int(self.pluginPrefs['reqTimeout']) #if self.reqTimeout <= 0: # self.reqTimeout = 8 self.reqTimeout = 8 def validateAddress(self, value): try: socket.inet_aton(value) except socket.error: return False return True ################################################################### # Concurrent Thread ################################################################### def runConcurrentThread(self): self.debugLog(u"Starting polling thread") try: while True: if self.reqRunning == False: todayNow = datetime.datetime.now() for deviceId in self.deviceList: if deviceId in indigo.devices: pollingInterval = 0 state = indigo.devices[deviceId].states[ "RoombaState"] #self.deviceList[deviceId]['ref'].states["RoombaState"] lastTimeSensor = self.deviceList[deviceId][ 'lastTimeSensor'] if state == "clean": pollingInterval = 3 elif state == "stop": pollingInterval = 3 else: pollingInterval = 120 nextTimeSensor = lastTimeSensor + datetime.timedelta( seconds=pollingInterval) if nextTimeSensor <= todayNow: self.debugLog("Thread. Roomba State = " + state) self.debugLog("Thread. Pooling interval = " + str(pollingInterval)) self.deviceList[deviceId][ 'lastTimeSensor'] = todayNow if self.reqRunning == False: self.sensorUpdateFromThread( indigo.devices[deviceId]) self.sleep(0.5) except self.StopThread: # cleanup pass self.debugLog(u"Exited polling thread") def stopConcurrentThread(self): self.stopThread = True self.debugLog(u"stopConcurrentThread called") ################################################################### # HTTP Request against RooWIFI. ################################################################### def sendRequest(self, device, urlAction): self.reqRunning = True requestTrial = 0 requestMax = 3 requestOK = False theUrl = u"http://" + device.pluginProps["address"] + urlAction self.debugLog("sending " + theUrl) while (requestTrial < requestMax) and (requestOK == False): try: f = urllib2.urlopen(theUrl) requestOK = True except Exception, e: requestTrial += 1 lastError = str(e) if (requestOK == False): self.errorLog(device.name + ": Error: " + lastError) self.errorLog(device.name + " did not received the request !") self.reqRunning = False return False self.sleep(1) self.sensorUpdateFromRequest(device) self.reqRunning = False return True
class Plugin(indigo.PluginBase): #------------------------------------------------------------------------------- def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater(self) #------------------------------------------------------------------------------- def __del__(self): indigo.PluginBase.__del__(self) #------------------------------------------------------------------------------- # Start and Stop #------------------------------------------------------------------------------- def startup(self): self.nextCheck = self.pluginPrefs.get('nextUpdateCheck', 0) self.debug = self.pluginPrefs.get('showDebugInfo', False) if self.debug: self.logger.debug("Debug logging enabled") global MIN_STEP_TIME MIN_STEP_TIME = self.pluginPrefs.get('minStepTime', 0.2) self.control = Control(self) self.airplay = Airplay(self) self.fader = Fader(self) #------------------------------------------------------------------------------- def shutdown(self): self.pluginPrefs['nextUpdateCheck'] = self.nextCheck self.pluginPrefs['showDebugInfo'] = self.debug self.pluginPrefs['minStepTime'] = MIN_STEP_TIME self.fader.cancel() #------------------------------------------------------------------------------- def runConcurrentThread(self): try: while True: if time.time() > self.nextCheck: self.checkForUpdates() self.sleep(600) except self.StopThread: pass # Optionally catch the StopThread exception and do any needed cleanup. #------------------------------------------------------------------------------- # Config and Validate #------------------------------------------------------------------------------- def closedPrefsConfigUi(self, valuesDict, userCancelled): if not userCancelled: self.debug = valuesDict.get('showDebugInfo', False) self.logger.debug("Debug logging {}".format( ["disabled", "enabled"][self.debug])) global MIN_STEP_TIME MIN_STEP_TIME = self.pluginPrefs.get('minStepTime', 0.2) #------------------------------------------------------------------------------- def validatePrefsConfigUi(self, valuesDict): errorsDict = indigo.Dict() try: valuesDict['minStepTime'] = round(float(valuesDict['minStepTime']), 4) if not (0.1 <= valuesDict['minStepTime'] <= 1): raise ValueError except: errorsDict['minStepTime'] = 'Must be number between 0.1 and 1.0' if len(errorsDict) > 0: return (False, valuesDict, errorsDict) return (True, valuesDict) #------------------------------------------------------------------------------- def validateActionConfigUi(self, valuesDict, typeId, devId): errorsDict = indigo.Dict() positiveIntegerFields = ['duration', 'trackNumber'] otherRequiredFields = [ 'variable', 'playlist', 'trackName', 'device', 'status', 'specifier', 'applescriptText' ] for key, value in valuesDict.items(): if key == 'volume': if not validateTextFieldNumber(value, numberType=int, zeroAllowed=True, negativeAllowed=False, maximumAllowed=100): errorsDict[key] = "Must be an integer between 0 and 100" elif key in positiveIntegerFields: if not validateTextFieldNumber(value, numberType=int, zeroAllowed=False, negativeAllowed=False): errorsDict[key] = "Must be a positive integer" elif key in otherRequiredFields: if value == "": errorsDict[key] = "Required" if len(errorsDict) > 0: self.logger.debug(u'\n{}'.format(valuesDict)) return (False, valuesDict, errorsDict) else: return (True, valuesDict) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Action Methods #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def launch(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.onState = True #------------------------------------------------------------------------------- def quit(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.onState = False #------------------------------------------------------------------------------- def toggle(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.onState = not self.onState #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def play(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.control.play() #------------------------------------------------------------------------------- def pause(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.control.pause() #------------------------------------------------------------------------------- def playpause(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.control.playpause() #------------------------------------------------------------------------------- def stop(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.control.stop() #------------------------------------------------------------------------------- def next(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.control.next() #------------------------------------------------------------------------------- def prev(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.control.prev() #------------------------------------------------------------------------------- def back(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.control.back() #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def setVolume(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['volume'])) self.volume = action.props['volume'] #------------------------------------------------------------------------------- def increaseVolume(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['volume'])) self.volume += int(action.props['volume']) #------------------------------------------------------------------------------- def decreaseVolume(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['volume'])) self.volume -= int(action.props['volume']) #------------------------------------------------------------------------------- def volumeToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.volume) #------------------------------------------------------------------------------- def volumeFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.volume = variable_get(action.props['variable'], int) #------------------------------------------------------------------------------- def fadeVolumeTo(self, action): self.logger.debug(u'action "{}": {}/{}s'.format( action.description, action.props['volume'], action.props['duration'])) self.fadeStart(action.props['volume'], action.props['duration']) #------------------------------------------------------------------------------- def fadeVolumeUp(self, action): self.logger.debug(u'action "{}": {}/{}s'.format( action.description, action.props['volume'], action.props['duration'])) self.fadeStart(self.volume + int(action.props['volume']), action.props['duration']) #------------------------------------------------------------------------------- def fadeVolumeDown(self, action): self.logger.debug(u'action "{}": {}/{}s'.format( action.description, action.props['volume'], action.props['duration'])) self.fadeStart(self.volume - int(action.props['volume']), action.props['duration']) #------------------------------------------------------------------------------- def fadeFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.fadeStart(variable_get(action.props['variable'], int), action.props['duration']) #------------------------------------------------------------------------------- def fadeStart(self, volume, duration): self.fader.fade(volume, duration) #------------------------------------------------------------------------------- def fadeStop(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.fader.stop() #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def playPlaylist(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['playlist'])) self.shuffle_state = False self.playlist = action.props['playlist'] #------------------------------------------------------------------------------- def playPlaylistShuffled(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['playlist'])) self.shuffle_mode = 'songs' self.shuffle_state = True self.playlist = action.props['playlist'] #------------------------------------------------------------------------------- def playlistToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.playlist) #------------------------------------------------------------------------------- def playlistFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.playlist = variable_get(action.props['variable']) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def playSingleTrackPlaylistNumber(self, action): self.logger.debug(u'action "{}": {} {}'.format( action.description, action.props['playlist'], int(action.props['trackNumber']))) itunes.play_single_track(action.props['playlist'], int(action.props['trackNumber'])) #------------------------------------------------------------------------------- def playSingleTrackPlaylistName(self, action): self.logger.debug(u'action "{}": {} {}'.format( action.description, action.props['playlist'], action.props['trackName'])) itunes.play_single_track(action.props['playlist'], action.props['trackName']) #------------------------------------------------------------------------------- def playSingleTrackPlaylistRandom(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['playlist'])) itunes.play_single_track(action.props['playlist'], None) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def shuffleStateOn(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.shuffle_state = True #------------------------------------------------------------------------------- def shuffleStateOff(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.shuffle_state = False #------------------------------------------------------------------------------- def shuffleStateToggle(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.shuffle_state = not self.shuffle_state #------------------------------------------------------------------------------- def shuffleStateToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.shuffle_state) #------------------------------------------------------------------------------- def shuffleStateFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.shuffle_state = variable_get(action.props['variable'], bool) #------------------------------------------------------------------------------- def shuffleModeSongs(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.shuffle_mode = 'songs' #------------------------------------------------------------------------------- def shuffleModeAlbums(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.shuffle_mode = 'albums' #------------------------------------------------------------------------------- def shuffleModeGroupings(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.shuffle_mode = 'groupings' #------------------------------------------------------------------------------- def shuffleModeToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.shuffle_mode) #------------------------------------------------------------------------------- def shuffleModeFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.shuffle_mode = variable_get(action.props['variable']) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def repeatOff(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.repeat = 'off' #------------------------------------------------------------------------------- def repeatOne(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.repeat = 'one' #------------------------------------------------------------------------------- def repeatAll(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.repeat = 'all' #------------------------------------------------------------------------------- def repeatToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.repeat) #------------------------------------------------------------------------------- def repeatFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.repeat = variable_get(action.props['variable']) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def eqStateOn(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.eq_state = True #------------------------------------------------------------------------------- def eqStateOff(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.eq_state = False #------------------------------------------------------------------------------- def eqStateToggle(self, action): self.logger.debug(u'action "{}"'.format(action.description)) self.eq_state = not self.eq_state #------------------------------------------------------------------------------- def eqStateToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.eq_state) #------------------------------------------------------------------------------- def eqStateFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.eq_state = variable_get(action.props['variable'], bool) #------------------------------------------------------------------------------- def eqPresetSet(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['preset'])) self.eq_preset = action.props['preset'] #------------------------------------------------------------------------------- def eqPresetToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.eq_preset) #------------------------------------------------------------------------------- def eqPresetFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.eq_preset = variable_get(action.props['variable']) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def airplayDeviceStatus(self, action): self.logger.debug(u'action "{}": {}@{} {}'.format( action.description, action.props['device'], action.props['volume'], action.props['status'])) self.airplay.device( action.props['device']).active = action.props['status'] self.airplay.device( action.props['device']).volume = action.props['volume'] #------------------------------------------------------------------------------- def airplayDeviceAdd(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['device'])) self.airplay.device(action.props['device']).active = True #------------------------------------------------------------------------------- def airplayDeviceRemove(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['device'])) self.airplay.device(action.props['device']).active = False #------------------------------------------------------------------------------- def airplayDeviceToggle(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['device'])) self.airplay.device(action.props['device']).toggle() #------------------------------------------------------------------------------- def airplayDeviceVolume(self, action): self.logger.debug(u'action "{}": {}@{}'.format(action.description, action.props['device'], action.props['volume'])) self.airplay.device( action.props['device']).volume = action.props['volume'] #------------------------------------------------------------------------------- def airplayDevicesGroup(self, action): self.logger.debug(u'action "{}": {}'.format( action.description, list(action.props['devices']))) self.airplay.active_devices = action.props['devices'] #------------------------------------------------------------------------------- def airplayDevicesToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) devices = self.airplay.active_devices if action.props['convert']: devices = ', '.join(devices) variable_set(action.props['variable'], devices) #------------------------------------------------------------------------------- def airplayDevicesFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.airplay.active_devices = variable_get(action.props['variable'], list) #------------------------------------------------------------------------------- def currentSettingsToVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) variable_set(action.props['variable'], self.settings) #------------------------------------------------------------------------------- def currentSettingsFromVariable(self, action): self.logger.debug(u'action "{}": {}'.format(action.description, action.props['variable'])) self.settings = variable_get(action.props['variable'], dict) #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ def playApplescriptSpecifier(self, action): itunes.playApplescriptSpecifier(action.props['specifier']) #------------------------------------------------------------------------------- def executeApplescriptText(self, action): itunes.executeApplescriptText(action.props['applescriptText']) #------------------------------------------------------------------------------- # Menu Methods #------------------------------------------------------------------------------- def checkForUpdates(self): self.nextCheck = time.time() + (kPluginUpdateCheckHours * 60 * 60) try: self.updater.checkForUpdate() except Exception as e: msg = 'Check for update error. Next attempt in {} hours.'.format( kPluginUpdateCheckHours) if self.debug: self.logger.exception(msg) else: self.logger.error(msg) self.logger.debug(e) #------------------------------------------------------------------------------- def updatePlugin(self): self.updater.update() #------------------------------------------------------------------------------- def forceUpdate(self): self.updater.update(currentVersion='0.0.0') #------------------------------------------------------------------------------- def toggleDebug(self): if self.debug: self.logger.debug("Debug logging disabled") self.debug = False else: self.debug = True self.logger.debug("Debug logging enabled") #------------------------------------------------------------------------------- # Menu Callbacks #------------------------------------------------------------------------------- def menu_airplay_devices(self, filter='', valuesDict=dict(), typeId='', targetId=0): return [(item, item) for item in self.airplay.all_devices] #------------------------------------------------------------------------------- def menu_playlists(self, filter='', valuesDict=dict(), typeId='', targetId=0): return [(item, item) for item in self.playlists] #------------------------------------------------------------------------------- def menu_eq_presets(self, filter='', valuesDict=dict(), typeId='', targetId=0): return [(item, item) for item in self.eq_presets] #------------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------------- def _onState_get(self): value = itunes.running() self.logger.debug(u'get running: {}'.format(value)) return value def _onState_set(self, value): self.logger.debug(u'set running: {}'.format(value)) if value: itunes.launch() else: itunes.quit() onState = property(_onState_get, _onState_set) #------------------------------------------------------------------------------- def _volume_get(self): value = itunes.volume_get() self.logger.debug(u'get volume: {}'.format(value)) return value def _volume_set(self, value): value = normalize_volume(value) self.fader.stop() self.logger.debug(u'set volume: {}'.format(value)) itunes.volume_set(value) volume = property(_volume_get, _volume_set) #------------------------------------------------------------------------------- @property def playlists(self): value = itunes.playlists() self.logger.debug(u'all playlists: {}'.format(value)) return value #------------------------------------------------------------------------------- def _playlist_get(self): value = itunes.playlist_current() self.logger.debug(u'get playlist: {}'.format(value)) return value def _playlist_set(self, value): self.logger.debug(u'set playlist: {}'.format(value)) itunes.playlist_play(value) playlist = property(_playlist_get, _playlist_set) #------------------------------------------------------------------------------- def _shuffle_state_get(self): value = itunes.shuffle_state_get() self.logger.debug(u'get shuffle state: {}'.format(value)) return value def _shuffle_state_set(self, value): self.logger.debug(u'set shuffle state: {}'.format(value)) itunes.shuffle_state_set(value) shuffle_state = property(_shuffle_state_get, _shuffle_state_set) #------------------------------------------------------------------------------- def _shuffle_mode_get(self): value = itunes.shuffle_mode_get() self.logger.debug(u'get shuffle mode: {}'.format(value)) return value def _shuffle_mode_set(self, value): self.logger.debug(u'set shuffle mode: {}'.format(value)) itunes.shuffle_mode_set(value) shuffle_mode = property(_shuffle_mode_get, _shuffle_mode_set) #------------------------------------------------------------------------------- def _repeat_get(self): value = itunes.repeat_get() self.logger.debug(u'get repeat: {}'.format(value)) return value def _repeat_set(self, value): self.logger.debug(u'set repeat: {}'.format(value)) itunes.repeat_set(value) repeat = property(_repeat_get, _repeat_set) #------------------------------------------------------------------------------- def _eq_state_get(self): value = itunes.eq_state_get() self.logger.debug(u'get eq state: {}'.format(value)) return value def _eq_state_set(self, value): self.logger.debug(u'set eq state: {}'.format(value)) itunes.eq_state_set(value) eq_state = property(_eq_state_get, _eq_state_set) #------------------------------------------------------------------------------- @property def eq_presets(self): value = itunes.eq_presets() self.logger.debug(u'all eq presets: {}'.format(value)) return value #------------------------------------------------------------------------------- def _eq_preset_get(self): value = itunes.eq_preset_get() self.logger.debug(u'get eq preset: {}'.format(value)) return value def _eq_preset_set(self, value): self.logger.debug(u'set eq preset: {}'.format(value)) itunes.eq_preset_set(value) eq_preset = property(_eq_preset_get, _eq_preset_set) #------------------------------------------------------------------------------- @property def player_state(self): value = itunes.player_state_get() self.logger.debug(u'get player state: {}'.format(value)) return value #------------------------------------------------------------------------------- def _settings_get(self): active_devices = self.airplay.active_devices airplay_volume = dict() for deviceName in active_devices: airplay_volume[deviceName] = self.airplay.device(deviceName).volume settings = { 'volume': self.volume, 'playlist': self.playlist, 'shuffle_state': self.shuffle_state, 'shuffle_mode': self.shuffle_mode, 'repeat': self.repeat, 'eq_state': self.eq_state, 'eq_preset': self.eq_preset, 'player_state': self.player_state, 'active_devices': active_devices, 'airplay_volume': airplay_volume } return settings def _settings_set(self, settings): self.volume = settings['volume'] self.shuffle_state = settings['shuffle_state'] self.shuffle_mode = settings['shuffle_mode'] self.repeat = settings['repeat'] self.eq_state = settings['eq_state'] self.eq_preset = settings['eq_preset'] self.airplay.active_devices = settings['active_devices'] for name, volume in settings['airplay_volume'].items(): if settings['volume'] == 0: self.airplay.device(name).volume = 0 else: self.airplay.device(name).volume = int( round(volume * 100.0 / settings['volume'])) if settings['playlist'] in self.playlists: if settings['player_state'] in ['playing', 'paused']: itunes.playlist_play(settings['playlist']) if settings['player_state'] == 'paused': itunes.pause() settings = property(_settings_get, _settings_set)
class Plugin(indigo.PluginBase): ######################################## # Main Plugin methods ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = self.pluginPrefs.get(u"showDebugInfo", False) self.debugLog(u"Debugging enabled") self.apiData = { "chamberlain" : { "service" : "https://myqexternal.myqdevice.com", # "appID" : "Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB%2fi" "appID" : "Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB/i" }, "craftsman" : { "service" : "https://craftexternal.myqdevice.com", "appID" : "eU97d99kMG4t3STJZO/Mu2wt69yTQwM0WXZA5oZ74/ascQ2xQrLD/yjeVhEQccBZ" }, "liftmaster" : { "service" : "https://myqexternal.myqdevice.com", # "appID" : "Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB%2fi" "appID" : "Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB/i" }, } self.triggers = { } def __del__(self): indigo.PluginBase.__del__(self) def startup(self): indigo.server.log(u"Starting MyQ") self.updater = GitHubPluginUpdater(self) self.updateFrequency = float(self.pluginPrefs.get('updateFrequency', "24")) * 60.0 * 60.0 if self.updateFrequency > 0: self.next_update_check = time.time() self.statusFrequency = float(self.pluginPrefs.get('statusFrequency', "10")) * 60.0 if self.statusFrequency > 0: self.next_status_check = time.time() def shutdown(self): indigo.server.log(u"Shutting down MyQ") def runConcurrentThread(self): try: while True: if self.updateFrequency > 0: if time.time() > self.next_update_check: self.updater.checkForUpdate() self.next_update_check = time.time() + self.updateFrequency if self.statusFrequency > 0: if time.time() > self.next_status_check: self.getDevices() self.next_status_check = time.time() + self.statusFrequency self.sleep(1.0) except self.stopThread: pass #################### def triggerStartProcessing(self, trigger): self.debugLog("Adding Trigger %s (%d) - %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) assert trigger.id not in self.triggers self.triggers[trigger.id] = trigger def triggerStopProcessing(self, trigger): self.debugLog("Removing Trigger %s (%d)" % (trigger.name, trigger.id)) assert trigger.id in self.triggers del self.triggers[trigger.id] def triggerCheck(self, device): for triggerId, trigger in sorted(self.triggers.iteritems()): self.debugLog("\tChecking Trigger %s (%s), Type: %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) #################### # def deviceStartComm(self, device): # self.debugLog(u'Called deviceStartComm(self, device): %s (%s)' % (device.name, device.id)) # instanceVers = int(device.pluginProps.get('devVersCount', 0)) # self.debugLog(device.name + u": Device Current Version = " + str(instanceVers)) # if instanceVers >= kCurDevVersCount: # self.debugLog(device.name + u": Device Version is up to date") # elif instanceVers < kCurDevVersCount: # newProps = device.pluginProps # else: # self.errorLog(u"Unknown device version: " + str(instanceVers) + " for device " + device.name) # def deviceStopComm(self, device): # self.debugLog(u'Called deviceStopComm(self, device): %s (%s)' % (device.name, device.id)) ######################################## # Menu Methods ######################################## def toggleDebugging(self): self.debug = not self.debug self.pluginPrefs["debugEnabled"] = self.debug indigo.server.log("Debug set to: " + str(self.debug)) def checkForUpdates(self): self.updater.checkForUpdate() def updatePlugin(self): self.updater.update() def forceUpdate(self): self.updater.update(currentVersion='0.0.0') ######################################## # ConfigUI methods ######################################## def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"validatePrefsConfigUi called") errorDict = indigo.Dict() if len(valuesDict['myqLogin']) < 5: errorDict['myqLogin'] = u"Enter your MyQ login name (email address)" if len(valuesDict['myqPassword']) < 1: errorDict['myqPassword'] = u"Enter your MyQ login password" statusFrequency = int(valuesDict['statusFrequency']) if (statusFrequency < 5) or (statusFrequency > (24 * 60)): errorDict['statusFrequency'] = u"Status frequency must be at least 5 min and less than 24 hours" updateFrequency = int(valuesDict['updateFrequency']) if (updateFrequency < 0) or (updateFrequency > 24): errorDict['updateFrequency'] = u"Update frequency is invalid - enter a valid number (between 0 and 24)" if len(errorDict) > 0: return (False, valuesDict, errorDict) return (True, valuesDict) def closedPrefsConfigUi(self, valuesDict, userCancelled): if not userCancelled: self.debug = valuesDict.get("showDebugInfo", False) if self.debug: self.debugLog(u"Debug logging enabled") else: self.debugLog(u"Debug logging disabled") # def validateDeviceConfigUi(self, valuesDict, typeId, devId): # self.debugLog(u'Called validateDeviceConfigUi, valuesDict = %s, typeId = %s, devId = %s' % (str(valuesDict), typeId, devId)) # errorsDict = indigo.Dict() # if int(valuesDict["address"]) < 1: # errorDict['address'] = u"Invalid Device ID Number" # if len(errorsDict) > 0: # return (False, valuesDict, errorsDict) # return (True, valuesDict) # def validateActionConfigUi(self, valuesDict, typeId, devId): # self.debugLog(u'Called validateActionConfigUi, valuesDict = %s, typeId = %s, devId = %s' % (str(valuesDict), typeId, devId)) # errorsDict = indigo.Dict() # try: # pass # except: # pass # if len(errorsDict) > 0: # return (False, valuesDict, errorsDict) # return (True, valuesDict) ######################################## def actionControlDimmerRelay(self, action, dev): if action.deviceAction == indigo.kDeviceAction.TurnOn: self.debugLog(u"actionControlDimmerRelay: \"%s\" On" % dev.name) self.changeDevice(dev, kDoorOpen) elif action.deviceAction == indigo.kDeviceAction.TurnOff: self.debugLog(u"actionControlDimmerRelay: \"%s\" Off" % dev.name) self.changeDevice(dev, kDoorClosed) elif action.deviceAction == indigo.kDeviceAction.Toggle: self.debugLog(u"actionControlDimmerRelay: \"%s\" Toggle" % dev.name) if dev.isOn: self.changeDevice(dev, kDoorClosed) else: self.changeDevice(dev, kDoorOpen) elif action.deviceAction == indigo.kDeviceAction.RequestStatus: self.debugLog(u"actionControlDimmerRelay: \"%s\" Request Status" % dev.name) self.getDevices() ######################################## def myqLogin(self): self.username = self.pluginPrefs.get('myqLogin', None) self.password = self.pluginPrefs.get('myqPassword', None) self.brand = self.pluginPrefs.get('openerBrand', None) if (self.brand): self.service = self.apiData[self.brand]["service"] self.appID = self.apiData[self.brand]["appID"] # self.logger.debug(u"myqLogin Info, username = %s, password length = %d, brand = %s, service = %s, appID = %s" % (self.username, len(self.password), self.brand, self.service, self.appID)) # url = self.service + '/Membership/ValidateUserWithCulture?appid=' + self.appID + '&securityToken=null&username='******'&password='******'&culture=en' payload = {'appId': self.appID, 'securityToken': 'null', 'username': self.username, 'password': self.password, 'culture': 'en'} login_url = self.service + '/Membership/ValidateUserWithCulture' headers = {'User-Agent': userAgent} try: # response = requests.get(url) response = requests.get(login_url, params=payload, headers=headers) self.logger.debug(u"myqLogin: response = " + str(response)) self.logger.debug(u"myqLogin: content = " + str(response.text)) except requests.exceptions.RequestException as err: self.logger.debug(u"myqLogin failure: RequestException: " + str(err)) self.securityToken = "" return try: data = response.json() except ValueError as err: self.logger.debug(u"myqLogin failure: JSON Decode Error: " + str(err)) self.securityToken = "" return if data['ReturnCode'] != '0': self.debugLog(u"myqLogin failure: Bad return code: " + data['ErrorMessage']) self.securityToken = "" return self.securityToken = data['SecurityToken'] self.debugLog(u"myqLogin: Success, SecurityToken = %s" % (self.securityToken)) ######################################## def getDevices(self): self.myqLogin() if not self.securityToken: return url = self.service + '/api/UserDeviceDetails' params = {'appId':self.appID, 'securityToken':self.securityToken} headers = {'User-Agent': userAgent } try: response = requests.get(url, params=params, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"getDevices: RequestException: " + str(err)) return data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"getDevices: Bad return code: " + data['ErrorMessage']) return self.debugLog(u"getDevices: %d Devices" % len(data['Devices'])) for device in data['Devices']: self.debugLog(u"getDevices: MyQDeviceTypeId = %s, DeviceId = %s" % (device['MyQDeviceTypeId'], device['DeviceId'])) if (device['MyQDeviceTypeId'] == 2) or (device['MyQDeviceTypeId'] == 5) or (device['MyQDeviceTypeId'] == 7): # MyQDeviceTypeId Door == 2, Gate == 5, Door? == 7 myqID = device['DeviceId'] name = self.getDeviceName(myqID) state = self.getDeviceState(myqID) if state > 7: self.errorLog(u"getDevices: Opener %s (%s), state out of range: %i" % (name, myqID, state)) state = 0 # unknown high states else: self.debugLog(u"getDevices: Opener %s (%s), state = %i" % (name, myqID, state)) # look for this opener device in the existing devices for this plugin. If it's not there (by id), then create it iterator = indigo.devices.iter(filter="com.flyingdiver.indigoplugin.myq") for dev in iterator: if dev.address == myqID: dev.updateStateOnServer(key="doorStatus", value=doorStateNames[int(state)]) if state == 2: dev.updateStateOnServer(key="onOffState", value=False) # closed is off else: dev.updateStateOnServer(key="onOffState", value=True) # anything other than closed is "on" break else: # Python syntax weirdness - this else belongs to the for loop! newdev = indigo.device.create(protocol=indigo.kProtocol.Plugin, address=myqID, description = "Opener Device auto-created by MyQ plugin from gateway information", deviceTypeId='myqOpener', name=name) newdev.updateStateOnServer(key="doorStatus", value=doorStateNames[int(state)]) self.debugLog(u'Created New Opener Device: %s (%s)' % (newdev.name, newdev.address)) elif device['MyQDeviceTypeId'] == 3: # Switch == 3? myqID = device['DeviceId'] name = self.getDeviceName(myqID) state = self.getDeviceState(myqID) # self.debugLog(u"getDevices: Switch = %s (%s), data = %s" % (name, myqID, str(device))) # look for this opener device in the existing devices for this plugin. If it's not there (by id), then create it iterator = indigo.devices.iter(filter="com.flyingdiver.indigoplugin.myq") for dev in iterator: if dev.address == myqID: break else: # Python syntax weirdness - this else belongs to the for loop! newdev = indigo.device.create(protocol=indigo.kProtocol.Plugin, address=myqID, description = "Switch Device auto-created by MyQ plugin from gateway information", deviceTypeId='myqSwitch', name=name) # newdev.updateStateOnServer(key="doorStatus", value=doorStateNames[int(state)]) self.debugLog(u'Created New Switch Device: %s (%s)' % (newdev.name, newdev.address)) def getDeviceName(self, doorID): url = self.service + '/Device/getDeviceAttribute' params = {'appId': self.appID, 'securityToken': self.securityToken, 'devId': doorID, 'name':'desc'} headers = {'User-Agent': userAgent} try: response = requests.get(url, params=params, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"getDeviceName: RequestException: " + str(err)) return "" data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"getDeviceName: Bad return code: " + data['ErrorMessage']) return "" return data['AttributeValue'] def getDeviceState(self, doorID): url = self.service + '/Device/getDeviceAttribute' params = {'appID': self.appID, 'securityToken': self.securityToken, 'devId': doorID, 'name':'doorstate'} headers = {'User-Agent': userAgent} try: response = requests.get(url, params=params, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"getDeviceState: RequestException: " + str(err)) return 0 data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"getDeviceState: Bad return code: " + data['ErrorMessage']) return 0 return int(data['AttributeValue']) ######################################## def changeDeviceAction(self, pluginAction): if pluginAction != None: myqDevice = indigo.devices[pluginAction.deviceId] myqActionId = pluginAction.pluginTypeId if myqActionId == "openDoor": self.changeDevice(myqDevice, kDoorOpen) elif myqActionId == "closeDoor": self.changeDevice(myqDevice, kDoorClosed) elif myqActionId == "switchOn": self.changeDevice(myqDevice, kSwitchOn) elif myqActionId == "switchOff": self.changeDevice(myqDevice, kSwitchOff) else: self.debugLog(u"changeDeviceAction, unknown myqActionId = %s" % myqActionId) def changeDevice(self, device, state): self.debugLog(u"changeDevice: %s, state = %d" % (device.name, state)) self.myqLogin() payload = { 'ApplicationId': self.appID, 'AttributeName': 'desireddoorstate', 'DeviceId': device.address, 'AttributeValue': state, 'SecurityToken': self.securityToken } url = self.service + '/api/deviceattribute/putdeviceattribute' headers = {'User-Agent': userAgent} try: response = requests.put(url, data=payload, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"changeDevice: RequestException: " + str(err)) return data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"changeDevice: Bad return code: " + data['ErrorMessage']) # schedule an update to check on the movement self.next_status_check = time.time() + 30.0
class Plugin(indigo.PluginBase): #--------------------------------------------------------------------------- def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = pluginPrefs.get('debug', False) self.updater = GitHubPluginUpdater(self) #--------------------------------------------------------------------------- def __del__(self): indigo.PluginBase.__del__(self) #--------------------------------------------------------------------------- def selfInstall(self): self.updater.install() #--------------------------------------------------------------------------- def forceUpdate(self): self.updater.update(currentVersion='0.0.0') #--------------------------------------------------------------------------- def updatePlugin(self): self.updater.update() #--------------------------------------------------------------------------- def checkForUpdates(self): self.updater.checkForUpdate() #--------------------------------------------------------------------------- def checkRateLimit(self): limiter = self.updater.getRateLimit() indigo.server.log('RateLimit {limit:%d remaining:%d resetAt:%d}' % limiter) #--------------------------------------------------------------------------- def testUpdateCheck(self): indigo.server.log('-- BEGIN testUpdateCheck --') self.updater.checkForUpdate() self.updater.checkForUpdate('0.0.0') emptyUpdater = GitHubPluginUpdater() emptyUpdater.checkForUpdate() emptyUpdater.checkForUpdate('0.0.0') emptyUpdater.checkForUpdate(str(self.pluginVersion)) indigo.server.log('-- END testUpdateCheck --') #--------------------------------------------------------------------------- def toggleDebugging(self): self.debug = not self.debug self.pluginPrefs['debug'] = self.debug #--------------------------------------------------------------------------- def closedPrefsConfigUi(self, values, canceled): if (not canceled): self.debug = values.get('debug', False) #--------------------------------------------------------------------------- def runConcurrentThread(self): while True: # this checks for any updates on a regular interval self.updater.checkForUpdate() # we are checking every 300 seconds (5 minutes) here as an example # in practice, this should not be less than 3600 seconds (1 hour) self.sleep(300)
class Plugin(indigo.PluginBase): ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): super(Plugin, self).__init__(pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = pluginPrefs.get("debug", False) self.PurpleAir = PurpleAir(self) self.retryCount = 0 self.keepProcessing = True self.restartCount = 0 self.deviceList = {} def _refreshStatesFromHardware(self, dev): #self.debugLog(u"Getting data for Sensor : %s" % dev) if dev.deviceTypeId != "RemotePurpleAirSensor": #self.debugLog(u"Getting data for Sensor : %s" % dev.address) data = PurpleAir.GetData(self.PurpleAir,dev.address) else: #self.debugLog(u"Getting data for Remote Sensor : %s with ID %s" % (dev.name,dev.address)) data = PurpleAir.GetRemoteData(self.PurpleAir,dev.address) try: self.updateStateOnServer(dev, "currentTemp", int(data.currentTemp)) except: self.de (dev, "currentTemp") try: self.updateStateOnServer(dev, "version", data.version) except: self.de (dev, "version") try: self.updateStateOnServer(dev, "currentHumidity", int(data.currentHumidity)) except: self.de (dev, "currentHumidity") try: self.updateStateOnServer(dev, "currentPressure", str(data.currentPressure)) except: self.de (dev, "currentPressure") try: self.updateStateOnServer(dev, "current25", float(data.current25)) except: self.de (dev, "current25") try: self.updateStateOnServer(dev, "lat", data.lat) except: self.de (dev, "lat") try: self.updateStateOnServer(dev, "lon", data.lon) except: self.de (dev, "lon") #The local devices don't have 24hr PM2.5 if dev.deviceTypeId == "RemotePurpleAirSensor": try: self.updateStateOnServer(dev, "AQI", int(data.aqi)) except: self.de (dev, "AQI") else: try: self.updateStateOnServer(dev, "current10", float(data.current10)) except: self.de (dev, "current10") try: self.updateStateOnServer(dev, "current1", float(data.current1)) except: self.de (dev, "current1") try: self.updateStateOnServer(dev, "currentDewPoint", float(data.currentDewPoint)) except: self.de (dev, "currentDewPoint") try: self.updateStateOnServer(dev, "uptime", int(data.uptime)) except: self.de (dev, "uptime") try: self.updateStateOnServer(dev, "hardwarediscovered", data.hardwarediscovered) except: self.de (dev, "hardwarediscovered") try: self.updateStateOnServer(dev, "hardwareversion", data.hardwareversion) except: self.de (dev, "hardwareversion") #twilioDevice.updateStateImageOnServer(indigo.kStateImageSel.SensorOn) def updateStateOnServer(self, dev, state, value): # if dev.states[state] != value and (dev.states[state] != "" and value != None): # self.debugLog(u"Updating Device: %s, State: %s, Value: '%s', From Current Value '%s'" % (dev.name, state, value, dev.states[state])) dev.updateStateOnServer(state, value) def de (self, dev, value): self.errorLog ("[%s] No value found for device: %s, field: %s" % (time.asctime(), dev.name, value)) # ######################################## def startup(self): self.debug = self.pluginPrefs.get('showDebugInLog', False) self.debugLog(u"startup called") self.updater = GitHubPluginUpdater(self) #self.updater.checkForUpdate() self.updateFrequency = float(self.pluginPrefs.get('updateFrequency', 24)) * 60.0 * 60.0 self.debugLog(u"updateFrequency = " + str(self.updateFrequency)) self.next_update_check = time.time() self.buildAvailableDeviceList() def shutdown(self): self.keepProcessing = False self.debugLog(u"shutdown called") # ######################################## def runConcurrentThread(self): try: while self.keepProcessing: #indigo.server.log(u"Processing...") if (self.updateFrequency > 0.0) and (time.time() > self.next_update_check): self.next_update_check = time.time() + self.updateFrequency self.updater.checkForUpdate() for dev in indigo.devices.iter("self"): if not dev.enabled: continue if (int(self.pluginPrefs.get("maxRetry", 5)) != 0 and self.retryCount >= int(self.pluginPrefs.get("maxRetry", 5))): self.errorLog("Reached max retry attempts. Won't Refresh from Server. !") self.sleep(36000) self._refreshStatesFromHardware(dev) self.restartCount = self.restartCount + 1 if (self.restartCount > 10000): self.restartCount = 0 indigo.server.log(u"Memory Leak Prevention. Restarting Plugin. - This will happen until I find and fix the leak") serverPlugin = indigo.server.getPlugin(self.pluginId) serverPlugin.restart(waitUntilDone=False) break self.sleep(int(self.pluginPrefs.get("refreshInterval",30))) except self.StopThread: pass # Optionally catch the StopThread exception and do any needed cleanup. # ######################################## def validateDeviceConfigUi(self, valuesDict, typeId, devId): indigo.server.log(u"validateDeviceConfigUi \"%s\"" % (valuesDict)) return (True, valuesDict) def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"Vaidating Plugin Configuration") errorsDict = indigo.Dict() if valuesDict[u"refreshInterval"] == "": errorsDict[u"refreshInterval"] = u"Please enter refresh value." else: try: int(valuesDict[u"refreshInterval"]) except: errorsDict[u"maxRetry"] = u"Please enter a valid refresh Value." if len(errorsDict) > 0: self.errorLog(u"\t Validation Errors") return (False, valuesDict, errorsDict) else: self.debugLog(u"\t Validation Succesful") return (True, valuesDict) return (True, valuesDict) ######################################## def initDevice(self, dev): if dev.deviceTypeId != "RemotePurpleAirSensor": self.debugLog("Initializing PurpleAir device: %s" % dev.name) data = PurpleAir.GetData(self.PurpleAir,dev.address) else: self.debugLog("Getting data for remote PurpleAir device: %s" % dev.name) data = PurpleAir.GetRemoteData(self.PurpleAir,dev.address) self.updateStateOnServer(dev,"currentTemp", data.currentTemp) self.updateStateOnServer(dev,"name", data.label) self.updateStateOnServer(dev,"lat", data.lat) self.updateStateOnServer(dev,"lon", data.lon) self.updateStateOnServer(dev,"currentHumidity", data.currentHumidity) self.updateStateOnServer(dev,"currentPressure", str(data.currentPressure)) self.updateStateOnServer(dev,"current25", data.current25) if dev.deviceTypeId != "RemotePurpleAirSensor": self.updateStateOnServer(dev,"current10", data.current10) self.updateStateOnServer(dev,"current1", data.current1) self.updateStateOnServer(dev,"uptime", data.uptime) self.updateStateOnServer(dev,"version", data.version) self.updateStateOnServer(dev,"hardwarediscovered", data.hardwarediscovered) self.updateStateOnServer(dev,"hardwareversion", data.hardwareversion) self.updateStateOnServer(dev,"currentDewPoint", data.currentDewPoint) def buildAvailableDeviceList(self): self.debugLog("Building Available Device List") self.deviceList = PurpleAir.GetDevices(self.PurpleAir) indigo.server.log("Number of devices found: %i" % (len(self.deviceList))) for (k, v) in self.deviceList.iteritems(): indigo.server.log("\t%s (id: %s)" % (v.label, k)) def showAvailableDevices(self): indigo.server.log("Number of devices found: %i" % (len(self.deviceList))) for (id, details) in self.deviceList.iteritems(): indigo.server.log("\t%s (id: %s)" % (details.label, id)) def sensorList(self, filter, valuesDict, typeId, targetId): #self.debugLog("sensorList called") deviceArray = [] deviceListCopy = deepcopy(self.deviceList) for existingDevice in indigo.devices.iter("self"): for id in self.deviceList: self.debugLog("States: %s" % existingDevice.address) #self.debugLog("\tcomparing %s against sensorList item %s" % (existingDevice.address,id)) if str(existingDevice.address) == str(id): self.debugLog("\tremoving item %s" % (id)) del deviceListCopy[id] break if len(deviceListCopy) > 0: for (id,value) in deviceListCopy.iteritems(): deviceArray.append((id,value.label)) else: if len(self.deviceList): indigo.server.log("All devices found are already defined") else: indigo.server.log("No devices were discovered within Range - select \"Rescan for Sensors\" from the plugin's menu to rescan") #self.debugLog("\t DeviceList deviceArray:\n%s" % (str(deviceArray))) return deviceArray def selectionChanged(self, valuesDict, typeId, devId): self.debugLog("SelectionChanged") if int(valuesDict["sensor"]) in self.deviceList: #self.debugLog("Looking up deviceID %s in DeviceList Table" % valuesDict["sensor"]) selectedData = self.deviceList[int(valuesDict["sensor"])] valuesDict["address"] = valuesDict["sensor"] valuesDict["id"] = valuesDict["sensor"] valuesDict["sensor"] = valuesDict["sensor"] valuesDict["name"] = selectedData.label valuesDict["model"] = selectedData.model valuesDict["lat"] = selectedData.lat valuesDict["lon"] = selectedData.lon #self.debugLog(u"\tSelectionChanged valuesDict to be returned:\n%s" % (str(valuesDict))) return valuesDict def deviceStartComm(self, dev): self.initDevice(dev) dev.stateListOrDisplayStateIdChanged() self.debugLog("Initialize %s" % dev.address) def deviceStopComm(self, dev): # Called when communication with the hardware should be shutdown. pass def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validateDeviceConfigUi called with valuesDict: %s" % str(valuesDict)) return (True, valuesDict) def checkForUpdates(self): self.updater.checkForUpdate() def updatePlugin(self): self.updater.update() def forceUpdate(self): self.updater.update(currentVersion='0.0.0')
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater(self) self.apiVersion = "2.0" self.localAddress = "" # create empty device list self.deviceList = {} def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(u"Started device: " + device.name) device.stateListOrDisplayStateIdChanged() self.addDeviceToList(device) def deviceStopComm(self, device): if device.id in self.deviceList: self.debugLog("Stoping device: " + device.name) self.deleteDeviceFromList(device) def deviceCreated(self, device): self.debugLog(u'Created device "' + device.name) pass def addDeviceToList(self, device): if device: if device.id not in self.deviceList: propsAddress = device.pluginProps["address"] propsAddress = propsAddress.strip() propsAddress = propsAddress.replace(' ', '') pingNextTime = datetime.datetime.now() - datetime.timedelta( seconds=10) pingInterval = device.pluginProps["pingInterval"] self.deviceList[device.id] = { 'ref': device, 'address': propsAddress, 'pingInterval': pingInterval, 'pingNextTime': pingNextTime } def deleteDeviceFromList(self, device): if device: if device.id in self.deviceList: del self.deviceList[device.id] def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.updater.checkForUpdate() def shutdown(self): self.debugLog(u"shutdown called") def getDeviceConfigUiValues(self, pluginProps, typeId, devId): valuesDict = pluginProps errorMsgDict = indigo.Dict() if "pingInterval" not in valuesDict: valuesDict["pingInterval"] = 300 return (valuesDict, errorMsgDict) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") self.debugLog(u"validating IP Address") ipAdr = valuesDict[u'address'] if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress(ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) pingInterval = valuesDict[u'pingInterval'] try: iInterval = int(pingInterval) if iInterval < 1: errorMsgDict = indigo.Dict() errorMsgDict[u'pingInterval'] = u"This needs to be > 0." return False except Exception, e: errorMsgDict = indigo.Dict() errorMsgDict[u'pingInterval'] = u"This needs to be a valid number." return False return (True, valuesDict)
class Plugin(indigo.PluginBase): def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.updater = GitHubPluginUpdater( self) #'tenallero', 'Indigo-XBMC', self) # Port self.listenPortDef = 8189 self.listenPort = 0 self.apiVersion = "2.0" self.localAddress = "" # Pooling self.pollingInterval = 0 self.requestID = 0 # Flag buttonRequest is processing self.reqRunning = 0 # create empty device list self.deviceList = {} self.sock = None self.socketBufferSize = 512 self.socketStop = False def __del__(self): indigo.PluginBase.__del__(self) ################################################################### # Plugin ################################################################### def deviceStartComm(self, device): self.debugLog(device.name + ": Starting device") device.stateListOrDisplayStateIdChanged() self.addDeviceToList(device) def addDeviceToList(self, device): if device: if device.id not in self.deviceList: propsAddress = '' propsAddress = device.pluginProps["address"] propsAddress = propsAddress.strip() propsAddress = propsAddress.replace(' ', '') self.deviceList[device.id] = { 'ref': device, 'address': propsAddress, 'lastTimeAlive': datetime.datetime.now() } def deleteDeviceFromList(self, device): if device: if device.id in self.deviceList: del self.deviceList[device.id] def deviceStopComm(self, device): if device.id in self.deviceList: self.debugLog(device.name + ": Stoping device") self.deleteDeviceFromList(device) def startup(self): self.loadPluginPrefs() self.debugLog(u"startup called") self.requestID = 0 # Obtain local address. # This will identify a XBMC device running in same machine than Indigo s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("gmail.com", 80)) self.localAddress = s.getsockname()[0] s.close() self.debugLog("Local IP address: " + self.localAddress) self.updater.checkForUpdate() def shutdown(self): self.debugLog(u"shutdown called") def deviceCreated(self, device): self.debugLog(u"Created device of type \"%s\"" % device.deviceTypeId) def validateDeviceConfigUi(self, valuesDict, typeId, devId): self.debugLog(u"validating device Prefs called") ipAdr = valuesDict[u'address'] if ipAdr.count('.') != 3: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) if self.validateAddress(ipAdr) == False: errorMsgDict = indigo.Dict() errorMsgDict[u'address'] = u"This needs to be a valid IP address." return (False, valuesDict, errorMsgDict) tcpPort = valuesDict[u'port'] try: iPort = int(tcpPort) if iPort <= 0: errorMsgDict = indigo.Dict() errorMsgDict[u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) except Exception, e: errorMsgDict = indigo.Dict() errorMsgDict[u'port'] = u"This needs to be a valid TCP port." return (False, valuesDict, errorMsgDict) if (valuesDict[u'useAuthentication']): if not (valuesDict[u'username'] > ""): errorMsgDict = indigo.Dict() errorMsgDict[u'username'] = u"Must be filled." return (False, valuesDict, errorMsgDict) if not (valuesDict[u'password'] > ""): errorMsgDict = indigo.Dict() errorMsgDict[u'password'] = u"Must be filled." return (False, valuesDict, errorMsgDict) return (True, valuesDict)
class Plugin(indigo.PluginBase): #--------------------------------------------------------------------------- def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = pluginPrefs.get('debug', False) self.updater = GitHubPluginUpdater('jheddings', 'indigo-ghpu', self) #--------------------------------------------------------------------------- def __del__(self): indigo.PluginBase.__del__(self) #--------------------------------------------------------------------------- def selfInstall(self): self.updater.install() #--------------------------------------------------------------------------- def forceUpdate(self): self.updater.update(currentVersion='0.0.0') #--------------------------------------------------------------------------- def updatePlugin(self): self.updater.update() #--------------------------------------------------------------------------- def checkForUpdates(self): self.updater.checkForUpdate() #--------------------------------------------------------------------------- def checkRateLimit(self): limiter = self.updater.getRateLimit() indigo.server.log('RateLimit {limit:%d remaining:%d resetAt:%d}' % limiter) #--------------------------------------------------------------------------- def testUpdateCheck(self): indigo.server.log('-- BEGIN testUpdateCheck --') self.updater.checkForUpdate() self.updater.checkForUpdate('0.0.0') emptyUpdater = GitHubPluginUpdater('jheddings', 'indigo-ghpu') emptyUpdater.checkForUpdate() emptyUpdater.checkForUpdate('0.0.0') emptyUpdater.checkForUpdate(str(self.pluginVersion)) indigo.server.log('-- END testUpdateCheck --') #--------------------------------------------------------------------------- def toggleDebugging(self): self.debug = not self.debug self.pluginPrefs['debug'] = self.debug #--------------------------------------------------------------------------- def closedPrefsConfigUi(self, values, canceled): if (not canceled): self.debug = values.get('debug', False) #--------------------------------------------------------------------------- def runConcurrentThread(self): while True: # this checks for any updates on a regular interval self.updater.checkForUpdate() # we are checking every 300 seconds (5 minutes) here as an example # in practice, this should not be less than 3600 seconds (1 hour) self.sleep(300)
class Plugin(indigo.PluginBase): ######################################## # Main Plugin methods ######################################## def __init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs): indigo.PluginBase.__init__(self, pluginId, pluginDisplayName, pluginVersion, pluginPrefs) self.debug = self.pluginPrefs.get(u"showDebugInfo", False) self.debugLog(u"Debugging enabled") self.apiData = { "chamberlain" : { "service" : "https://myqexternal.myqdevice.com", "appID" : "Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB/i" }, "craftsman" : { "service" : "https://craftexternal.myqdevice.com", "appID" : "eU97d99kMG4t3STJZO/Mu2wt69yTQwM0WXZA5oZ74/ascQ2xQrLD/yjeVhEQccBZ" }, "liftmaster" : { "service" : "https://myqexternal.myqdevice.com", "appID" : "Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB/i" }, } self.triggers = { } def __del__(self): indigo.PluginBase.__del__(self) def startup(self): indigo.server.log(u"Starting MyQ") self.updater = GitHubPluginUpdater(self) self.updateFrequency = float(self.pluginPrefs.get('updateFrequency', "24")) * 60.0 * 60.0 if self.updateFrequency > 0: self.next_update_check = time.time() self.statusFrequency = float(self.pluginPrefs.get('statusFrequency', "10")) * 60.0 if self.statusFrequency > 0: self.next_status_check = time.time() def shutdown(self): indigo.server.log(u"Shutting down MyQ") def runConcurrentThread(self): try: while True: if self.updateFrequency > 0: if time.time() > self.next_update_check: self.updater.checkForUpdate() self.next_update_check = time.time() + self.updateFrequency if self.statusFrequency > 0: if time.time() > self.next_status_check: self.getDevices() self.next_status_check = time.time() + self.statusFrequency self.sleep(1.0) except self.stopThread: pass #################### def triggerStartProcessing(self, trigger): self.debugLog("Adding Trigger %s (%d) - %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) assert trigger.id not in self.triggers self.triggers[trigger.id] = trigger def triggerStopProcessing(self, trigger): self.debugLog("Removing Trigger %s (%d)" % (trigger.name, trigger.id)) assert trigger.id in self.triggers del self.triggers[trigger.id] def triggerCheck(self, device): for triggerId, trigger in sorted(self.triggers.iteritems()): self.debugLog("\tChecking Trigger %s (%s), Type: %s" % (trigger.name, trigger.id, trigger.pluginTypeId)) #################### # def deviceStartComm(self, device): # self.debugLog(u'Called deviceStartComm(self, device): %s (%s)' % (device.name, device.id)) # instanceVers = int(device.pluginProps.get('devVersCount', 0)) # self.debugLog(device.name + u": Device Current Version = " + str(instanceVers)) # if instanceVers >= kCurDevVersCount: # self.debugLog(device.name + u": Device Version is up to date") # elif instanceVers < kCurDevVersCount: # newProps = device.pluginProps # else: # self.errorLog(u"Unknown device version: " + str(instanceVers) + " for device " + device.name) # def deviceStopComm(self, device): # self.debugLog(u'Called deviceStopComm(self, device): %s (%s)' % (device.name, device.id)) ######################################## # Menu Methods ######################################## def toggleDebugging(self): self.debug = not self.debug self.pluginPrefs["debugEnabled"] = self.debug indigo.server.log("Debug set to: " + str(self.debug)) def checkForUpdates(self): self.updater.checkForUpdate() def updatePlugin(self): self.updater.update() def forceUpdate(self): self.updater.update(currentVersion='0.0.0') ######################################## # ConfigUI methods ######################################## def validatePrefsConfigUi(self, valuesDict): self.debugLog(u"validatePrefsConfigUi called") errorDict = indigo.Dict() if len(valuesDict['myqLogin']) < 5: errorDict['myqLogin'] = u"Enter your MyQ login name (email address)" if len(valuesDict['myqPassword']) < 1: errorDict['myqPassword'] = u"Enter your MyQ login password" statusFrequency = int(valuesDict['statusFrequency']) if (statusFrequency < 5) or (statusFrequency > (24 * 60)): errorDict['statusFrequency'] = u"Status frequency must be at least 5 min and less than 24 hours" updateFrequency = int(valuesDict['updateFrequency']) if (updateFrequency < 0) or (updateFrequency > 24): errorDict['updateFrequency'] = u"Update frequency is invalid - enter a valid number (between 0 and 24)" if len(errorDict) > 0: return (False, valuesDict, errorDict) return (True, valuesDict) def closedPrefsConfigUi(self, valuesDict, userCancelled): if not userCancelled: self.debug = valuesDict.get("showDebugInfo", False) if self.debug: self.debugLog(u"Debug logging enabled") else: self.debugLog(u"Debug logging disabled") # def validateDeviceConfigUi(self, valuesDict, typeId, devId): # self.debugLog(u'Called validateDeviceConfigUi, valuesDict = %s, typeId = %s, devId = %s' % (str(valuesDict), typeId, devId)) # errorsDict = indigo.Dict() # if int(valuesDict["address"]) < 1: # errorDict['address'] = u"Invalid Device ID Number" # if len(errorsDict) > 0: # return (False, valuesDict, errorsDict) # return (True, valuesDict) # def validateActionConfigUi(self, valuesDict, typeId, devId): # self.debugLog(u'Called validateActionConfigUi, valuesDict = %s, typeId = %s, devId = %s' % (str(valuesDict), typeId, devId)) # errorsDict = indigo.Dict() # try: # pass # except: # pass # if len(errorsDict) > 0: # return (False, valuesDict, errorsDict) # return (True, valuesDict) ######################################## def actionControlDimmerRelay(self, action, dev): if action.deviceAction == indigo.kDeviceAction.TurnOn: self.debugLog(u"actionControlDimmerRelay: \"%s\" On" % dev.name) self.changeDevice(dev, kDoorOpen) elif action.deviceAction == indigo.kDeviceAction.TurnOff: self.debugLog(u"actionControlDimmerRelay: \"%s\" Off" % dev.name) self.changeDevice(dev, kDoorClosed) elif action.deviceAction == indigo.kDeviceAction.Toggle: self.debugLog(u"actionControlDimmerRelay: \"%s\" Toggle" % dev.name) if dev.isOn: self.changeDevice(dev, kDoorClosed) else: self.changeDevice(dev, kDoorOpen) elif action.deviceAction == indigo.kDeviceAction.RequestStatus: self.debugLog(u"actionControlDimmerRelay: \"%s\" Request Status" % dev.name) self.getDevices() ######################################## def myqLogin(self): self.username = self.pluginPrefs.get('myqLogin', None) self.password = self.pluginPrefs.get('myqPassword', None) self.brand = self.pluginPrefs.get('openerBrand', None) if (self.brand): self.service = self.apiData[self.brand]["service"] self.appID = self.apiData[self.brand]["appID"] payload = {'appId': self.appID, 'securityToken': 'null', 'username': self.username, 'password': self.password, 'culture': 'en'} login_url = self.service + '/Membership/ValidateUserWithCulture' headers = {'User-Agent': userAgent} try: response = requests.get(login_url, params=payload, headers=headers) self.logger.debug(u"myqLogin: response = " + str(response)) self.logger.debug(u"myqLogin: content = " + str(response.text)) except requests.exceptions.RequestException as err: self.logger.debug(u"myqLogin failure: RequestException: " + str(err)) self.securityToken = "" return try: data = response.json() except ValueError as err: self.logger.debug(u"myqLogin failure: JSON Decode Error: " + str(err)) self.securityToken = "" return if data['ReturnCode'] != '0': self.debugLog(u"myqLogin failure: Bad return code: " + data['ErrorMessage']) self.securityToken = "" return self.securityToken = data['SecurityToken'] self.debugLog(u"myqLogin: Success, SecurityToken = %s" % (self.securityToken)) ######################################## def getDevices(self): self.myqLogin() if not self.securityToken: return url = self.service + '/api/UserDeviceDetails' params = {'appId':self.appID, 'securityToken':self.securityToken} headers = {'User-Agent': userAgent } try: response = requests.get(url, params=params, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"getDevices: RequestException: " + str(err)) return data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"getDevices: Bad return code: " + data['ErrorMessage']) return self.debugLog(u"getDevices: %d Devices" % len(data['Devices'])) for device in data['Devices']: self.debugLog(u"getDevices: MyQDeviceTypeId = %s, DeviceId = %s" % (device['MyQDeviceTypeId'], device['DeviceId'])) if (device['MyQDeviceTypeId'] == 2) or (device['MyQDeviceTypeId'] == 5) or (device['MyQDeviceTypeId'] == 7): # MyQDeviceTypeId Door == 2, Gate == 5, Door? == 7 myqID = device['DeviceId'] name = self.getDeviceName(myqID) state = self.getDeviceState(myqID) if state > 7: self.errorLog(u"getDevices: Opener %s (%s), state out of range: %i" % (name, myqID, state)) state = 0 # unknown high states else: self.debugLog(u"getDevices: Opener %s (%s), state = %i" % (name, myqID, state)) # look for this opener device in the existing devices for this plugin. If it's not there (by id), then create it iterator = indigo.devices.iter(filter="com.flyingdiver.indigoplugin.myq") for dev in iterator: if dev.address == myqID: dev.updateStateOnServer(key="doorStatus", value=doorStateNames[int(state)]) if state == 2: dev.updateStateOnServer(key="onOffState", value=False) # closed is off else: dev.updateStateOnServer(key="onOffState", value=True) # anything other than closed is "on" break else: # Python syntax weirdness - this else belongs to the for loop! newdev = indigo.device.create(protocol=indigo.kProtocol.Plugin, address=myqID, description = "Opener Device auto-created by MyQ plugin from gateway information", deviceTypeId='myqOpener', name=name) newdev.updateStateOnServer(key="doorStatus", value=doorStateNames[int(state)]) self.debugLog(u'Created New Opener Device: %s (%s)' % (newdev.name, newdev.address)) elif device['MyQDeviceTypeId'] == 3: # Switch == 3? myqID = device['DeviceId'] name = self.getDeviceName(myqID) state = self.getDeviceState(myqID) # self.debugLog(u"getDevices: Switch = %s (%s), data = %s" % (name, myqID, str(device))) # look for this opener device in the existing devices for this plugin. If it's not there (by id), then create it iterator = indigo.devices.iter(filter="com.flyingdiver.indigoplugin.myq") for dev in iterator: if dev.address == myqID: break else: # Python syntax weirdness - this else belongs to the for loop! newdev = indigo.device.create(protocol=indigo.kProtocol.Plugin, address=myqID, description = "Switch Device auto-created by MyQ plugin from gateway information", deviceTypeId='myqSwitch', name=name) # newdev.updateStateOnServer(key="doorStatus", value=doorStateNames[int(state)]) self.debugLog(u'Created New Switch Device: %s (%s)' % (newdev.name, newdev.address)) def getDeviceName(self, doorID): url = self.service + '/Device/getDeviceAttribute' params = {'appId': self.appID, 'securityToken': self.securityToken, 'devId': doorID, 'name':'desc'} headers = {'User-Agent': userAgent} try: response = requests.get(url, params=params, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"getDeviceName: RequestException: " + str(err)) return "" data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"getDeviceName: Bad return code: " + data['ErrorMessage']) return "" return data['AttributeValue'] def getDeviceState(self, doorID): url = self.service + '/Device/getDeviceAttribute' params = {'appID': self.appID, 'securityToken': self.securityToken, 'devId': doorID, 'name':'doorstate'} headers = {'User-Agent': userAgent} try: response = requests.get(url, params=params, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"getDeviceState: RequestException: " + str(err)) return 0 data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"getDeviceState: Bad return code: " + data['ErrorMessage']) return 0 return int(data['AttributeValue']) ######################################## def changeDeviceAction(self, pluginAction): if pluginAction != None: myqDevice = indigo.devices[pluginAction.deviceId] myqActionId = pluginAction.pluginTypeId if myqActionId == "openDoor": self.changeDevice(myqDevice, kDoorOpen) elif myqActionId == "closeDoor": self.changeDevice(myqDevice, kDoorClosed) elif myqActionId == "switchOn": self.changeDevice(myqDevice, kSwitchOn) elif myqActionId == "switchOff": self.changeDevice(myqDevice, kSwitchOff) else: self.debugLog(u"changeDeviceAction, unknown myqActionId = %s" % myqActionId) def changeDevice(self, device, state): self.debugLog(u"changeDevice: %s, state = %d" % (device.name, state)) self.myqLogin() payload = { 'ApplicationId': self.appID, 'AttributeName': 'desireddoorstate', 'DeviceId': device.address, 'AttributeValue': state, 'SecurityToken': self.securityToken } url = self.service + '/api/deviceattribute/putdeviceattribute' headers = {'User-Agent': userAgent} try: response = requests.put(url, data=payload, headers=headers) except requests.exceptions.RequestException as err: self.debugLog(u"changeDevice: RequestException: " + str(err)) return data = response.json() if data['ReturnCode'] != '0': self.debugLog(u"changeDevice: Bad return code: " + data['ErrorMessage']) # schedule an update to check on the movement self.next_status_check = time.time() + 30.0