示例#1
0
    def isEnabled(self, value):
        with self._lock:
            logger.reportDebug('{1}.isEnabled={0}, activationTimer is {2}'.format(value, self, self._activationTimer))
            if not value:
                self.stopActivationTimer()

            if self._enabledObject.value == value: return

            # Make sure this sensor is still required by the current mode.
            # Safer in case of data race between the thread that runs
            # updateModeFromLinknx and the one that runs the activation timer.
            if self.isRequiredByCurrentMode() or not value:
                self._enabledObject.value = value

            if value:
                try:
                    if self.persistenceObject != None:
                        self.persistenceObject.value = False
                    self.onEnabled()
                except Exception as e:
                    self._enabledObject.value = False
                    logger.reportException()
            else:
                # Sensor may currently be in alert.
                self.alert.removeSensorFromAlert(self)
                self.onDisabled()

            logger.reportInfo('Sensor {0} is now {1}'.format(self.name, 'enabled' if value else 'disabled'))
示例#2
0
 def sendEmailMock(self, actionXml):
     logger.reportDebug('sendEmailMock: {0}'.format(actionXml.toxml()))
     self.assertIsNone(
         self.emailInfo,
         'An unconsumed email is about to be deleted. It is likely to be an unexpected email. Details are {0}'
         .format(self.emailInfo))
     self.emailInfo = {'action': actionXml, 'date': time.ctime()}
     logger.reportInfo('sendEmail mock received {0}'.format(self.emailInfo))
示例#3
0
 def extend(self, starts = False):
     """ Prolong duration by the timeout amount of time. """
     if self.isTerminating:
         raise Exception('Timer {0} is terminating, it cannot be extended.'.format(self))
     self.endTime = time.time() + self.timeout
     if starts:
         logger.reportDebug('{0} started for {1} seconds.'.format(self, self.timeout))
     else:
         logger.reportDebug('{0} is extended by {1} seconds.'.format(self, self.timeout))
示例#4
0
文件: tests.py 项目: NhaTrang/pyknx
    def mockForTestMultipleConnections(self, context):
        logger.reportDebug('Mock called with objectId={0}'.format(context.objectId))
        self.callbackCalledFor.append(context.object)
        while self.blockCallback:
            time.sleep(0.5)
        logger.reportDebug('Mock ended for objectId={0}'.format(context.objectId))

        # Make sure next callback is blocked!
        self.blockCallback = True
示例#5
0
    def stop(self):
        with self._lock:
            if self.isStopped: return

            logger.reportDebug('Stopping {0}: sensorsInPrealert={1} sensorsInAlert={2}'.format(self, self._sensorsInPrealert, self._sensorsInAlert))
            hasChanged = len(self._sensorsInPrealert) + len(self._sensorsInAlert) != 0
            self._sensorsInPrealert.clear()
            self._sensorsInAlert.clear()
            self.invalidateStatus()
            self.updateStatus()
示例#6
0
    def testMultipleConnections(self):
        """ Checks that communicator can handle several connections at once (with a queue of connections when a connection is being treated). """
        # Set state to a known one.
        booleanObject = self.linknx.getObject('Boolean')
        floatObject = self.linknx.getObject('Float16')
        booleanObject.value = False
        floatObject.value = 0.0
        self.waitDuring(2, 'Initializing...')

        # Redirect some events to be able to block them.
        self.callbackCalledFor = []
        try:
            self.blockCallback = True

            with self.patchUserModule({
                    'onBooleanChanged':
                    self.mockForTestMultipleConnections,
                    'onFloatChanged':
                    self.mockForTestMultipleConnections
            }):

                # Change an object.
                booleanObject.value = True

                # Wait for callback.
                while not self.callbackCalledFor:
                    logger.reportDebug('Waiting for first callback...')
                    time.sleep(1)
                self.assertEqual(self.callbackCalledFor, [booleanObject])
                self.assertTrue(booleanObject.value)

                # Chain with second callback while first is blocked.
                floatObject.value = 1.0

                # Wait a few seconds, callback should not be called now (since it is not
                # reentrant).
                self.waitDuring(
                    5,
                    'Checking that second callback is not called until first callback is released.',
                    [
                        lambda: self.assertEqual(self.callbackCalledFor,
                                                 [booleanObject])
                    ])

                # Release callback.
                self.blockCallback = False

                # Wait for second callback.
                self.waitDuring(3, 'Waiting for second callback...')
                self.assertEqual(self.callbackCalledFor,
                                 [booleanObject, floatObject])

        finally:
            # Release all threads in case test went wrong.
            self.blockCallback = False
示例#7
0
    def mockForTestMultipleConnections(self, context):
        logger.reportDebug('Mock called with objectId={0}'.format(
            context.objectId))
        self.callbackCalledFor.append(context.object)
        while self.blockCallback:
            time.sleep(0.5)
        logger.reportDebug('Mock ended for objectId={0}'.format(
            context.objectId))

        # Make sure next callback is blocked!
        self.blockCallback = True
示例#8
0
    def stop(self):
        with self._lock:
            if self.isStopped: return

            logger.reportDebug(
                'Stopping {0}: sensorsInPrealert={1} sensorsInAlert={2}'.
                format(self, self._sensorsInPrealert, self._sensorsInAlert))
            hasChanged = len(self._sensorsInPrealert) + len(
                self._sensorsInAlert) != 0
            self._sensorsInPrealert.clear()
            self._sensorsInAlert.clear()
            self.invalidateStatus()
            self.updateStatus()
示例#9
0
    def run(self):
        try:
            logger.reportDebug('Starting {0}'.format(self))
            while not self.isTerminating:
                if self.onIterate is not None: self.onIterate(self)

                # Check for termination.
                if self.isTerminating:
                    return

                # Main loop.
                if not self.isPaused:
                    if self.endTime is None: self.extend(starts=True)
                    if time.time() > self.endTime:
                        # Execute delayed job.
                        logger.reportDebug('Timeout reached for {0}.'.format(self))
                        if callable(self.onTimeoutReached): self.onTimeoutReached(self)
                        break

                time.sleep(0.2)
        finally:
            if self.isTerminating:
                # Timer has been stopped from outside.
                logger.reportDebug('{0} is canceled.'.format(self))
            else:
                # Maybe useless but set it for consistency.
                self.isTerminating = True
            if callable(self.onTerminated): self.onTerminated(self)
            logger.reportDebug('{0} is now terminated.'.format(self))
            self.isTerminated = True
            self.isTerminating = False
示例#10
0
 def _loadUserFile(self):
     # Append the directory that contains the user script to python path.
     if self._userFile:
         dirName, fileName = os.path.split(self._userFile)
         dirName = os.path.abspath(dirName)
         moduleName, fileExt = os.path.splitext(fileName)
         sys.path.append(dirName)
         logger.reportDebug('_loadUserFile: moduleName={0} fileExt={1} dirName={2}'.format(moduleName, fileExt, dirName))
         self._userModule = importlib.import_module(moduleName)
         logger.reportDebug('Imported {0}'.format(self._userModule.__file__))
         return True
     else:
         logger.reportError('No user file specified.')
         return False
示例#11
0
 def run(self):
     try:
         self.socket.connect((self.linknx.host, self.linknx.port))
         logger.reportDebug('Message sent to linknx: ' +
                            self.messageWithEncodingHeader)
         answer = self.socket.sendString(self.messageWithEncodingHeader,
                                         encoding='utf8')
         while True:
             logger.reportDebug('Linknx answered ' + answer)
             answerDom = parseString(answer[0:answer.rfind(chr(4))])
             execNodes = answerDom.getElementsByTagName(
                 self.commandName)
             status = execNodes[0].getAttribute("status")
             if status == "ongoing":
                 # Wait for the final status.
                 answer = self.socket.waitForStringAnswer()
                 logger.reportDebug('New answer is {0}'.format(answer))
             else:
                 if status != "success":
                     self.error = self._getErrorFromXML(execNodes[0])
                     logger.reportError(self.error)
                 self.finalStatus = status
                 self.answerDom = answerDom
                 break
     finally:
         self.socket.close()
         if self.is_alive():
             logger.reportDebug('Thread is now stopped.')
