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
Exemple #2
0
    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 --')
Exemple #3
0
    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 --')
Exemple #4
0
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)
Exemple #5
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)
Exemple #6
0
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
Exemple #7
0
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
Exemple #9
0
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)
Exemple #10
0
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')
Exemple #11
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)
Exemple #12
0
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')
Exemple #13
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)
Exemple #14
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 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    
Exemple #16
0
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
Exemple #17
0
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)
Exemple #18
0
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
Exemple #19
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')
Exemple #21
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)
Exemple #22
0
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)
Exemple #23
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('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)
Exemple #24
0
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