def execute(self): # time on which the last full sensor states were sent # to the server lastFullStateSent = 0 # Get reference to server communication object. while self.connection is None: time.sleep(0.5) self.connection = self.globalData.serverComm self._isInitialized = True while True: # check if the client is connected to the server # => wait and continue loop until client is connected if not self.connection.isConnected(): time.sleep(0.5) continue # poll all sensors and check their states for sensor in self.sensors: oldState = sensor.getState() sensor.updateState() currentState = sensor.getState() # Check if a sensor alert is forced to send to the server. # => update already known state and continue sensorAlert = sensor.forceSendAlert() if sensorAlert: oldState = currentState asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorAlert = True asyncSenderProcess.sendSensorAlertSensorAlert = sensorAlert asyncSenderProcess.start() continue # check if the current state is the same # than the already known state => continue elif oldState == currentState: continue # check if the current state is an alert triggering state elif currentState == sensor.triggerState: # check if the sensor triggers a sensor alert # => send sensor alert to server if sensor.triggerAlert: logging.info("[%s]: Sensor alert " % self.fileName + "triggered by '%s'." % sensor.description) # Create sensor alert object to send to the server. sensorAlert = SensorAlert() sensorAlert.clientSensorId = sensor.id sensorAlert.state = 1 sensorAlert.hasOptionalData = sensor.hasOptionalData sensorAlert.optionalData = sensor.optionalData sensorAlert.changeState = sensor.changeState sensorAlert.hasLatestData = sensor.hasLatestData sensorAlert.dataType = sensor.sensorDataType sensorAlert.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorAlert = True asyncSenderProcess.sendSensorAlertSensorAlert = \ sensorAlert asyncSenderProcess.start() # if sensor does not trigger sensor alert # => just send changed state to server else: logging.debug("[%s]: State " % self.fileName + "changed by '%s'." % sensor.description) # Create state change object to send to the server. stateChange = StateChange() stateChange.clientSensorId = sensor.id stateChange.state = 1 stateChange.dataType = sensor.sensorDataType stateChange.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendStateChange = True asyncSenderProcess.sendStateChangeStateChange = \ stateChange asyncSenderProcess.start() # only possible situation left => sensor changed # back from triggering state to a normal state else: # check if the sensor triggers a sensor alert when # state is back to normal # => send sensor alert to server if sensor.triggerAlertNormal: logging.info("[%s]: Sensor alert " % self.fileName + "for back to normal state " + "triggered by '%s'." % sensor.description) # Create sensor alert object to send to the server. sensorAlert = SensorAlert() sensorAlert.clientSensorId = sensor.id sensorAlert.state = 0 sensorAlert.hasOptionalData = sensor.hasOptionalData sensorAlert.optionalData = sensor.optionalData sensorAlert.changeState = sensor.changeState sensorAlert.hasLatestData = sensor.hasLatestData sensorAlert.dataType = sensor.sensorDataType sensorAlert.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorAlert = True asyncSenderProcess.sendSensorAlertSensorAlert = \ sensorAlert asyncSenderProcess.start() # if sensor does not trigger sensor alert when # state is back to normal # => just send changed state to server else: logging.debug("[%s]: State " % self.fileName + "changed by '%s'." % sensor.description) # Create state change object to send to the server. stateChange = StateChange() stateChange.clientSensorId = sensor.id stateChange.state = 0 stateChange.dataType = sensor.sensorDataType stateChange.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendStateChange = True asyncSenderProcess.sendStateChangeStateChange = \ stateChange asyncSenderProcess.start() # Poll all sensors if they want to force an update that should # be send to the server. for sensor in self.sensors: stateChange = sensor.forceSendState() if stateChange: asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendStateChange = True asyncSenderProcess.sendStateChangeStateChange = stateChange asyncSenderProcess.start() # check if the last state that was sent to the server # is older than 60 seconds => send state update utcTimestamp = int(time.time()) if (utcTimestamp - lastFullStateSent) > 60: logging.debug("[%s]: Last state " % self.fileName + "timed out.") asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorsState = True asyncSenderProcess.start() # update time on which the full state update was sent lastFullStateSent = utcTimestamp time.sleep(0.5)
def _sensorAlertHandler(self, incomingMessage): logging.info("[%s]: Received sensor alert." % self.fileName) # extract sensor alert values sensorAlert = SensorAlert() sensorAlert.timeReceived = int(time.time()) try: if not self._checkMsgServerTime( incomingMessage["serverTime"], incomingMessage["message"]): logging.error("[%s]: Received serverTime invalid." % self.fileName) return False if not self._checkMsgAlertLevels( incomingMessage["payload"]["alertLevels"], incomingMessage["message"]): logging.error("[%s]: Received alertLevels invalid." % self.fileName) return False if not self._checkMsgDescription( incomingMessage["payload"]["description"], incomingMessage["message"]): logging.error("[%s]: Received description invalid." % self.fileName) return False if not self._checkMsgRulesActivated( incomingMessage["payload"]["rulesActivated"], incomingMessage["message"]): logging.error("[%s]: Received rulesActivated invalid." % self.fileName) return False if not self._checkMsgSensorId( incomingMessage["payload"]["sensorId"], incomingMessage["message"]): logging.error("[%s]: Received sensorId invalid." % self.fileName) return False if not self._checkMsgState( incomingMessage["payload"]["state"], incomingMessage["message"]): logging.error("[%s]: Received state invalid." % self.fileName) return False if not self._checkMsgHasOptionalData( incomingMessage["payload"]["hasOptionalData"], incomingMessage["message"]): logging.error("[%s]: Received hasOptionalData invalid." % self.fileName) return False if incomingMessage["payload"]["hasOptionalData"]: if not self._checkMsgOptionalData( incomingMessage["payload"]["optionalData"], incomingMessage["message"]): logging.error("[%s]: Received optionalData invalid." % self.fileName) return False if not self._checkMsgSensorDataType( incomingMessage["payload"]["dataType"], incomingMessage["message"]): logging.error("[%s]: Received dataType invalid." % self.fileName) return False if incomingMessage["payload"]["dataType"] != SensorDataType.NONE: if not self._checkMsgSensorData( incomingMessage["payload"]["data"], incomingMessage["payload"]["dataType"], incomingMessage["message"]): logging.error("[%s]: Received data invalid." % self.fileName) return False if not self._checkMsgHasLatestData( incomingMessage["payload"]["hasLatestData"], incomingMessage["message"]): logging.error("[%s]: Received hasLatestData invalid." % self.fileName) return False if not self._checkMsgChangeState( incomingMessage["payload"]["changeState"], incomingMessage["message"]): logging.error("[%s]: Received changeState invalid." % self.fileName) return False sensorAlert.sensorId = incomingMessage["payload"]["sensorId"] sensorAlert.alertLevels = incomingMessage["payload"]["alertLevels"] sensorAlert.state = incomingMessage["payload"]["state"] sensorAlert.description = incomingMessage["payload"]["description"] sensorAlert.changeState = incomingMessage["payload"]["changeState"] sensorAlert.rulesActivated = \ incomingMessage["payload"]["rulesActivated"] # parse received data (if data transfer is activated) sensorAlert.optionalData = None sensorAlert.hasOptionalData = \ incomingMessage["payload"]["hasOptionalData"] if sensorAlert.hasOptionalData: sensorAlert.optionalData = incomingMessage[ "payload"]["optionalData"] sensorAlert.changeState = incomingMessage["payload"]["changeState"] sensorAlert.hasLatestData = \ incomingMessage["payload"]["hasLatestData"] sensorAlert.dataType = incomingMessage["payload"]["dataType"] sensorAlert.sensorData = None if sensorAlert.dataType != SensorDataType.NONE: sensorAlert.sensorData = incomingMessage["payload"]["data"] except Exception as e: logging.exception("[%s]: Received sensor alert " % self.fileName + "invalid.") # send error message back try: utcTimestamp = int(time.time()) message = {"clientTime": utcTimestamp, "message": incomingMessage["message"], "error": "received sensor alert invalid"} self.client.send(json.dumps(message)) except Exception as e: pass return False # sending sensor alert response logging.debug("[%s]: Sending sensor alert " % self.fileName + "response message.") try: payload = {"type": "response", "result": "ok"} utcTimestamp = int(time.time()) message = {"clientTime": utcTimestamp, "message": "sensoralert", "payload": payload} self.client.send(json.dumps(message)) except Exception as e: logging.exception("[%s]: Sending sensor alert " % self.fileName + "response failed.") return False # trigger all alerts that have the same alert level atLeastOnceTriggered = False for alert in self.alerts: for alertLevel in sensorAlert.alertLevels: if alertLevel in alert.alertLevels: atLeastOnceTriggered = True # trigger alert in an own thread to not block this one alertTriggerProcess = AsynchronousAlertExecuter(alert) alertTriggerProcess.sensorAlert = sensorAlert # set thread to daemon # => threads terminates when main thread terminates alertTriggerProcess.daemon = True alertTriggerProcess.triggerAlert = True alertTriggerProcess.start() break # Write to log file if no alert was triggered for received sensorAlert. if not atLeastOnceTriggered: alertLevelsStr = ", ".join(map(str, sensorAlert.alertLevels)) logging.info("[%s]: No alert triggered for alertLevels: %s." % (self.fileName, alertLevelsStr)) return True
def run(self): while True: # check if FIFO file exists # => remove it if it does if os.path.exists(self.fifoFile): try: os.remove(self.fifoFile) except Exception as e: logging.exception( "[%s]: Could not delete " % self.fileName + "FIFO file of sensor with id '%d'." % self.id) time.sleep(10) continue # create a new FIFO file try: os.umask(self.umask) os.mkfifo(self.fifoFile) except Exception as e: logging.exception("[%s]: Could not create " % self.fileName + "FIFO file of sensor with id '%d'." % self.id) time.sleep(10) continue # read FIFO for data data = "" try: fifo = open(self.fifoFile, "r") data = fifo.read() fifo.close() except Exception as e: logging.exception( "[%s]: Could not read data from " % self.fileName + "FIFO file of sensor with id '%d'." % self.id) time.sleep(10) continue logging.debug("[%s]: Received data '%s' from " % (self.fileName, data) + "FIFO file of sensor with id '%d'." % self.id) # parse received data try: message = json.loads(data) # Parse message depending on type. # Type: statechange if str(message["message"]).upper() == "STATECHANGE": # Check if state is valid. tempInputState = message["payload"]["state"] if not self._checkState(tempInputState): logging.error( "[%s]: Received state " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue # Check if data type is valid. tempDataType = message["payload"]["dataType"] if not self._checkDataType(tempDataType): logging.error( "[%s]: Received data type " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue # Set new data. if self.sensorDataType == SensorDataType.NONE: self.sensorData = None elif self.sensorDataType == SensorDataType.INT: self.sensorData = int(message["payload"]["data"]) elif self.sensorDataType == SensorDataType.FLOAT: self.sensorData = float(message["payload"]["data"]) # Set state. self.temporaryState = tempInputState # Force state change sending if the data could be changed. if self.sensorDataType != SensorDataType.NONE: # Create state change object that is # send to the server. self.forceSendStateLock.acquire() self.stateChange = StateChange() self.stateChange.clientSensorId = self.id if tempInputState == self.triggerState: self.stateChange.state = 1 else: self.stateChange.state = 0 self.stateChange.dataType = tempDataType self.stateChange.sensorData = self.sensorData self.shouldForceSendState = True self.forceSendStateLock.release() # Type: sensoralert elif str(message["message"]).upper() == "SENSORALERT": # Check if state is valid. tempInputState = message["payload"]["state"] if not self._checkState(tempInputState): logging.error( "[%s]: Received state " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue # Check if hasOptionalData field is valid. tempHasOptionalData = message["payload"]["hasOptionalData"] if not self._checkHasOptionalData(tempHasOptionalData): logging.error( "[%s]: Received hasOptionalData field " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue # Check if data type is valid. tempDataType = message["payload"]["dataType"] if not self._checkDataType(tempDataType): logging.error( "[%s]: Received data type " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue if self.sensorDataType == SensorDataType.NONE: tempSensorData = None elif self.sensorDataType == SensorDataType.INT: tempSensorData = int(message["payload"]["data"]) elif self.sensorDataType == SensorDataType.FLOAT: tempSensorData = float(message["payload"]["data"]) # Check if hasLatestData field is valid. tempHasLatestData = message["payload"]["hasLatestData"] if not self._checkHasLatestData(tempHasLatestData): logging.error( "[%s]: Received hasLatestData field " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue # Check if changeState field is valid. tempChangeState = message["payload"]["changeState"] if not self._checkChangeState(tempChangeState): logging.error( "[%s]: Received changeState field " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue # Check if data should be transfered with the sensor alert # => if it should parse it tempOptionalData = None if tempHasOptionalData: tempOptionalData = message["payload"]["optionalData"] # check if data is of type dict if not isinstance(tempOptionalData, dict): logging.warning( "[%s]: Received optional data " % self.fileName + "from FIFO file of sensor with id '%d' " % self.id + "invalid. Ignoring message.") continue # Set optional data. self.hasOptionalData = tempHasOptionalData self.optionalData = tempOptionalData # Set new data. if tempHasLatestData: self.sensorData = tempSensorData # Set state. if tempChangeState: self.temporaryState = tempInputState # Create sensor alert object that is send to the server. self.forceSendAlertLock.acquire() self.sensorAlert = SensorAlert() self.sensorAlert.clientSensorId = self.id if tempInputState == self.triggerState: self.sensorAlert.state = 1 else: self.sensorAlert.state = 0 self.sensorAlert.hasOptionalData = tempHasOptionalData self.sensorAlert.optionalData = tempOptionalData self.sensorAlert.changeState = tempChangeState self.sensorAlert.hasLatestData = tempHasLatestData self.sensorAlert.dataType = tempDataType self.sensorAlert.sensorData = tempSensorData self.shouldForceSendAlert = True self.forceSendAlertLock.release() # Type: invalid else: raise ValueError("Received invalid message type.") except Exception as e: logging.exception("[%s]: Could not parse received data from " % self.fileName + "FIFO file of sensor with id '%d'." % self.id) continue
def forceSendAlert(self): # Check if we have exceeded the threshold of failed calendar # retrieval attempts and create a sensor alert if we have. sensorAlert = None if (not self.inFailedState and self.failedCounter > self.maxFailedAttempts): logging.warning("[%s] Triggering sensor alert for " % self.fileName + "'%d' failed calendar fetching attempts." % self.failedCounter) sensorAlert = SensorAlert() sensorAlert.clientSensorId = self.id sensorAlert.state = 1 sensorAlert.hasOptionalData = True msg = "Failed more than %d times for '%s' " \ % (self.maxFailedAttempts, self.name) \ + "to retrieve calendar data." sensorAlert.optionalData = {"message": msg, "calendar": self.name, "type": "timeout"} sensorAlert.changeState = True sensorAlert.hasLatestData = False sensorAlert.dataType = SensorDataType.NONE self.state = self.triggerState self.inFailedState = True # If we are in a failed retrieval state and we could retrieve # calendar data again trigger a sensor alert for "normal". elif (self.inFailedState and self.failedCounter <= self.maxFailedAttempts): logging.warning("[%s] Fetching calendar succeeded after " % self.fileName + "multiple failed attempts. Triggering sensor alert.") sensorAlert = SensorAlert() sensorAlert.clientSensorId = self.id sensorAlert.state = 0 sensorAlert.hasOptionalData = True msg = "Calendar data for '%s' retrievable again." \ % self.name sensorAlert.optionalData = {"message": msg, "calendar": self.name, "type": "timeout"} sensorAlert.changeState = True sensorAlert.hasLatestData = False sensorAlert.dataType = SensorDataType.NONE self.state = 1 - self.triggerState self.inFailedState = False # If we have sensor alerts of reminder waiting # return the oldest of them. elif not self.reminderAlertQueue.empty(): try: sensorAlert = self.reminderAlertQueue.get(True, 2) except: pass return sensorAlert
def execute(self): # time on which the last full sensor states were sent # to the server lastFullStateSent = 0 # Get reference to server communication object. while self.connection is None: time.sleep(0.5) self.connection = self.globalData.serverComm self._isInitialized = True while True: # check if the client is connected to the server # => wait and continue loop until client is connected if not self.connection.isConnected(): time.sleep(0.5) continue # poll all sensors and check their states for sensor in self.sensors: oldState = sensor.getState() sensor.updateState() currentState = sensor.getState() # Check if a sensor alert is forced to send to the server. # => update already known state and continue sensorAlert = sensor.forceSendAlert() if sensorAlert: oldState = currentState asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorAlert = True asyncSenderProcess.sendSensorAlertSensorAlert = sensorAlert asyncSenderProcess.start() continue # check if the current state is the same # than the already known state => continue elif oldState == currentState: continue # check if the current state is an alert triggering state elif currentState == sensor.triggerState: # check if the sensor triggers a sensor alert # => send sensor alert to server if sensor.triggerAlert: logging.info("[%s]: Sensor alert " % self.fileName + "triggered by '%s'." % sensor.description) # Create sensor alert object to send to the server. sensorAlert = SensorAlert() sensorAlert.clientSensorId = sensor.id sensorAlert.state = 1 sensorAlert.hasOptionalData = sensor.hasOptionalData sensorAlert.optionalData = sensor.optionalData sensorAlert.changeState = sensor.changeState sensorAlert.hasLatestData = sensor.hasLatestData sensorAlert.dataType = sensor.sensorDataType sensorAlert.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorAlert = True asyncSenderProcess.sendSensorAlertSensorAlert = \ sensorAlert asyncSenderProcess.start() # if sensor does not trigger sensor alert # => just send changed state to server else: logging.debug("[%s]: State " % self.fileName + "changed by '%s'." % sensor.description) # Create state change object to send to the server. stateChange = StateChange() stateChange.clientSensorId = sensor.id stateChange.state = 1 stateChange.dataType = sensor.sensorDataType stateChange.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendStateChange = True asyncSenderProcess.sendStateChangeStateChange = \ stateChange asyncSenderProcess.start() # only possible situation left => sensor changed # back from triggering state to a normal state else: # check if the sensor triggers a sensor alert when # state is back to normal # => send sensor alert to server if sensor.triggerAlertNormal: logging.info("[%s]: Sensor alert " % self.fileName + "for back to normal state " + "triggered by '%s'." % sensor.description) # Create sensor alert object to send to the server. sensorAlert = SensorAlert() sensorAlert.clientSensorId = sensor.id sensorAlert.state = 0 sensorAlert.hasOptionalData = sensor.hasOptionalData sensorAlert.optionalData = sensor.optionalData sensorAlert.changeState = sensor.changeState sensorAlert.hasLatestData = sensor.hasLatestData sensorAlert.dataType = sensor.sensorDataType sensorAlert.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorAlert = True asyncSenderProcess.sendSensorAlertSensorAlert = \ sensorAlert asyncSenderProcess.start() # if sensor does not trigger sensor alert when # state is back to normal # => just send changed state to server else: logging.debug("[%s]: State " % self.fileName + "changed by '%s'." % sensor.description) # Create state change object to send to the server. stateChange = StateChange() stateChange.clientSensorId = sensor.id stateChange.state = 0 stateChange.dataType = sensor.sensorDataType stateChange.sensorData = sensor.sensorData asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendStateChange = True asyncSenderProcess.sendStateChangeStateChange = \ stateChange asyncSenderProcess.start() # Poll all sensors if they want to force an update that should # be send to the server. for sensor in self.sensors: stateChange = sensor.forceSendState() if stateChange: asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendStateChange = True asyncSenderProcess.sendStateChangeStateChange = stateChange asyncSenderProcess.start() # check if the last state that was sent to the server # is older than 60 seconds => send state update if (time.time() - lastFullStateSent) > 60: logging.debug("[%s]: Last state " % self.fileName + "timed out.") asyncSenderProcess = AsynchronousSender( self.connection, self.globalData) # set thread to daemon # => threads terminates when main thread terminates asyncSenderProcess.daemon = True asyncSenderProcess.sendSensorsState = True asyncSenderProcess.start() # update time on which the full state update was sent lastFullStateSent = time.time() time.sleep(0.5)
def _processEventAlarms(self, event, eventDatetime): # Create current datetime object. currentUTCTime = time.time() currentDatetime = datetime.datetime.utcfromtimestamp(currentUTCTime) currentDatetime = currentDatetime.replace(tzinfo=pytz.UTC) for alarm in event.walk("VALARM"): if alarm.has_key("TRIGGER"): # Get time delta when reminder is triggered. trigger = alarm.get("TRIGGER") # Get time when reminder is triggered. # When trigger time is a delta, then calculate the actual time. if type(trigger.dt) == datetime.timedelta: triggerDatetime = eventDatetime + trigger.dt # When trigger time is an actual time, use this. elif type(trigger.dt) == datetime.datetime: triggerDatetime = trigger.dt # Use the same timezone as the event when # no is given. if triggerDatetime.tzinfo is None: triggerDatetime = triggerDatetime.replace( tzinfo=eventDatetime.tzinfo) # When trigger time is only a date, start at midnight, # however, use the same timezone as the event. elif type(trigger.dt) == datetime.date: triggerUTCTime = calendar.timegm(trigger.dt.timetuple()) triggerDatetime = datetime.datetime.utcfromtimestamp( triggerUTCTime) # Since we just overwrite the timezone here, we do not # care about conversion from UTC since the new datetime # object starts at midnight in the given timezone. triggerDatetime = triggerDatetime.replace( tzinfo=eventDatetime.tzinfo) else: logging.error("[%s] Error: Do not know how to handle " % self.fileName + "trigger type '%s'." % trigger.dt.__class__) continue # Uid of event is needed. uid = event.get("UID") # Get time when event starts. dtstart = event.get("DTSTART") # Check if we already triggered an alarm for the event # with this uid and the given alarm trigger time. if (uid, triggerDatetime) in self.alreadyTriggered: break # Check if the alarm trigger time lies in the past but not # more than 1 day. if((currentDatetime - self.timedelta1day) <= triggerDatetime <= currentDatetime): title = event.get("SUMMARY") # Get description if event has one. evDescription = "" if event.has_key("DESCRIPTION"): evDescription = event.get("DESCRIPTION") # Get location if event has one. location = "" if event.has_key("LOCATION"): location = event.get("LOCATION") # Create the utc unix timestamp for the start of the event. unixStart = datetime.datetime.utcfromtimestamp(0) unixStart = unixStart.replace(tzinfo=pytz.UTC) utcDtstart = int( (eventDatetime - unixStart).total_seconds()) # Create the utc unix timestamp for the reminder trigger. utcTrigger = int( (triggerDatetime - unixStart).total_seconds()) eventDateStr = time.strftime("%D %H:%M:%S", time.localtime(utcDtstart)) msg = "Reminder for event '%s' at %s" \ % (title, eventDateStr) # Create sensor alert. sensorAlert = SensorAlert() sensorAlert.clientSensorId = self.id sensorAlert.state = 1 sensorAlert.hasOptionalData = True sensorAlert.optionalData = {"message": msg, "calendar": self.name, "type": "reminder", "title": title, "description": evDescription, "location": location, "trigger": utcTrigger, "start": utcDtstart} sensorAlert.changeState = False sensorAlert.hasLatestData = False sensorAlert.dataType = SensorDataType.NONE self.reminderAlertQueue.put(sensorAlert) # Store the event uid and the alarm trigger time # as already triggered. self.alreadyTriggered.add( (uid, triggerDatetime) )
def updateState(self): self.hasOptionalData = False self.optionalData = None # check if a process is executed # => if none no process is executed if self.process is None: # check if the interval in which the service should be checked # is exceeded utcTimestamp = int(time.time()) if (utcTimestamp - self.timeExecute) > self.intervalToCheck: logging.debug("[%s]: Executing process " % self.fileName + "'%s'." % self.description) self.process = subprocess.Popen(self.execute, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.timeExecute = utcTimestamp # => process is still running else: # check if process is not finished yet if self.process.poll() is None: # check if process has timed out utcTimestamp = int(time.time()) if (utcTimestamp - self.timeExecute) > self.timeout: self.state = self.triggerState self.hasOptionalData = True self.optionalData = {"message": "Timeout"} logging.error("[%s]: Process " % self.fileName + "'%s' has timed out." % self.description) # terminate process self.process.terminate() # give the process one second to terminate time.sleep(1) # check if the process has terminated # => if not kill it exitCode = self.process.poll() if exitCode != -15: try: logging.error("[%s]: Could not " % self.fileName + "terminate '%s'. Killing it." % self.description) self.process.kill() except: pass self.optionalData["exitCode"] = exitCode # set process to none so it can be newly started # in the next state update self.process = None # process has finished else: # Distinguish if we should parse the output or not. if self.parseOutput: # Parse output. output, err = self.process.communicate() if not self._parseOutput(output): logging.error("[%s] Not able to parse output " % self.fileName + "of sensor with id '%d'." % self.id) logging.error("[%s] Sensor with id '%d' stdout: %s" % (self.fileName, self.id, output)) logging.error("[%s] Sensor with id '%d' stderr: %s" % (self.fileName, self.id, err)) self.state = self.triggerState # Generate sensor alert object. self.sensorAlert = SensorAlert() self.sensorAlert.clientSensorId = self.id self.sensorAlert.state = 1 self.sensorAlert.hasOptionalData = True self.sensorAlert.optionalData = \ {"message": "Illegal output"} self.sensorAlert.changeState = True self.sensorAlert.hasLatestData = False self.sensorAlert.dataType = self.sensorDataType if self.sensorDataType == SensorDataType.NONE: self.sensorAlert.sensorData = None elif self.sensorDataType == SensorDataType.INT: self.sensorAlert.sensorData = 0 elif self.sensorDataType == SensorDataType.FLOAT: self.sensorAlert.sensorData = 0.0 self.shouldForceSendAlert = True else: self.hasOptionalData = True self.optionalData = dict() # check if the process has exited with code 0 # => everything works fine exitCode = self.process.poll() if exitCode == 0: self.state = 1 - self.triggerState # process did not exited correctly # => something is wrong with the service else: output, err = self.process.communicate() logging.error("[%s] Sensor with id '%d' stdout: %s" % (self.fileName, self.id, output)) logging.error("[%s] Sensor with id '%d' stderr: %s" % (self.fileName, self.id, err)) self.state = self.triggerState self.optionalData["exitCode"] = exitCode # set process to none so it can be newly started # in the next state update self.process = None
def _parseOutput(self, data): # Parse output data try: logging.debug("[%s] Received output from sensor with id '%d': %s" % (self.fileName, self.id, data)) message = json.loads(data) # Parse message depending on type. # Type: statechange if str(message["message"]).upper() == "STATECHANGE": # Check if state is valid. tempInputState = message["payload"]["state"] if not self._checkState(tempInputState): logging.error("[%s]: Received state " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False # Check if data type is valid. tempDataType = message["payload"]["dataType"] if not self._checkDataType(tempDataType): logging.error("[%s]: Received data type " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False # Set new data. if self.sensorDataType == SensorDataType.NONE: self.sensorData = None elif self.sensorDataType == SensorDataType.INT: self.sensorData = int(message["payload"]["data"]) elif self.sensorDataType == SensorDataType.FLOAT: self.sensorData = float(message["payload"]["data"]) # Force state change sending if the data could be changed # or the state has changed. if (self.sensorDataType != SensorDataType.NONE or self.state != tempInputState): # Create state change object that is # send to the server. self.stateChange = StateChange() self.stateChange.clientSensorId = self.id if tempInputState == self.triggerState: self.stateChange.state = 1 else: self.stateChange.state = 0 self.stateChange.dataType = tempDataType self.stateChange.sensorData = self.sensorData self.shouldForceSendState = True # Set state. self.state = tempInputState # Type: sensoralert elif str(message["message"]).upper() == "SENSORALERT": # Check if state is valid. tempInputState = message["payload"]["state"] if not self._checkState(tempInputState): logging.error("[%s]: Received state " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False # Check if hasOptionalData field is valid. tempHasOptionalData = message["payload"]["hasOptionalData"] if not self._checkHasOptionalData(tempHasOptionalData): logging.error("[%s]: Received hasOptionalData field " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False # Check if data type is valid. tempDataType = message["payload"]["dataType"] if not self._checkDataType(tempDataType): logging.error("[%s]: Received data type " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False if self.sensorDataType == SensorDataType.NONE: tempSensorData = None elif self.sensorDataType == SensorDataType.INT: tempSensorData = int(message["payload"]["data"]) elif self.sensorDataType == SensorDataType.FLOAT: tempSensorData = float(message["payload"]["data"]) # Check if hasLatestData field is valid. tempHasLatestData = message["payload"]["hasLatestData"] if not self._checkHasLatestData(tempHasLatestData): logging.error( "[%s]: Received hasLatestData field " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False # Check if changeState field is valid. tempChangeState = message["payload"]["changeState"] if not self._checkChangeState(tempChangeState): logging.error( "[%s]: Received changeState field " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False # Check if data should be transfered with the sensor alert # => if it should parse it tempOptionalData = None if tempHasOptionalData: tempOptionalData = message["payload"]["optionalData"] # check if data is of type dict if not isinstance(tempOptionalData, dict): logging.warning( "[%s]: Received optional data " % self.fileName + "from output of sensor with id '%d' " % self.id + "invalid. Ignoring output.") return False # Set optional data. self.hasOptionalData = tempHasOptionalData self.optionalData = tempOptionalData # Set new data. if tempHasLatestData: self.sensorData = tempSensorData # Set state. if tempChangeState: self.state = tempInputState # Create sensor alert object that is send to the server. self.sensorAlert = SensorAlert() self.sensorAlert.clientSensorId = self.id if tempInputState == self.triggerState: self.sensorAlert.state = 1 else: self.sensorAlert.state = 0 self.sensorAlert.hasOptionalData = tempHasOptionalData self.sensorAlert.optionalData = tempOptionalData self.sensorAlert.changeState = tempChangeState self.sensorAlert.hasLatestData = tempHasLatestData self.sensorAlert.dataType = tempDataType self.sensorAlert.sensorData = tempSensorData self.shouldForceSendAlert = True # Type: invalid else: raise ValueError("Received invalid message type.") except Exception as e: logging.exception("[%s]: Could not parse received data from " % self.fileName + "output of sensor with id '%d'." % self.id) return False return True
def forceSendAlert(self): # Check if we have exceeded the threshold of failed calendar # retrieval attempts and create a sensor alert if we have. sensorAlert = None if (not self.inFailedState and self.failedCounter > self.maxFailedAttempts): logging.warning( "[%s] Triggering sensor alert for " % self.fileName + "'%d' failed calendar fetching attempts." % self.failedCounter) sensorAlert = SensorAlert() sensorAlert.clientSensorId = self.id sensorAlert.state = 1 sensorAlert.hasOptionalData = True msg = "Failed more than %d times for '%s' " \ % (self.maxFailedAttempts, self.name) \ + "to retrieve calendar data." sensorAlert.optionalData = { "message": msg, "calendar": self.name, "type": "timeout" } sensorAlert.changeState = True sensorAlert.hasLatestData = False sensorAlert.dataType = SensorDataType.NONE self.state = self.triggerState self.inFailedState = True # If we are in a failed retrieval state and we could retrieve # calendar data again trigger a sensor alert for "normal". elif (self.inFailedState and self.failedCounter <= self.maxFailedAttempts): logging.warning( "[%s] Fetching calendar succeeded after " % self.fileName + "multiple failed attempts. Triggering sensor alert.") sensorAlert = SensorAlert() sensorAlert.clientSensorId = self.id sensorAlert.state = 0 sensorAlert.hasOptionalData = True msg = "Calendar data for '%s' retrievable again." \ % self.name sensorAlert.optionalData = { "message": msg, "calendar": self.name, "type": "timeout" } sensorAlert.changeState = True sensorAlert.hasLatestData = False sensorAlert.dataType = SensorDataType.NONE self.state = 1 - self.triggerState self.inFailedState = False # If we have sensor alerts of reminder waiting # return the oldest of them. elif not self.reminderAlertQueue.empty(): try: sensorAlert = self.reminderAlertQueue.get(True, 2) except: pass return sensorAlert
def _processEventAlarms(self, event, eventDatetime): # Create current datetime object. currentUTCTime = time.time() currentDatetime = datetime.datetime.utcfromtimestamp(currentUTCTime) currentDatetime = currentDatetime.replace(tzinfo=pytz.UTC) for alarm in event.walk("VALARM"): if alarm.has_key("TRIGGER"): # Get time delta when reminder is triggered. trigger = alarm.get("TRIGGER") # Get time when reminder is triggered. # When trigger time is a delta, then calculate the actual time. if type(trigger.dt) == datetime.timedelta: triggerDatetime = eventDatetime + trigger.dt # When trigger time is an actual time, use this. elif type(trigger.dt) == datetime.datetime: triggerDatetime = trigger.dt # Thunderbird uses the same timezone as the event when # no is given. if triggerDatetime.tzinfo is None: triggerDatetime = triggerDatetime.replace( tzinfo=eventDatetime.tzinfo) # When trigger time is only a date, start at midnight. elif type(trigger.dt) == datetime.date: triggerUTCTime = calendar.timegm(trigger.dt.timetuple()) triggerDatetime = datetime.datetime.utcfromtimestamp( triggerUTCTime) triggerDatetime = triggerDatetime.replace(tzinfo=pytz.UTC) else: logging.error("[%s] Error: Do not know how to handle " % self.fileName + "trigger type '%s'." % trigger.dt.__class__) continue # Uid of event is needed. uid = event.get("UID") # Get time when event starts. dtstart = event.get("DTSTART") # Check if we already triggered an alarm for the event # with this uid and the given alarm trigger time. if (uid, triggerDatetime) in self.alreadyTriggered: break # Check if the alarm trigger time lies in the past but not # more than 1 day. if ((currentDatetime - self.timedelta1day) <= triggerDatetime <= currentDatetime): title = event.get("SUMMARY") # Get description if event has one. evDescription = "" if event.has_key("DESCRIPTION"): evDescription = event.get("DESCRIPTION") # Get location if event has one. location = "" if event.has_key("LOCATION"): location = event.get("LOCATION") # Create the utc unix timestamp for the start of the event. unixStart = datetime.datetime.utcfromtimestamp(0) unixStart = unixStart.replace(tzinfo=pytz.UTC) utcDtstart = int( (eventDatetime - unixStart).total_seconds()) # Create the utc unix timestamp for the reminder trigger. utcTrigger = int( (triggerDatetime - unixStart).total_seconds()) eventDateStr = time.strftime("%D %H:%M:%S", time.localtime(utcDtstart)) msg = "Reminder for event '%s' at %s" \ % (title, eventDateStr) # Create sensor alert. sensorAlert = SensorAlert() sensorAlert.clientSensorId = self.id sensorAlert.state = 1 sensorAlert.hasOptionalData = True sensorAlert.optionalData = { "message": msg, "calendar": self.name, "type": "reminder", "title": title, "description": evDescription, "location": location, "trigger": utcTrigger, "start": utcDtstart } sensorAlert.changeState = False sensorAlert.hasLatestData = False sensorAlert.dataType = SensorDataType.NONE self.reminderAlertQueue.put(sensorAlert) # Store the event uid and the alarm trigger time # as already triggered. self.alreadyTriggered.add((uid, triggerDatetime))