示例#12
0
文件: linknx.py 项目: 2franix/pyknx
 def run(self):
     try:
         self.socket.connect((self.linknx.host, self.linknx.port))
         logger.reportDebug("Message sent to linknx: " + self.messageWithEncodingHeader)
         answer = self.socket.sendString(self.messageWithEncodingHeader, encoding="utf8")
         while True:
             logger.reportDebug("Linknx answered " + answer)
             answerDom = parseString(answer[0 : answer.rfind(chr(4))])
             execNodes = answerDom.getElementsByTagName(self.commandName)
             status = execNodes[0].getAttribute("status")
             if status == "ongoing":
                 # Wait for the final status.
                 answer = self.socket.waitForStringAnswer()
                 logger.reportDebug("New answer is {0}".format(answer))
             else:
                 if status != "success":
                     self.error = self._getErrorFromXML(execNodes[0])
                     logger.reportError(self.error)
                 self.finalStatus = status
                 self.answerDom = answerDom
                 break
     finally:
         self.socket.close()
         if self.isAlive():
             logger.reportDebug("Thread is now stopped.")
示例#13
0
 def _executeUserCallback(self, callbackName, context, isOptional=False):
     try:
         if hasattr(self._userModule, callbackName):
             logger.reportDebug('Calling user callback {0} with context {1}'.format(callbackName, context))
             callback = getattr(self._userModule, callbackName)
             res = callback(context)
             logger.reportDebug('Callback {0} returned {1}'.format(callbackName, res))
             return res
         else:
             message='No function {0} defined in {1}'.format(callbackName, self._userFile)
             if isOptional:
                 logger.reportInfo(message + ', skipping')
             else:
                 logger.reportWarning(message)
     except Exception as e:
         logger.reportException('User code execution failed.')
示例#14
0
 def executeAction(self, actionXml):
     logger.reportDebug('executeActionMock: {0}'.format(
         actionXml.toxml()))
     actionNode = actionXml.getElementsByTagName('action')[0]
     if actionNode.getAttribute('type') == 'shell-cmd':
         self.test.assertIsNone(
             self.test.shellCmdInfo,
             'An unconsumed shell command is about to be deleted. It is likely to be an unexpected shell command. Details are {0}'
             .format(self.test.shellCmdInfo))
         self.test.shellCmdInfo = {
             'action': actionXml,
             'date': time.ctime()
         }
         logger.reportInfo('executeAction mock received {0}'.format(
             self.test.shellCmdInfo))
     self.realExecute(actionXml)
示例#15
0
文件: linknx.py 项目: 2franix/pyknx
    def value(self, objValue):
        """ Write object's value to linknx. """
        # Convert value to the linknx format.
        logger.reportDebug("Attempting to set value of {0} to {1}".format(self._id, objValue))
        objectValue = self.convertValueToString(objValue)

        if not objValue is objectValue:
            logger.reportDebug("Value has been converted to " + str(objectValue))

        # Initialize DOM with a simple string, then use minidom to write
        # attributes so that special characters are properly encoded (for
        # instance, &ampersand; in place of &, etc).
        messageDom = parseString("<write><object/></write>")
        objectNode = messageDom.getElementsByTagName("object")[0]
        objectNode.setAttribute("id", self._id)
        objectNode.setAttribute("value", objectValue)
        answerDom = self._linknx._sendMessage("Write {0}={1}".format(self.id, objValue), messageDom.toxml(), "write")
示例#16
0
    def onPersistentAlertChanged(self, persistentObject):
        logger.reportDebug('onPersistentAlertChanged {0}={1}'.format(persistentObject.id, persistentObject.value))

        # Do nothing when persistent alert becomes true.
        if persistentObject.value: return

        # Reset persistence of sensors that belong to this alert type.
        deactivatedAlert = self.getAlertByPersistenceObjectId(persistentObject.id)

        # Not found.
        if deactivatedAlert is None:
            raise Exception('Persistent alert object does not match any alert type.')

        deactivatedAlert.stop()

        # Reset persistent alert for all sensors.
        for sensor in [s for s in self.sensors if s.alert == deactivatedAlert]:
            if sensor.persistenceObject != None: sensor.persistenceObject.value = False
示例#17
0
文件: linknx.py 项目: 2franix/pyknx
    def executeAction(self, actionDetails):
        if isinstance(actionDetails, str):
            actionXML = actionDetails
        elif isinstance(actionDetails, Document):
            actionXML = actionDetails.childNodes[0].toxml()
        elif isinstance(actionDetails, Element):
            actionXML = actionDetails.toxml()
        else:
            raise Exception("Unsupported action details: must be a minidom XML document or element or an XML string.")

        # Build XML document to send to linknx.
        self._sendMessage(
            "Execute {0}".format(actionXML),
            "<execute>{action}</execute>".format(action=actionXML),
            "execute",
            waitsForAnswer=False,
        )
        logger.reportDebug("Action execution has been sent to linknx.")
示例#18
0
    def executeAction(self, actionDetails):
        if isinstance(actionDetails, str):
            actionXML = actionDetails
        elif isinstance(actionDetails, Document):
            actionXML = actionDetails.childNodes[0].toxml()
        elif isinstance(actionDetails, Element):
            actionXML = actionDetails.toxml()
        else:
            raise Exception(
                'Unsupported action details: must be a minidom XML document or element or an XML string.'
            )

        # Build XML document to send to linknx.
        self._sendMessage(
            'Execute {0}'.format(actionXML),
            '<execute>{action}</execute>'.format(action=actionXML),
            'execute',
            waitsForAnswer=False)
        logger.reportDebug('Action execution has been sent to linknx.')
示例#19
0
文件: tests.py 项目: NhaTrang/pyknx
    def testMultipleConnections(self):
        """ Checks that communicator can handle several connections at once (with a queue of connections when a connection is being treated). """
        # Set state to a known one.
        booleanObject = self.linknx.getObject('Boolean')
        floatObject = self.linknx.getObject('Float16')
        booleanObject.value = False
        floatObject.value = 0.0
        self.waitDuring(2, 'Initializing...')

        # Redirect some events to be able to block them.
        self.callbackCalledFor = []
        try:
            self.blockCallback = True

            with self.patchUserModule({'onBooleanChanged' : self.mockForTestMultipleConnections, 'onFloatChanged' : self.mockForTestMultipleConnections}):

                # Change an object.
                booleanObject.value = True

                # Wait for callback.
                while not self.callbackCalledFor:
                    logger.reportDebug('Waiting for first callback...')
                    time.sleep(1)
                self.assertEqual(self.callbackCalledFor, [booleanObject])
                self.assertTrue(booleanObject.value)

                # Chain with second callback while first is blocked.
                floatObject.value = 1.0

                # Wait a few seconds, callback should not be called now (since it is not
                # reentrant).
                self.waitDuring(5, 'Checking that second callback is not called until first callback is released.', [lambda: self.assertEqual(self.callbackCalledFor, [booleanObject])])

                # Release callback.
                self.blockCallback = False

                # Wait for second callback.
                self.waitDuring(3, 'Waiting for second callback...')
                self.assertEqual(self.callbackCalledFor, [booleanObject, floatObject])

        finally:
            # Release all threads in case test went wrong.
            self.blockCallback = False
示例#20
0
    def value(self, objValue):
        """ Write object's value to linknx. """
        # Convert value to the linknx format.
        logger.reportDebug('Attempting to set value of {0} to {1}'.format(
            self._id, objValue))
        objectValue = self.convertValueToString(objValue)

        if not objValue is objectValue:
            logger.reportDebug('Value has been converted to ' +
                               str(objectValue))

        # Initialize DOM with a simple string, then use minidom to write
        # attributes so that special characters are properly encoded (for
        # instance, &ampersand; in place of &, etc).
        messageDom = parseString('<write><object/></write>')
        objectNode = messageDom.getElementsByTagName('object')[0]
        objectNode.setAttribute('id', self._id)
        objectNode.setAttribute('value', objectValue)
        answerDom = self._linknx._sendMessage(
            'Write {0}={1}'.format(self.id, objValue), messageDom.toxml(),
            'write')
示例#21
0
    def onPersistentAlertChanged(self, persistentObject):
        logger.reportDebug('onPersistentAlertChanged {0}={1}'.format(
            persistentObject.id, persistentObject.value))

        # Do nothing when persistent alert becomes true.
        if persistentObject.value: return

        # Reset persistence of sensors that belong to this alert type.
        deactivatedAlert = self.getAlertByPersistenceObjectId(
            persistentObject.id)

        # Not found.
        if deactivatedAlert is None:
            raise Exception(
                'Persistent alert object does not match any alert type.')

        deactivatedAlert.stop()

        # Reset persistent alert for all sensors.
        for sensor in [s for s in self.sensors if s.alert == deactivatedAlert]:
            if sensor.persistenceObject != None:
                sensor.persistenceObject.value = False
示例#22
0
        def run(self):
            logger.reportInfo('Listening on ' + str(self._address))
            self._isStopRequested = False
            try:
                self._socket.bind(self._address)

                # Thread loop.
                while not self._isStopRequested:
                    self.isReady = True
                    data, conn = self._socket.waitForString(endChar='$')
                    # Throw data away if script has not been initialized yet.
                    # See startListening for details.
                    if data is None or not self._communicator.isUserScriptInitialized:
                        time.sleep(0.1)
                        continue

                    logger.reportDebug('Data received: {0}'.format(data))

                    # Handle request.
                    tokens = data.split('|')
                    callbackName = tokens[0]
                    # Parse arguments. First is object id.
                    args={}
                    for token in tokens[1:]:
                        argName, sep, argValue = token.partition('=')
                        if argValue: argValue = argValue.strip()
                        args[argName.strip()] = argValue
                    context = CallbackContext(self, args)
                    res = self._communicator._executeUserCallback(callbackName, context)
                    if res:
                        conn.sendall(res + '$')
                    conn.close()
            except Exception as e:
                logger.reportException()
            finally:
                logger.reportDebug('Closing socket...')
                self._socket.close()
                logger.reportInfo('Socket closed. Listening terminated.')
                self._socket = None
示例#23
0
    def fireEvent(self, eventType, description, context):
        """ Raises event (i.e executes every action related to this event). """
        logger.reportDebug('Firing event {0}'.format(description))
        for event in self.eventConfigs:
            if event.type != eventType: continue

            # Set up the various actions for that event type.
            logger.reportDebug('Executing actions {0}'.format(event.actions))
            for actionConfig in event.actions:
                if actionConfig.type == 'send-email':
                    action = SendEmailAction(self.daemon, actionConfig)
                elif actionConfig.type == 'send-sms':
                    action = SendSMSAction(self.daemon, actionConfig)
                else:
                    # Delegate execution to linknx.
                    action = LinknxAction(self.daemon, actionConfig)

                action.execute(context)
        logger.reportDebug('Event {0} is now finished.'.format(description))
示例#24
0
    def fireEvent(self, eventType, description, context):
        """ Raises event (i.e executes every action related to this event). """
        logger.reportDebug('Firing event {0}'.format(description))
        for event in self.eventConfigs:
            if event.type != eventType: continue

            # Set up the various actions for that event type.
            logger.reportDebug('Executing actions {0}'.format(event.actions))
            for actionConfig in event.actions:
                if actionConfig.type == 'send-email':
                    action = SendEmailAction(self.daemon, actionConfig)
                elif actionConfig.type == 'send-sms':
                    action = SendSMSAction(self.daemon, actionConfig)
                elif actionConfig.type == 'shell-cmd':
                    action = ShellCommandAction(self.daemon, actionConfig)
                else:
                    # Delegate execution to linknx.
                    action = LinknxAction(self.daemon, actionConfig)

                action.execute(context)
        logger.reportDebug('Event {0} is now finished.'.format(description))
示例#25
0
def onModeObjectChanged(context):
    global alarmDaemon
    logger.reportDebug('Alarm mode changed to ' + str(context.object.value))
    alarmDaemon.onModeValueChanged(context.object.value)
示例#26
0
 def sendEmailMock(self, actionXml):
     logger.reportDebug('sendEmailMock: {0}'.format(actionXml.toxml()))
     self.assertIsNone(self.emailInfo, 'An unconsumed email is about to be deleted. It is likely to be an unexpected email. Details are {0}'.format(self.emailInfo))
     self.emailInfo = {'action' : actionXml, 'date' : time.ctime()}
     logger.reportInfo('sendEmail mock received {0}'.format(self.emailInfo))
示例#27
0
    def doTestIntrusion(self, togglesSensorBeforeEndOfPrealert, shuntsprealertWithFasterSensor, cancelsAlarm, testsInhibition):
        """ Test exercising the alert handling when an intrusion is detected.  """
        logger.reportInfo('\n\n*********INITIALIZE testIntrusion togglesSensorBeforeEndOfPrealert={0} shuntsprealertWithFasterSensor={1} cancelsAlarm={2}********************'.format(togglesSensorBeforeEndOfPrealert, shuntsprealertWithFasterSensor, cancelsAlarm))
        daemon = self.alarmDaemon

        self.assertTrue(togglesSensorBeforeEndOfPrealert ^ shuntsprealertWithFasterSensor ^ cancelsAlarm ^ testsInhibition)

        # Prepare sensors involved in this test.
        entranceSensor = daemon.getSensorByName('EntranceDoorOpening')
        livingRoomWindowSensor = daemon.getSensorByName('LivingRoomWindowOpening')
        kitchenWindowSensor = daemon.getSensorByName('KitchenWindowOpening')
        intrusionAlert = daemon.getAlertByName('Intrusion')

        # Initialize state to a known one.
        self.alarmModeObject.value = 1
        for sensor in (entranceSensor, livingRoomWindowSensor, kitchenWindowSensor):
            sensor.watchedObject.value = False

        self.waitDuring(1.5, 'Initializing')
        modeChangeTime = time.time()
        self.emailInfo = None # In case mode initialization has raised an email.
        self.changeAlarmMode('Away', '*****@*****.**')

        self.waitUntil(modeChangeTime + entranceSensor.getActivationDelay() + 0.5, 'Waiting for door to be enabled.')
        for sensor in (entranceSensor, livingRoomWindowSensor, kitchenWindowSensor):
            self.assertTrue(sensor.isEnabled, '{0} should now be enabled.'.format(sensor))
        self.assertEqual(entranceSensor.getPrealertDuration(), 6)

        # Step inside home.
        firstTriggerTime = time.time()
        entranceSensor.watchedObject.value = True

        sensorsInPrealert = [entranceSensor]
        sensorsInAlert = []
        sensorsInPersistentAlert = []
        checkAlertStatus = lambda: self.assertAlert(sensorsInPrealert, sensorsInAlert, sensorsInPersistentAlert)
        intermediaryDelay = entranceSensor.getPrealertDuration() / 4
        if togglesSensorBeforeEndOfPrealert:
            # Release sensor as quickly as possible.
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            entranceSensor.watchedObject.value = False

            # Toggle sensor again.
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            entranceSensor.watchedObject.value = True

            # Release again and leave it in that state until end of prealert (to
            # make sure alert state is not taken from the current sensor
            # status).
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            entranceSensor.watchedObject.value = False

        if cancelsAlarm:
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            self.changeAlarmMode('Presence', '*****@*****.**') # Takes some time.
            del(sensorsInPrealert[:])
            for sensor in (entranceSensor, livingRoomWindowSensor, kitchenWindowSensor):
                self.assertFalse(sensor.isEnabled, '{0} should not be enabled anymore.'.format(sensor))

            # Make sure alert is not raised after prealert delay of entrance
            # door.
            self.waitUntil(firstTriggerTime + entranceSensor.getPrealertDuration() + 1.5, 'Wait a few seconds to make sure no alert is being raised.', [checkAlertStatus])
        else:
            if shuntsprealertWithFasterSensor:
                # Prealert duration is driven by living room window, as it is a faster sensor
                # than entrance door.
                self.waitDuring(intermediaryDelay, [checkAlertStatus])
                sensorsInPrealert.append(livingRoomWindowSensor)
                livingRoomWindowSensor.watchedObject.value = True
                # Check that living room window will raise alert faster than
                # entrance door (the opposite would denote a configuration
                # error).
                remainingTimeBeforeDoorAlert = entranceSensor.getPrealertDuration() - (time.time() - firstTriggerTime)
                logger.reportDebug('Remaining time before door alert: {0}s, before living room alert: {1} (expected to be shorter in living room!)'.format(remainingTimeBeforeDoorAlert, livingRoomWindowSensor.getPrealertDuration()))
                self.assertLess(livingRoomWindowSensor.getPrealertDuration(), remainingTimeBeforeDoorAlert, 'Living room window will not raise alert before entrance door, this test is not properly set up.')
                self.waitDuring(livingRoomWindowSensor.getPrealertDuration() + 1, 'Let living room window prealert pass.', [checkAlertStatus], 0.2, 1.2)
            else:
                # Normal prealert with entrance door.
                self.waitUntil(firstTriggerTime + entranceSensor.getPrealertDuration() + 1, 'Let prealert delay pass.', [checkAlertStatus], 0.5, 1.2)

            # Whichever strategy should now lead to entrance door being in alert
            # (either because of its own prealert or because an intrusion alert has
            # been raised by kitchen blinds meanwhile).
            sensorsInAlert.extend(sensorsInPrealert)
            sensorsInPersistentAlert.extend(sensorsInAlert)
            del(sensorsInPrealert[:])

            # Wait for first sensor to quit alert. At this point, entranceSensor
            # should already have been in alert for 1 second.
            self.assertTrue(entranceSensor.getAlertDuration() < livingRoomWindowSensor.getAlertDuration(), 'This test assumes that door\'s alert is shorter than kitchen\'s one.')
            self.assertEmail('Sensor joined', ['*****@*****.**'], 'Alert Intrusion: sensor joined', [])
            self.waitDuring(entranceSensor.getAlertDuration() - 0.5, 'Wait for first sensor to quit alert.', [checkAlertStatus], 0, 1)
            sensorsInAlert.remove(entranceSensor)
            self.assertFalse(entranceSensor.isAlertActive)

            # Wait for the end of second sensor's alert.
            self.waitUntil(firstTriggerTime + intermediaryDelay + livingRoomWindowSensor.getPrealertDuration() + livingRoomWindowSensor.getAlertDuration() + 1, 'Waiting for second sensor to quit alert.', [checkAlertStatus], 0, 1)
            usesLivingWindow = livingRoomWindowSensor in sensorsInAlert
            if usesLivingWindow: sensorsInAlert.remove(livingRoomWindowSensor)

            # Check that test is properly set up at this point.
            self.assertFalse(sensorsInAlert)
            self.assertFalse(sensorsInPrealert)
            self.assertEqual(set(sensorsInPersistentAlert), set([livingRoomWindowSensor, entranceSensor] if usesLivingWindow else [entranceSensor]))

            # Wait a few seconds more, to check everything stays ok.
            self.waitDuring(5, 'Checking that no event occurs...', [checkAlertStatus])

            if testsInhibition:
                # Relaunch alert => shunt prealert and go to alert immediately.
                sensorsInAlert.append(entranceSensor)
                sensorsInPersistentAlert.append(entranceSensor)
                entranceSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing entrance door.')
                entranceSensor.watchedObject.value = True
                self.waitDuring(0.5, 'Waiting for alert to be reraised...')
                self.assertEmail('Sensor joined', ['*****@*****.**'], 'Alert Intrusion: sensor joined', [])
                checkAlertStatus()

                # Stop current alert without inhibiting for now.
                intrusionAlert.persistenceObject.value = False
                sensorsInAlert.remove(entranceSensor)
                sensorsInPersistentAlert.clear()
                self.waitDuring(0.5, 'Stopping current alert without inhibiting...')
                checkAlertStatus()

                # Relaunch alert again.
                entranceSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing entrance door.')
                entranceSensor.watchedObject.value = True
                self.waitDuring(0.2, 'Reopening entrance door.')
                entranceSensor.watchedObject.value = False
                sensorsInPrealert.append(entranceSensor)
                self.waitDuring(5.9, 'Waiting for alert to be reraised...', [checkAlertStatus], 0.5, 0.2)
                self.assertEmail('Sensor joined', ['*****@*****.**'], 'Alert Intrusion: sensor joined', [])
                sensorsInPrealert.remove(entranceSensor)
                sensorsInAlert.append(entranceSensor)
                sensorsInPersistentAlert.append(entranceSensor)
                checkAlertStatus()

                # Now inhibit intrusion alert.
                intrusionAlert.inhibitionObject.value = True
                intrusionAlert.persistenceObject.value = False
                del(sensorsInAlert[:])
                del(sensorsInPersistentAlert[:])
                self.waitDuring(0.6, 'Inhibiting intrusion alert...')
                checkAlertStatus()

                # Trigger another sensor to make sure alert is really inhibited.
                kitchenWindowSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing kitchen window.')
                kitchenWindowSensor.watchedObject.value = True
                self.waitDuring(kitchenWindowSensor.getPrealertDuration() + 1.5, 'Checking that opening sesnors do not fire alert anymore (alert is inhibited)...', [checkAlertStatus])

                # Remove inhibition: nothing should occur until a sensor gets
                # triggered again (currently triggered sensors are still ignored).
                intrusionAlert.inhibitionObject.value = False
                self.waitDuring(4.5, 'Checking no event occurs...', [checkAlertStatus], 1.5)

                # Close window and open it again: alert!
                kitchenWindowSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing kitchen window.', [checkAlertStatus])
                triggerTime = time.time()
                kitchenWindowSensor.watchedObject.value = True
                sensorsInPrealert.append(kitchenWindowSensor)
                self.waitDuring(kitchenWindowSensor.getPrealertDuration() + 0.2, 'Opening door again...', [checkAlertStatus], 0.2, 0.4)
                sensorsInPrealert.remove(kitchenWindowSensor)
                sensorsInAlert.append(kitchenWindowSensor)
                sensorsInPersistentAlert.append(kitchenWindowSensor)
                self.assertEmail('Sensor joined', ['*****@*****.**'], 'Alert Intrusion: sensor joined', [])
                self.waitUntil(triggerTime + kitchenWindowSensor.getPrealertDuration() + kitchenWindowSensor.getAlertDuration(), 'Checking door alert...', [checkAlertStatus], 0.2, 0.2)
                sensorsInAlert.remove(kitchenWindowSensor)
                self.waitUntil(2, 'Alert should now be paused...', [checkAlertStatus], 0.2, 0)
示例#28
0
 def stop(self):
     if not self.isCancelled:
         self.isCancelled = True
         self.isTerminating = True
         logger.reportDebug('Cancelling {0}.'.format(self))
示例#29
0
 def __enter__(self):
     for k, v in self.patches.items():
         logger.reportDebug('Patching function object {0}.{1}={2}'.format(
             self.module, k, v))
         self.originalMethodObjects[k] = getattr(self.module, k)
         setattr(self.module, k, v)
示例#30
0
 def __exit__(self, exc_type, exc_value, traceback):
     for k, v in self.originalMethodObjects.items():
         logger.reportDebug('Restoring function object {0}.{1}={2}'.format(
             self.module, k, v))
         setattr(self.module, k, v)
示例#31
0
    def generateConfig(self):
        # Read xml to get pyknx special attributes.
        config = self.config
        doc = config.ownerDocument
        rulesNode = self._getOrAddConfigElement(config, 'rules')

        # Generate a rule for each object that has a callback in the user file.
        objectNodes = config.getElementsByTagName('objects')[0]
        configuredAtLeastOne = False
        definesLegacyCallbackAttribute = False
        callbackAttributeName = self.callbackAttributeName
        for objectNode in objectNodes.getElementsByTagName('object'):
            objectConfig = ObjectConfig(objectNode)
            objectId = objectConfig.id
            callback = objectNode.getAttribute(callbackAttributeName)
            if callback == None or callback == '':
                if objectNode.getAttribute('pyknxcallback'):
                    logger.reportError('pyknxcallback found on {0}'.format(objectNode.toxml()))
                    definesLegacyCallbackAttribute = True
                logger.reportDebug('No callback found for object ' + objectConfig.id + ' (no {0} attribute for this object)'.format(callbackAttributeName))
                continue

            configuredAtLeastOne = True
            ruleNode = doc.createElement('rule')
            ruleId = '{0}{1}'.format(self._communicatorName, objectId)
            logger.reportInfo('Generating rule {0}'.format(ruleId))
            ruleNode.setAttribute('id', ruleId)
            ruleNode.setAttribute('init', 'false')
            conditionNode = doc.createElement('condition')
            conditionNode.setAttribute('type', 'object')
            conditionNode.setAttribute('id', objectId)
            # conditionNode.setAttribute('value', objectConfig.defaultValue)
            conditionNode.setAttribute('trigger', 'true')
            ruleNode.appendChild(conditionNode)
            actionListNode = doc.createElement('actionlist')
            actionListNode.setAttribute('type', 'if-true')
            ruleNode.appendChild(actionListNode)
            actionNode = self.createActionNode(callback, {'objectId' : objectId})
            actionListNode.appendChild(actionNode)
            # actionListIfFalseNode = actionListNode.cloneNode(True)
            # actionListIfFalseNode.setAttribute('type', 'on-false')
            # # ruleNode.appendChild(actionListIfFalseNode)
            rulesNode.appendChild(ruleNode)

        if not configuredAtLeastOne:
            logger.reportInfo('Nothing to do. None of the objects does define a callback attribute.')
            if definesLegacyCallbackAttribute:
                logger.reportWarning('There is at least one pyknxcallback attribute in the config file. These attributes were recognized by Pyknx before version 2.2. Did you forget to rename them to {0}?'.format(callbackAttributeName))
        else:
            # Add an ioport service for the communicator.
            servicesNode = self._getOrAddConfigElement(config, 'services')
            ioportsNode = self._getOrAddConfigElement(servicesNode, 'ioports')
            ioportNode = doc.createElement('ioport')
            ioportNode.setAttribute('id', self._communicatorName)
            try:
                hostIP = socket.gethostbyname(self._address[0])
            except:
                logger.reportWarning('Could not check that {0} is a valid ip address. Please check the output configuration. Linknx does not support hostnames, it requires IP address.'.format(self._address[0]))
                hostIP = self._address[0]
            ioportNode.setAttribute('host', hostIP) #gethostbyname converts the hostname into an ip. Linknx does not support ioport hostnames.
            ioportNode.setAttribute('port', str(self._address[1]))
            ioportNode.setAttribute('type', 'tcp')
            ioportsNode.appendChild(ioportNode)
示例#32
0
    def updateStatus(self):
        """
        Updates the status of this alert and raises the required events accordingly.

        """
        # Do not update if the daemon is in a process that may trigger
        # irrelevant intermediary states.
        if self.daemon.areAlertStatusUpdatesSuspended: return

        if not self.isStatusDirty:
            logger.reportDebug('Status of {0} is already up-to-date, nothing to change.'.format(self))
            return
        logger.reportDebug('Updating status of {0}'.format(self))

        # Compute current status.
        if self._sensorsInAlert:
            newStatus = Alert.Status.ACTIVE
        elif self._sensorsInPrealert:
            newStatus = Alert.Status.INITIALIZING
        elif self.status == Alert.Status.ACTIVE and (self.persistenceObject != None and self.persistenceObject.value):
            # PAUSED status may only occur if persistence is supported.
            # Otherwise, as soon as last sensor leaves the alert, alert is
            # stopped and will start if a sensor gets triggered afterwards. This
            # is not the most convenient behaviour but with it, the user is free not to
            # define persistence.
            newStatus = Alert.Status.PAUSED
        else:
            newStatus = Alert.Status.STOPPED

        logger.reportDebug('New status for {0} is {1}'.format(self, newStatus))

        # When the alert is active, all sensors should leave the "prealert"
        # state to join the alert.
        if newStatus in (Alert.Status.ACTIVE, Alert.Status.PAUSED): # PAUSED is to be on the safe side as alert should always go through the ACTIVE state before going to PAUSED.
            for sensor in self._sensorsInPrealert:
                if not sensor in self._sensorsInAlert:
                    self._sensorsInAlert.add(sensor) # None at this point. Timers will be created later in this method.   # sensor.makeAlertTimer(onTimeoutReached=None, onTerminated=lambda: self.removeSensorFromAlert(sensor))
            self._sensorsInPrealert = set()

        # Diff registered sensors.
        joiningSensors = self._sensorsInAlert - self._sensorsInAlertOnLastUpdateStatus
        leavingSensors = self._sensorsInAlertOnLastUpdateStatus- self._sensorsInAlert
        logger.reportDebug('Updating status for {0}: joiningSensors={1}, leavingSensors={2}'.format(self, joiningSensors, leavingSensors))

        if newStatus == Alert.Status.ACTIVE:
            if self.persistenceObject != None: self.persistenceObject.value = True

        # Handle consequences of status change.
        if self.status == Alert.Status.STOPPED:
            if newStatus == Alert.Status.STOPPED:
                # No change.
                pass
            elif newStatus == Alert.Status.INITIALIZING:
                self.notifyAlertStarted()
            else:
                # Should not happen.
                logger.reportError('Unsupported switch from "{old}" to "{new}" for alert {alert}'.format(alert=self, old=self.status, new=newStatus))
        elif self.status == Alert.Status.ACTIVE:
            if newStatus == Alert.Status.ACTIVE:
                # Check if a sensor joined or left.
                if joiningSensors:
                    self.notifySensorJoined()
                if leavingSensors:
                    self.notifySensorLeft()
            elif newStatus in (Alert.Status.PAUSED, Alert.Status.STOPPED):
                if not leavingSensors:
                    logger.reportError('A sensor should have left the alert.')
                else:
                    self.notifySensorLeft()

                self.notifyAlertDeactivated()

                if newStatus == Alert.Status.STOPPED:
                    self.notifyAlertReset()
                    self.notifyAlertStopped()
                elif newStatus == Alert.Status.PAUSED:
                    self.notifyAlertPaused()
                else:
                    raise Exception('Not implemented.')

            else:
                # Should not happen.
                logger.reportError('Unsupported switch from "{old}" to "{new}" for alert {alert}'.format(alert=self, old=self.status, new=newStatus))
        elif self.status == Alert.Status.PAUSED:
            if newStatus == Alert.Status.PAUSED:
                # No change.
                pass
            elif newStatus == Alert.Status.STOPPED:
                self.notifyAlertReset()
                self.notifyAlertStopped()
            elif newStatus == Alert.Status.ACTIVE:
                self.notifyAlertResumed()
                if not joiningSensors:
                    logger.reportError('A sensor should have joined the alert.')
                else:
                    self.notifySensorJoined()
                self.notifyAlertActivated()
        elif self.status == Alert.Status.INITIALIZING:
            if newStatus == Alert.Status.INITIALIZING:
                # No change.
                pass
            elif newStatus == Alert.Status.ACTIVE:
                # Events to raise: started, sensor-joined, activated.
                if not joiningSensors:
                    logger.reportError('A sensor should have joined the alert.')
                else:
                    self.notifySensorJoined()
                self.notifyAlertActivated()
            elif newStatus == Alert.Status.STOPPED:
                self.notifyAlertAborted()
                self.notifyAlertStopped()

        # Stop obsolete timers for all sensors related to this alert.
        if newStatus in (Alert.Status.PAUSED, Alert.Status.STOPPED) or (self.status == Alert.Status.INITIALIZING and newStatus == Alert.Status.ACTIVE):
            for sensor in self.sensors:
                # Get the optional timer currently running for this sensor.
                timer = self._sensorTimers.get(sensor)
                if timer == None: continue

                timer.stop()
                timer = None
                del self._sensorTimers[sensor]

        for sensor in self._sensorsInAlert.union(self._sensorsInPrealert):
            # Start a new timer?
            if newStatus in (Alert.Status.INITIALIZING, Alert.Status.ACTIVE) and self._sensorTimers.get(sensor) == None:
                timer = sensor.makePrealertTimer() if newStatus == Alert.Status.INITIALIZING else sensor.makeAlertTimer()
                self._sensorTimers[sensor] = timer # Prealert timer has been deleted above if applicable.
                timer.start()

        # Update persistence objects for all sensors.
        for s in self._sensorsInAlert:
            if s.persistenceObject != None:
                s.persistenceObject.value = True

        # Store current status.
        self._sensorsInAlertOnLastUpdateStatus = self._sensorsInAlert.copy()
        self.status = newStatus
        self.isStatusDirty = False
示例#33
0
 def getActivationDelay(self):
     delay = self._config.activationDelay.getForMode(self.daemon.currentMode.name)
     logger.reportDebug('getActivationDelay of {2} for {0}, currentMode={1}'.format(self, self.daemon.currentMode, delay))
     return delay
示例#34
0
文件: base.py 项目: 2franix/pyknx
 def __exit__(self, exc_type, exc_value, traceback):
     for k,v in self.originalMethodObjects.items():
         logger.reportDebug('Restoring function object {0}.{1}={2}'.format(self.module, k, v))
         setattr(self.module, k, v)
示例#35
0
 def forceTimeout(self):
     logger.reportDebug('Forcing timeout of {0}'.format(self))
     self.endTime = 0
示例#36
0
    # Configure logger.
    logger.initLogger(None, args.verbosityLevel.upper())

    # The homewatcher daemon is represented by an instance of
    # a pyknx.communicator.Communicator that runs with an "user script" dedicated to
    # interfacing linknx with homewatcher's capabilities.
    # First: read homewatcher config to read the linknx server url.
    # Second: start pyknxcommunicator with homewatcher's user script.
    logger.reportInfo(
        'Reading config file {file}'.format(file=args.homewatcherConfig))
    config = configuration.Configuration.parseFile(args.homewatcherConfig)
    userScript = os.path.join(os.path.dirname(configuration.__file__),
                              'linknxuserfile.py')
    logger.reportDebug(
        'Pyknx\'s user script for homewatcher is {script}'.format(
            script=userScript))
    userScriptArgs = {'hwconfig': config}
    services = config.servicesRepository
    communicatorAddress = (services.daemon.host, services.daemon.port)
    logger.reportInfo(
        'Starting Homewatcher at {communicatorAddr}, linked to linknx at {linknxAddr}'
        .format(communicatorAddr=communicatorAddress,
                linknxAddr=services.linknx.address))
    linknx = linknx.Linknx(services.linknx.host, services.linknx.port)
    communicator.Communicator.run(linknxAddress=linknx.address,
                                  userFile=userScript,
                                  communicatorAddress=communicatorAddress,
                                  userScriptArgs=userScriptArgs,
                                  verbosityLevel=args.verbosityLevel,
                                  logFile=args.logFile,
示例#37
0
文件: base.py 项目: 2franix/pyknx
 def __enter__(self):
     for k,v in self.patches.items():
         logger.reportDebug('Patching function object {0}.{1}={2}'.format(self.module, k, v))
         self.originalMethodObjects[k] = getattr(self.module, k)
         setattr(self.module, k, v)
示例#38
0
    def doTestIntrusion(self, togglesSensorBeforeEndOfPrealert,
                        shuntsprealertWithFasterSensor, cancelsAlarm,
                        testsInhibition):
        """ Test exercising the alert handling when an intrusion is detected.  """
        logger.reportInfo(
            '\n\n*********INITIALIZE testIntrusion togglesSensorBeforeEndOfPrealert={0} shuntsprealertWithFasterSensor={1} cancelsAlarm={2}********************'
            .format(togglesSensorBeforeEndOfPrealert,
                    shuntsprealertWithFasterSensor, cancelsAlarm))
        daemon = self.alarmDaemon

        self.assertTrue(togglesSensorBeforeEndOfPrealert
                        ^ shuntsprealertWithFasterSensor ^ cancelsAlarm
                        ^ testsInhibition)

        # Prepare sensors involved in this test.
        entranceSensor = daemon.getSensorByName('EntranceDoorOpening')
        livingRoomWindowSensor = daemon.getSensorByName(
            'LivingRoomWindowOpening')
        kitchenWindowSensor = daemon.getSensorByName('KitchenWindowOpening')
        intrusionAlert = daemon.getAlertByName('Intrusion')

        # Initialize state to a known one.
        self.alarmModeObject.value = 1
        for sensor in (entranceSensor, livingRoomWindowSensor,
                       kitchenWindowSensor):
            sensor.watchedObject.value = False

        self.waitDuring(1.5, 'Initializing')
        modeChangeTime = time.time()
        self.emailInfo = None  # In case mode initialization has raised an email.
        self.changeAlarmMode('Away', '*****@*****.**')

        self.waitUntil(
            modeChangeTime + entranceSensor.getActivationDelay() + 0.5,
            'Waiting for door to be enabled.')
        for sensor in (entranceSensor, livingRoomWindowSensor,
                       kitchenWindowSensor):
            self.assertTrue(sensor.isEnabled,
                            '{0} should now be enabled.'.format(sensor))
        self.assertEqual(entranceSensor.getPrealertDuration(), 6)

        # Step inside home.
        firstTriggerTime = time.time()
        entranceSensor.watchedObject.value = True

        sensorsInPrealert = [entranceSensor]
        sensorsInAlert = []
        sensorsInPersistentAlert = []
        checkAlertStatus = lambda: self.assertAlert(
            sensorsInPrealert, sensorsInAlert, sensorsInPersistentAlert)
        intermediaryDelay = entranceSensor.getPrealertDuration() / 4
        if togglesSensorBeforeEndOfPrealert:
            # Release sensor as quickly as possible.
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            entranceSensor.watchedObject.value = False

            # Toggle sensor again.
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            entranceSensor.watchedObject.value = True

            # Release again and leave it in that state until end of prealert (to
            # make sure alert state is not taken from the current sensor
            # status).
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            entranceSensor.watchedObject.value = False

        if cancelsAlarm:
            self.waitDuring(intermediaryDelay, [checkAlertStatus])
            self.changeAlarmMode('Presence',
                                 '*****@*****.**')  # Takes some time.
            del (sensorsInPrealert[:])
            for sensor in (entranceSensor, livingRoomWindowSensor,
                           kitchenWindowSensor):
                self.assertFalse(
                    sensor.isEnabled,
                    '{0} should not be enabled anymore.'.format(sensor))

            # Make sure alert is not raised after prealert delay of entrance
            # door.
            self.waitUntil(
                firstTriggerTime + entranceSensor.getPrealertDuration() + 1.5,
                'Wait a few seconds to make sure no alert is being raised.',
                [checkAlertStatus])
        else:
            if shuntsprealertWithFasterSensor:
                # Prealert duration is driven by living room window, as it is a faster sensor
                # than entrance door.
                self.waitDuring(intermediaryDelay, [checkAlertStatus])
                sensorsInPrealert.append(livingRoomWindowSensor)
                livingRoomWindowSensor.watchedObject.value = True
                # Check that living room window will raise alert faster than
                # entrance door (the opposite would denote a configuration
                # error).
                remainingTimeBeforeDoorAlert = entranceSensor.getPrealertDuration(
                ) - (time.time() - firstTriggerTime)
                logger.reportDebug(
                    'Remaining time before door alert: {0}s, before living room alert: {1} (expected to be shorter in living room!)'
                    .format(remainingTimeBeforeDoorAlert,
                            livingRoomWindowSensor.getPrealertDuration()))
                self.assertLess(
                    livingRoomWindowSensor.getPrealertDuration(),
                    remainingTimeBeforeDoorAlert,
                    'Living room window will not raise alert before entrance door, this test is not properly set up.'
                )
                self.waitDuring(
                    livingRoomWindowSensor.getPrealertDuration() + 1,
                    'Let living room window prealert pass.',
                    [checkAlertStatus], 0.2, 1.2)
            else:
                # Normal prealert with entrance door.
                self.waitUntil(
                    firstTriggerTime + entranceSensor.getPrealertDuration() +
                    1, 'Let prealert delay pass.', [checkAlertStatus], 0.5,
                    1.2)

            # Whichever strategy should now lead to entrance door being in alert
            # (either because of its own prealert or because an intrusion alert has
            # been raised by kitchen blinds meanwhile).
            sensorsInAlert.extend(sensorsInPrealert)
            sensorsInPersistentAlert.extend(sensorsInAlert)
            del (sensorsInPrealert[:])

            # Wait for first sensor to quit alert. At this point, entranceSensor
            # should already have been in alert for 1 second.
            self.assertTrue(
                entranceSensor.getAlertDuration() <
                livingRoomWindowSensor.getAlertDuration(),
                'This test assumes that door\'s alert is shorter than kitchen\'s one.'
            )
            self.assertEmail('Sensor joined', ['*****@*****.**'],
                             'Alert Intrusion: sensor joined', [])
            self.waitDuring(entranceSensor.getAlertDuration() - 0.5,
                            'Wait for first sensor to quit alert.',
                            [checkAlertStatus], 0, 1)
            sensorsInAlert.remove(entranceSensor)
            self.assertFalse(entranceSensor.isAlertActive)

            # Wait for the end of second sensor's alert.
            self.waitUntil(
                firstTriggerTime + intermediaryDelay +
                livingRoomWindowSensor.getPrealertDuration() +
                livingRoomWindowSensor.getAlertDuration() + 1,
                'Waiting for second sensor to quit alert.', [checkAlertStatus],
                0, 1)
            usesLivingWindow = livingRoomWindowSensor in sensorsInAlert
            if usesLivingWindow: sensorsInAlert.remove(livingRoomWindowSensor)

            # Check that test is properly set up at this point.
            self.assertFalse(sensorsInAlert)
            self.assertFalse(sensorsInPrealert)
            self.assertEqual(
                set(sensorsInPersistentAlert),
                set([livingRoomWindowSensor, entranceSensor]
                    if usesLivingWindow else [entranceSensor]))

            # Wait a few seconds more, to check everything stays ok.
            self.waitDuring(5, 'Checking that no event occurs...',
                            [checkAlertStatus])

            if testsInhibition:
                # Relaunch alert => shunt prealert and go to alert immediately.
                sensorsInAlert.append(entranceSensor)
                sensorsInPersistentAlert.append(entranceSensor)
                entranceSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing entrance door.')
                entranceSensor.watchedObject.value = True
                self.waitDuring(0.5, 'Waiting for alert to be reraised...')
                self.assertEmail('Sensor joined', ['*****@*****.**'],
                                 'Alert Intrusion: sensor joined', [])
                checkAlertStatus()

                # Stop current alert without inhibiting for now.
                intrusionAlert.persistenceObject.value = False
                sensorsInAlert.remove(entranceSensor)
                sensorsInPersistentAlert.clear()
                self.waitDuring(
                    0.5, 'Stopping current alert without inhibiting...')
                checkAlertStatus()

                # Relaunch alert again.
                entranceSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing entrance door.')
                entranceSensor.watchedObject.value = True
                self.waitDuring(0.2, 'Reopening entrance door.')
                entranceSensor.watchedObject.value = False
                sensorsInPrealert.append(entranceSensor)
                self.waitDuring(5.9, 'Waiting for alert to be reraised...',
                                [checkAlertStatus], 0.5, 0.2)
                self.assertEmail('Sensor joined', ['*****@*****.**'],
                                 'Alert Intrusion: sensor joined', [])
                sensorsInPrealert.remove(entranceSensor)
                sensorsInAlert.append(entranceSensor)
                sensorsInPersistentAlert.append(entranceSensor)
                checkAlertStatus()

                # Now inhibit intrusion alert.
                intrusionAlert.inhibitionObject.value = True
                intrusionAlert.persistenceObject.value = False
                del (sensorsInAlert[:])
                del (sensorsInPersistentAlert[:])
                self.waitDuring(0.6, 'Inhibiting intrusion alert...')
                checkAlertStatus()

                # Trigger another sensor to make sure alert is really inhibited.
                kitchenWindowSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing kitchen window.')
                kitchenWindowSensor.watchedObject.value = True
                self.waitDuring(
                    kitchenWindowSensor.getPrealertDuration() + 1.5,
                    'Checking that opening sesnors do not fire alert anymore (alert is inhibited)...',
                    [checkAlertStatus])

                # Remove inhibition: nothing should occur until a sensor gets
                # triggered again (currently triggered sensors are still ignored).
                intrusionAlert.inhibitionObject.value = False
                self.waitDuring(4.5, 'Checking no event occurs...',
                                [checkAlertStatus], 1.5)

                # Close window and open it again: alert!
                kitchenWindowSensor.watchedObject.value = False
                self.waitDuring(0.2, 'Closing kitchen window.',
                                [checkAlertStatus])
                triggerTime = time.time()
                kitchenWindowSensor.watchedObject.value = True
                sensorsInPrealert.append(kitchenWindowSensor)
                self.waitDuring(
                    kitchenWindowSensor.getPrealertDuration() + 0.2,
                    'Opening door again...', [checkAlertStatus], 0.2, 0.4)
                sensorsInPrealert.remove(kitchenWindowSensor)
                sensorsInAlert.append(kitchenWindowSensor)
                sensorsInPersistentAlert.append(kitchenWindowSensor)
                self.assertEmail('Sensor joined', ['*****@*****.**'],
                                 'Alert Intrusion: sensor joined', [])
                self.waitUntil(
                    triggerTime + kitchenWindowSensor.getPrealertDuration() +
                    kitchenWindowSensor.getAlertDuration(),
                    'Checking door alert...', [checkAlertStatus], 0.2, 0.2)
                sensorsInAlert.remove(kitchenWindowSensor)
                self.waitUntil(2, 'Alert should now be paused...',
                               [checkAlertStatus], 0.2, 0)
示例#39
0
import os

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('homewatcherConfig', help='use HWCONF as homewatcher configuration.', metavar='HWCONF')
    parser.add_argument('-d', '--daemonize', help='ask daemon to detach and run as a background daemon.', action='store_true', default=False)
    parser.add_argument('--pid-file', dest='pidFile', help='write the PID of the daemon process to PIDFILE.', metavar='PIDFILE')
    parser.add_argument('--log-file', dest='logFile', help='output daemon\'s activity to LOGFILE rather than to standard output.', metavar='LOGFILE', default=None)
    parser.add_argument('-v', '--verbosity', dest='verbosityLevel', help='set verbosity level.', metavar='LEVEL', choices=[l.lower() for l in logger.getLevelsToString()], default='info')

    args = parser.parse_args()

    # Configure logger.
    logger.initLogger(None, args.verbosityLevel.upper())

    # The homewatcher daemon is represented by an instance of
    # a pyknx.communicator.Communicator that runs with an "user script" dedicated to
    # interfacing linknx with homewatcher's capabilities.
    # First: read homewatcher config to read the linknx server url.
    # Second: start pyknxcommunicator with homewatcher's user script.
    logger.reportInfo('Reading config file {file}'.format(file=args.homewatcherConfig))
    config = configuration.Configuration.parseFile(args.homewatcherConfig)
    userScript = os.path.join(os.path.dirname(configuration.__file__), 'linknxuserfile.py')
    logger.reportDebug('Pyknx\'s user script for homewatcher is {script}'.format(script=userScript))
    userScriptArgs = {'hwconfig' : config}
    services = config.servicesRepository
    communicatorAddress=(services.daemon.host, services.daemon.port)
    logger.reportInfo('Starting Homewatcher at {communicatorAddr}, linked to linknx at {linknxAddr}'.format(communicatorAddr=communicatorAddress, linknxAddr=services.linknx.address))
    linknx = linknx.Linknx(services.linknx.host, services.linknx.port)
    communicator.Communicator.run(linknxAddress=linknx.address, userFile=userScript, communicatorAddress=communicatorAddress, userScriptArgs=userScriptArgs, verbosityLevel=args.verbosityLevel, logFile=args.logFile, daemonizes=args.daemonize, pidFile=args.pidFile)
示例#40
0
 def onModeValueChanged(self, value):
     logger.reportDebug('onModeValueChanged value={0}'.format(value))
     self._updateModeFromLinknx()
示例#41
0
 def onModeValueChanged(self, value):
     logger.reportDebug('onModeValueChanged value={0}'.format(value))
     self._updateModeFromLinknx()
示例#42
0
    def updateStatus(self):
        """
        Updates the status of this alert and raises the required events accordingly.

        """
        # Do not update if the daemon is in a process that may trigger
        # irrelevant intermediary states.
        if self.daemon.areAlertStatusUpdatesSuspended: return

        if not self.isStatusDirty:
            logger.reportDebug(
                'Status of {0} is already up-to-date, nothing to change.'.
                format(self))
            return
        logger.reportDebug('Updating status of {0}'.format(self))

        # Compute current status.
        if self._sensorsInAlert:
            newStatus = Alert.Status.ACTIVE
        elif self._sensorsInPrealert:
            newStatus = Alert.Status.INITIALIZING
        elif self.status == Alert.Status.ACTIVE and (
                self.persistenceObject != None
                and self.persistenceObject.value):
            # PAUSED status may only occur if persistence is supported.
            # Otherwise, as soon as last sensor leaves the alert, alert is
            # stopped and will start if a sensor gets triggered afterwards. This
            # is not the most convenient behaviour but with it, the user is free not to
            # define persistence.
            newStatus = Alert.Status.PAUSED
        else:
            newStatus = Alert.Status.STOPPED

        logger.reportDebug('New status for {0} is {1}'.format(self, newStatus))

        # When the alert is active, all sensors should leave the "prealert"
        # state to join the alert.
        if newStatus in (
                Alert.Status.ACTIVE, Alert.Status.PAUSED
        ):  # PAUSED is to be on the safe side as alert should always go through the ACTIVE state before going to PAUSED.
            for sensor in self._sensorsInPrealert:
                if not sensor in self._sensorsInAlert:
                    self._sensorsInAlert.add(
                        sensor
                    )  # None at this point. Timers will be created later in this method.   # sensor.makeAlertTimer(onTimeoutReached=None, onTerminated=lambda: self.removeSensorFromAlert(sensor))
            self._sensorsInPrealert = set()

        # Diff registered sensors.
        joiningSensors = self._sensorsInAlert - self._sensorsInAlertOnLastUpdateStatus
        leavingSensors = self._sensorsInAlertOnLastUpdateStatus - self._sensorsInAlert
        logger.reportDebug(
            'Updating status for {0}: joiningSensors={1}, leavingSensors={2}'.
            format(self, joiningSensors, leavingSensors))

        if newStatus == Alert.Status.ACTIVE:
            if self.persistenceObject != None:
                self.persistenceObject.value = True

        # Handle consequences of status change.
        if self.status == Alert.Status.STOPPED:
            if newStatus == Alert.Status.STOPPED:
                # No change.
                pass
            elif newStatus == Alert.Status.INITIALIZING:
                self.notifyAlertStarted()
            else:
                # Should not happen.
                logger.reportError(
                    'Unsupported switch from "{old}" to "{new}" for alert {alert}'
                    .format(alert=self, old=self.status, new=newStatus))
        elif self.status == Alert.Status.ACTIVE:
            if newStatus == Alert.Status.ACTIVE:
                # Check if a sensor joined or left.
                if joiningSensors:
                    self.notifySensorJoined()
                if leavingSensors:
                    self.notifySensorLeft()
            elif newStatus in (Alert.Status.PAUSED, Alert.Status.STOPPED):
                if not leavingSensors:
                    logger.reportError('A sensor should have left the alert.')
                else:
                    self.notifySensorLeft()

                self.notifyAlertDeactivated()

                if newStatus == Alert.Status.STOPPED:
                    self.notifyAlertReset()
                    self.notifyAlertStopped()
                elif newStatus == Alert.Status.PAUSED:
                    self.notifyAlertPaused()
                else:
                    raise Exception('Not implemented.')

            else:
                # Should not happen.
                logger.reportError(
                    'Unsupported switch from "{old}" to "{new}" for alert {alert}'
                    .format(alert=self, old=self.status, new=newStatus))
        elif self.status == Alert.Status.PAUSED:
            if newStatus == Alert.Status.PAUSED:
                # No change.
                pass
            elif newStatus == Alert.Status.STOPPED:
                self.notifyAlertReset()
                self.notifyAlertStopped()
            elif newStatus == Alert.Status.ACTIVE:
                self.notifyAlertResumed()
                if not joiningSensors:
                    logger.reportError(
                        'A sensor should have joined the alert.')
                else:
                    self.notifySensorJoined()
                self.notifyAlertActivated()
        elif self.status == Alert.Status.INITIALIZING:
            if newStatus == Alert.Status.INITIALIZING:
                # No change.
                pass
            elif newStatus == Alert.Status.ACTIVE:
                # Events to raise: started, sensor-joined, activated.
                if not joiningSensors:
                    logger.reportError(
                        'A sensor should have joined the alert.')
                else:
                    self.notifySensorJoined()
                self.notifyAlertActivated()
            elif newStatus == Alert.Status.STOPPED:
                self.notifyAlertAborted()
                self.notifyAlertStopped()

        # Stop obsolete timers for all sensors related to this alert.
        if newStatus in (Alert.Status.PAUSED, Alert.Status.STOPPED) or (
                self.status == Alert.Status.INITIALIZING
                and newStatus == Alert.Status.ACTIVE):
            for sensor in self.sensors:
                # Get the optional timer currently running for this sensor.
                timer = self._sensorTimers.get(sensor)
                if timer == None: continue

                timer.stop()
                timer = None
                del self._sensorTimers[sensor]

        for sensor in self._sensorsInAlert.union(self._sensorsInPrealert):
            # Start a new timer?
            if newStatus in (Alert.Status.INITIALIZING, Alert.Status.ACTIVE
                             ) and self._sensorTimers.get(sensor) == None:
                timer = sensor.makePrealertTimer(
                ) if newStatus == Alert.Status.INITIALIZING else sensor.makeAlertTimer(
                )
                self._sensorTimers[
                    sensor] = timer  # Prealert timer has been deleted above if applicable.
                timer.start()

        # Update persistence objects for all sensors.
        for s in self._sensorsInAlert:
            if s.persistenceObject != None:
                s.persistenceObject.value = True

        # Store current status.
        self._sensorsInAlertOnLastUpdateStatus = self._sensorsInAlert.copy()
        self.status = newStatus
        self.isStatusDirty = False