Example #1
0
 def cleanConfig(self):
     callbackAttributeName = self.callbackAttributeName
     for objectXmlConfig in self.config.getElementsByTagName('object'):
         if objectXmlConfig.hasAttribute(callbackAttributeName):
             logger.reportInfo('Removed callback {0} for {1}'.format(objectXmlConfig.getAttribute(callbackAttributeName), objectXmlConfig.getAttribute('id')))
             objectXmlConfig.removeAttribute(callbackAttributeName)
     pyknx.configurator.Configurator.cleanConfig(self)
Example #2
0
    def addSensorToAlert(self, sensor):
        if self.isInhibited:
            logger.reportInfo(
                '{0} will not join {1} since alert is currently inhibited (cf value of {2}).'
                .format(sensor, self, self.inhibitionObject))
            return

        with self._lock:
            logger.reportInfo('Sensor {0} joins {1}'.format(sensor, self))

            # Decide whether sensor should go through an initial prealert state.
            if self.status in (Alert.Status.STOPPED,
                               Alert.Status.INITIALIZING):
                self._sensorsInPrealert.add(sensor)
                self.invalidateStatus()
            elif self.status in (Alert.Status.PAUSED, Alert.Status.ACTIVE):
                if not sensor in self._sensorsInAlert:
                    # Sensor joins the alert.
                    self._sensorsInAlert.add(sensor)
                    self.invalidateStatus()
                else:
                    # Sensor is retriggered during its alert. Extend alert
                    # duration.
                    self._sensorTimers[sensor].extend()
            self.updateStatus()
Example #3
0
    def _updateModeFromLinknx(self):
        """ Update integral mode to reflect the current mode in linknx.
        """
        with self.suspendAlertStatusUpdates():
            modeValue = self.modeValue
            newMode = self.getMode(modeValue)

            if self._isTerminated:
                self.disableAllSensors()
                return

            # Notify mode change.
            hasModeChanged = self._currentMode == None or self._currentMode != newMode
            if not hasModeChanged:
                self._currentMode = newMode
                return

            # Mode left event.
            if self._currentMode != None:
                self._currentMode.notifyLeft()
            self._currentMode = newMode
            logger.reportInfo('Current alarm mode is now {0}'.format(self._currentMode))

            # Update sensors enabled state.
            for sensor in self.sensors:
                if sensor.isRequiredByCurrentMode():
                    if not sensor.isEnabled:
                        sensor.startActivationTimer()
                else:
                    sensor.stopActivationTimer() # Issue 23: to help prevent data race with the activation timer.
                    sensor.isEnabled = False
            
            # Mode entered event.
            if self._currentMode != None:
                self._currentMode.notifyEntered()
Example #4
0
 def setUp(self, linknxConfFile='linknx_test_conf.xml', usesCommunicator=True,  hwConfigFile=os.path.join(os.path.dirname(__file__), 'homewatcher_test_conf.xml')):
     usesLinknx = linknxConfFile != None
     communicatorAddress = ('localhost', 1031) if usesCommunicator else None
     userScript = os.path.join(os.path.dirname(configuration.__file__), 'linknxuserfile.py')
     userScriptArgs = {'hwconfig':hwConfigFile}
     try:
         if usesCommunicator:
             linknxPatchedFile = tempfile.mkstemp(suffix='.xml', text=True)[1]
             hwConfigurator = configurator.Configurator(hwConfigFile, linknxConfFile, linknxPatchedFile)
             hwConfigurator.generateConfig()
             hwConfigurator.writeConfig()
         else:
             linknxPatchedFile = None
         base.WithLinknxTestCase.setUp(self, linknxConfFile=linknxPatchedFile, communicatorAddr=communicatorAddress, patchLinknxConfig=False, userScript=userScript, userScriptArgs=userScriptArgs)
     finally:
         if linknxPatchedFile is not None: os.remove(linknxPatchedFile)
     self.homewatcherScriptsDirectory = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
     self.homewatcherModulesDirectory = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
     try:
         # Redirect the emailing capability of the daemon.
         if self.alarmDaemon:
             logger.reportInfo('Redirecting email capability of the alarm daemon to the mock for testing.')
             self.alarmDaemon.sendEmail = self.sendEmailMock
         else:
             logger.reportInfo('No alarm daemon. Email redirection is not set.')
         self.emailInfo = None
     except:
         logger.reportException('Error in setUp.')
         self.tearDown()
         self.fail('Test setup failed.')
         raise
Example #5
0
    def addCallbackForObject(self, objectId, callbackName,
                             callbackDestination):
        if objectId == None or objectId == '':
            logger.reportWarning(
                '{0} is not defined, skipping callback.'.format(
                    callbackDestination))
            return

        # Search object in config.
        found = False
        callbackAttributeName = self.callbackAttributeName

        for objectXmlConfig in self.config.getElementsByTagName('object'):
            if objectXmlConfig.getAttribute('id') == objectId:
                if found:
                    raise Exception(
                        'Two objects with id {id} found.'.format(id=objectId))
                found = True
                objectXmlConfig.setAttribute(callbackAttributeName,
                                             callbackName)
                logger.reportInfo('Added callback {0} for {1}'.format(
                    callbackName, objectId))
        if not found:
            raise Exception(
                'Object {id} not found in linknx configuration'.format(
                    id=objectId))
Example #6
0
    def testModeChange(self):
        """
            Test that merely exercises the everyday changing of mode.

            Nothing special here, we just switch from one mode to another to check that sensors are activated/deactivated as expected.
        """
        daemon = self.alarmDaemon

        # Check mode objects are properly set up.
        for modeName in ('Presence', 'Away', 'Night'):
            mode = daemon.getMode(modeName)
            self.assertEqual(len(mode.eventManager.eventConfigs), 2)
            self.assertEqual(mode.eventManager.eventConfigs[0].type, 'entered')
            self.assertEqual(len(mode.eventManager.eventConfigs[0].actions), 2)
            self.assertEqual(mode.eventManager.eventConfigs[0].actions[0].type, 'send-email')
            self.assertEqual(mode.eventManager.eventConfigs[1].type, 'left')
            self.assertEqual(len(mode.eventManager.eventConfigs[1].actions), 1)

        # Prepare sensors involved in this test.
        entranceDoor = daemon.getSensorByName('EntranceDoorOpening')
        livingWindow = daemon.getSensorByName('LivingRoomWindowOpening')
        kitchenSmoke = daemon.getSensorByName('KitchenSmokeSensor')
        bedroomSmoke = daemon.getSensorByName('BedroomSmokeSensor')

        # Initialize state to a known one.
        self.alarmModeObject.value = 1
        livingWindow.watchedObject.value = True # Open window.

        self.waitDuring(1, 'Initializing')
        modeChangeTime = time.time()
        logger.reportInfo('\n\n********* SWITCH TO MODE: AWAY ********************')
        self.emailInfo = None # In case mode initialization has raised an email.
        self.changeAlarmMode('Away', '*****@*****.**')

        # Check smoke detectors are immediately active.
        self.waitDuring(1, 'Waiting for some sensors to be enabled.')
        for sensor in (kitchenSmoke, bedroomSmoke):
            self.assertTrue(sensor.isEnabled, '{0} should now be enabled.'.format(sensor))

        # Check entrance door is not immediately active. 
        self.waitUntil(modeChangeTime + 5.5, 'Consuming activation delay for {0}'.format(entranceDoor), [lambda: self.assertFalse(entranceDoor.isEnabled)], 0, 0.5)
        self.assertTrue(entranceDoor.isEnabled)

        # Check living window is not active since it is triggered.
        self.assertTrue(livingWindow.isTriggered, '{0} has been manually triggered at the beginning of the test, it should still be so.'.format(livingWindow))
        self.assertFalse(livingWindow.isEnabled, '{0} should not be enabled since it is triggered.'.format(livingWindow))

        # Close window and check that it becomes enabled in a short time.
        livingWindow.watchedObject.value = False
        self.waitDuring(1.2, 'Waiting for {0} to be enabled.'.format(livingWindow), [lambda: self.assertFalse(livingWindow.isEnabled)], 0, 0.2)
        self.assertTrue(livingWindow.isEnabled, '{0} should now be enabled.'.format(livingWindow))

        logger.reportInfo('\n\n************ SWITCH TO MODE: PRESENCE **********************')
        self.changeAlarmMode('Presence', '*****@*****.**')

        # Check all sensors are now inactive.
        def checkAllSensorsEnabledState():
            for s in daemon.getAlertByName('Intrusion').sensors:
                self.assertFalse(s.isEnabled, '{0} should now be disabled.'.format(s))
        self.waitDuring(4, 'Let little time go to test sensors\' state in the long run.', [checkAllSensorsEnabledState])
Example #7
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'))
Example #8
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))
Example #9
0
 def testValues(objectId, values):
     logger.reportInfo('Testing {0} with values {1}'.format(objectId, values))
     for value in values:
         obj = self.linknx.getObject(objectId)
         assignedValue = value if not isinstance(value, tuple) else value[0]
         readValue = value if not isinstance(value, tuple) else value[1]
         obj.value = assignedValue
         self.assertEqual(obj.value, readValue)
Example #10
0
 def writeConfig(self):
     if self._outputFile != None:
         outputXMLFile = codecs.open(self._outputFile, mode='w', encoding='utf-8')
         outputXMLFile.write(self.config.toxml())
         outputXMLFile.close()
         logger.reportInfo('Output config written to ' + self._outputFile)
     else:
         print(self.config.toxml())
Example #11
0
    def startActivationTimer(self):
        # Already enabled.
        if self.isEnabled: return

        if self.isActivationPending():
            logger.reportInfo('An activation timer for {0} is already running. Cancel it and start a new one.'.format(self))
            self._activationTimer.stop()
        self._activationTimer = timer.Timer(self, self.getActivationDelay(), 'Activation timer', onTimeoutReached=self._onActivationTimerTimeout, onIterate=self._onActivationTimerIterate)
        self._activationTimer.start()
Example #12
0
 def _getOrAddConfigElement(self, parent, elementTagName):
     elementNodes = parent.getElementsByTagName(elementTagName)
     if not elementNodes:
         elementNode = parent.ownerDocument.createElement(elementTagName)
         parent.appendChild(elementNode)
         logger.reportInfo('No <' + elementTagName + '> element in config, creating one.')
     else:
         elementNode = elementNodes[0]
     return elementNode
Example #13
0
 def cleanConfig(self):
     callbackAttributeName = self.callbackAttributeName
     for objectXmlConfig in self.config.getElementsByTagName('object'):
         if objectXmlConfig.hasAttribute(callbackAttributeName):
             logger.reportInfo('Removed callback {0} for {1}'.format(
                 objectXmlConfig.getAttribute(callbackAttributeName),
                 objectXmlConfig.getAttribute('id')))
             objectXmlConfig.removeAttribute(callbackAttributeName)
     pyknx.configurator.Configurator.cleanConfig(self)
Example #14
0
 def testDocumentationSamples(self):
     allAObjects = self.linknx.getObjects('A.*')
     logger.reportInfo(str(allAObjects))
     self.assertEqual(len(allAObjects), 3)
     for objectId, value in allAObjects.getValues().items():
         self.assertIn(objectId, [
             'Angle Unsigned Byte', 'Ascii String14',
             'Extended Ascii String14'
         ])
Example #15
0
 def _onActivationTimerIterate(self, timer):
     if self.activationCriterion != None and not self.activationCriterion.isValid():
         if not timer.isPaused:
             logger.reportInfo('Pausing activation timer for {0} because activation criterion is not satisfied.'.format(self))
         timer.pause()
     else:
         if timer.isPaused:
             # Restart activation delay.
             logger.reportInfo('Restarting activation timer for {0} because activation criterion is now satisfied.'.format(self))
             timer.reset()
Example #16
0
 def testValues(objectId, values):
     logger.reportInfo('Testing {0} with values {1}'.format(
         objectId, values))
     for value in values:
         obj = self.linknx.getObject(objectId)
         assignedValue = value if not isinstance(value,
                                                 tuple) else value[0]
         readValue = value if not isinstance(value, tuple) else value[1]
         obj.value = assignedValue
         self.assertEqual(obj.value, readValue)
Example #17
0
 def setUp(self):
     self.name = self.id()[len(self.__module__) + 1:]
     logFile = 'test_files/{0}.log'.format(self.name)
     if os.path.exists(logFile):
         os.remove(logFile)
     logger.initLogger((logFile, logging.DEBUG), logging.INFO)
     logger.reportInfo('*******Start {0}*************'.format(self.name))
     self.currentAssertions = []
     self.pyknxScriptsDirectory = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
     self.pyknxModulesDirectory = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
Example #18
0
 def setUp(self):
     self.name = self.id()[len(self.__module__) + 1:]
     logFile = 'test_files/{0}.log'.format(self.name)
     if os.path.exists(logFile):
         os.remove(logFile)
     logger.initLogger((logFile, logging.DEBUG), logging.INFO)
     logger.reportInfo('*******Start {0}*************'.format(self.name))
     self.currentAssertions = []
     self.pyknxScriptsDirectory = os.path.normpath(
         os.path.join(os.path.dirname(__file__), '..', '..'))
     self.pyknxModulesDirectory = os.path.normpath(
         os.path.join(os.path.dirname(__file__), '..'))
Example #19
0
 def setUp(self,
           linknxConfFile='linknx_test_conf.xml',
           usesCommunicator=True,
           hwConfigFile=os.path.join(os.path.dirname(__file__),
                                     'homewatcher_test_conf.xml')):
     usesLinknx = linknxConfFile != None
     communicatorAddress = ('localhost', 1031) if usesCommunicator else None
     userScript = os.path.join(os.path.dirname(configuration.__file__),
                               'linknxuserfile.py')
     userScriptArgs = {'hwconfig': hwConfigFile}
     try:
         if usesCommunicator:
             linknxPatchedFile = tempfile.mkstemp(suffix='.xml',
                                                  text=True)[1]
             hwConfigurator = configurator.Configurator(
                 hwConfigFile, linknxConfFile, linknxPatchedFile)
             hwConfigurator.generateConfig()
             hwConfigurator.writeConfig()
         else:
             linknxPatchedFile = None
         base.WithLinknxTestCase.setUp(self,
                                       linknxConfFile=linknxPatchedFile,
                                       communicatorAddr=communicatorAddress,
                                       patchLinknxConfig=False,
                                       userScript=userScript,
                                       userScriptArgs=userScriptArgs)
     finally:
         if linknxPatchedFile is not None: os.remove(linknxPatchedFile)
     self.homewatcherScriptsDirectory = os.path.normpath(
         os.path.join(os.path.dirname(__file__), '..', '..'))
     self.homewatcherModulesDirectory = os.path.normpath(
         os.path.join(os.path.dirname(__file__), '..'))
     try:
         # Redirect the emailing capability of the daemon.
         if self.alarmDaemon:
             logger.reportInfo(
                 'Redirecting email capability of the alarm daemon to the mock for testing.'
             )
             self.alarmDaemon.sendEmail = self.sendEmailMock
             mock = TestCaseBase.ExecuteActionMock(self.alarmDaemon.linknx,
                                                   self)
             self.alarmDaemon.linknx.executeAction = mock.executeAction
         else:
             logger.reportInfo(
                 'No alarm daemon. Email redirection is not set.')
         self.emailInfo = None
         self.shellCmdInfo = None
     except:
         logger.reportException('Error in setUp.')
         self.tearDown()
         self.fail('Test setup failed.')
         raise
Example #20
0
    def setUp(self, linknxConfFile='linknx_test_conf.xml', communicatorAddr=('localhost', 1031), patchLinknxConfig=True, userScript='linknxuserfile.py', userScriptArgs=None):
        TestCaseBase.setUp(self)
        self.linknxProcess = None
        self.linknx = None
        self.communicator = None
        self.linknxOutputFDs = None
        self.linknxXMLConfig = linknxConfFile
        self.communicatorAddress = communicatorAddr
        try:

            # Patch config.
            if self.linknxXMLConfig != None:
                testDir = 'test_files'
                if not os.path.exists(testDir):
                    os.mkdir(testDir)
                if self.communicatorAddress != None and patchLinknxConfig:
                    linknxPatchedConfigFile = os.path.join(testDir, 'autogenlinknx.conf.xml')
                    if os.path.exists(linknxPatchedConfigFile):
                        os.remove(linknxPatchedConfigFile)
                    self.configurator = configurator.Configurator(self.linknxXMLConfig, linknxPatchedConfigFile, self.communicatorAddress)
                    self.configurator.cleanConfig()
                    self.configurator.generateConfig()
                    self.configurator.writeConfig()
                else:
                    linknxPatchedConfigFile = self.linknxXMLConfig

                # Start linknx.
                linknxErrFilename = 'test_files/{0}.linknx.err'.format(self.name)
                linknxOutFilename = 'test_files/{0}.linknx.out'.format(self.name)
                efdw = open(linknxErrFilename, 'w')
                efdr = open(linknxErrFilename, 'r')
                ofdw = open(linknxOutFilename, 'w')
                self.linknxOutputFDs = (efdr, efdw, ofdw)
                self.linknxProcess = subprocess.Popen( ['linknx', '--config={0}'.format(linknxPatchedConfigFile)], stdout=self.linknxOutputFDs[2], stderr=self.linknxOutputFDs[1])
                logger.reportInfo('linknx started with pid {0}'.format(self.linknxProcess.pid))
                self.linknx = linknx.Linknx('localhost', 1030)
                self.currentAssertions.append(self.checkLinknx)

            # Start pyknx.
            if self.communicatorAddress != None:
                self.communicator = communicator.Communicator(self.linknx, userScript, self.communicatorAddress, userScriptArgs)
                self.communicator.startListening()
            else:
                self.communicator = None

            logger.reportInfo('Set up finished.')
            self.waitDuring(0.5, 'Pause to make sure everything gets ready.')
        except:
            logger.reportException('Error in setUp.')
            self.tearDown()
            self.fail('Test setup failed.')
            raise
Example #21
0
 def notifyWatchedObjectChanged(self):
     """ Notifies the sensor that its watched object's value has just changed. """
     newTriggeredState = self.getUpdatedTriggerState() # Depends on the concrete sensor class. Most of them will do nothing as trigger state IS the watched object state. But for FloatSensor for instance, trigger may take an hysteresis into account.
     if self._isTriggered == newTriggeredState: return
     self._isTriggered = newTriggeredState
     if self.isTriggered:
         logger.reportInfo('{0} is triggered.'.format(self.name))
         if self.isEnabled:
             self.alert.addSensorToAlert(self)
     else:
         # Nothing to do regarding alert here. If sensor was previously
         # triggered, alert will not end by simply releasing trigger.
         logger.reportInfo('{0}\'s trigger is released.'.format(self.name))
Example #22
0
def initializeUserScript(context):
    global alarmDaemon
    logger.reportInfo('hw config is {0}'.format(context.hwconfig))
    # motionConfigDir = context.customArgs.get('motionconfigdir')
    # motionOutputDir = context.customArgs.get('motionoutputdir')

    if isinstance(context.hwconfig, str):
        config = configuration.Configuration.parseFile(context.hwconfig)
    elif isinstance(context.hwconfig, configuration.Configuration):
        config = context.hwconfig
    else:
        raise Exception('The hwconfig argument must be either a string or a homewatcher.configuration.Configuration object. "{0}" was passed.'.format(type(context.hwconfig)))

    # Instanciate daemon.
    alarmDaemon = alarm.Daemon(context.communicator, config)
Example #23
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)
Example #24
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.')
Example #25
0
    def waitUntil(self,
                  endTime,
                  reason,
                  assertions=[],
                  assertStartMargin=0,
                  assertEndMargin=0):
        state = 0  # 0: undefined, 1: before assertions, 2: during assertions, 3: after assertions
        with AssertionsHandle(self, assertions):
            startTime = time.time()
            assertStart = startTime + assertStartMargin
            assertEnd = endTime - assertEndMargin
            while time.time() < endTime:
                currentTime = time.time()
                if currentTime < assertStart and assertStartMargin != 0:
                    newState = 1
                    status = 'before assertions'
                    duration = assertStart - currentTime
                elif currentTime > assertEnd and assertEndMargin != 0:
                    newState = 3
                    status = 'after assertions'
                    duration = endTime - currentTime
                else:
                    newState = 2
                    status = 'with assertions' if assertions else 'no assertions'
                    duration = assertEnd - currentTime

                # Notify progress.
                if newState != state:
                    state = newState
                    logger.reportInfo('{0} ({2}) (during {1} seconds)'.format(
                        reason, round(duration, 1), status))

                # Check all pending assertions.
                for assertion in self.currentAssertions:
                    try:
                        if state == 2:
                            assertion()
                    except:
                        logger.reportInfo(
                            'Exception caught in waitUntil after {0}s'.format(
                                time.time() - startTime))
                        raise

                # Sleep until optimal end of iteration.
                time.sleep(0.1)
Example #26
0
    def tearDown(self):
        logger.reportInfo('Tearing down...')

        if self.communicator:
            logger.reportInfo('Stopping communicator...')
            self.communicator.stopListening()
            logger.reportInfo('communicator is stopped.')
        if self.linknxProcess:
            self.linknxProcess.kill()
            logger.reportInfo('linknx is stopped.')
        if self.linknxOutputFDs:
            self.linknxOutputFDs[0].close()
            self.linknxOutputFDs[1].close()
            self.linknxOutputFDs[2].close()

        TestCaseBase.tearDown(self)

        logger.reportInfo('*******End of {0}*************\n\n\n'.format(self.name))
Example #27
0
    def addCallbackForObject(self, objectId, callbackName, callbackDestination):
        if objectId == None or objectId == '':
            logger.reportWarning('{0} is not defined, skipping callback.'.format(callbackDestination))
            return

        # Search object in config.
        found = False
        callbackAttributeName = self.callbackAttributeName

        for objectXmlConfig in self.config.getElementsByTagName('object'):
            if objectXmlConfig.getAttribute('id') == objectId:
                if found:
                    raise Exception('Two objects with id {id} found.'.format(id=objectId))
                found = True
                objectXmlConfig.setAttribute(callbackAttributeName, callbackName)
                logger.reportInfo('Added callback {0} for {1}'.format(callbackName, objectId))
        if not found:
            raise Exception('Object {id} not found in linknx configuration'.format(id=objectId))
Example #28
0
    def testTimer(self):
        class TimerStatus:
            def __init__(self):
                self.isTimeoutReached = False
                self.isTerminated = False

            def onTimeout(self, timer):
                self.isTimeoutReached = True

            def onTerminated(self, timer):
                logger.reportInfo('onTerminated')
                self.isTerminated = True

        status = TimerStatus()
        timer = Timer(None, 2, 'Test timer', onTimeoutReached=status.onTimeout, onTerminated=status.onTerminated)
        timer.start()
        self.waitDuring(2.1, 'Waiting for test timer to complete', assertions=[lambda: self.assertFalse(status.isTimeoutReached or status.isTerminated)], assertEndMargin=0.2)
        logger.reportInfo('isTimeoutReached={isTimeoutReached}, isTerminated={isTerminated}'.format(isTimeoutReached=status.isTimeoutReached, isTerminated=status.isTerminated))
        self.assertTrue(status.isTimeoutReached and status.isTerminated)
Example #29
0
    def tearDown(self):
        logger.reportInfo('Tearing down...')

        if self.communicator:
            logger.reportInfo('Stopping communicator...')
            self.communicator.stopListening()
            logger.reportInfo('communicator is stopped.')
        if self.linknxProcess:
            self.linknxProcess.kill()
            logger.reportInfo('linknx is stopped.')
        if self.linknxOutputFDs:
            self.linknxOutputFDs[0].close()
            self.linknxOutputFDs[1].close()
            self.linknxOutputFDs[2].close()

        TestCaseBase.tearDown(self)

        logger.reportInfo('*******End of {0}*************\n\n\n'.format(
            self.name))
Example #30
0
    def stopListening(self):
        """ Stop communicator. No new incoming connection will be possible. """
        # Notify user script first. This allows linknx to notify a few object
        # changes before communicator really stop listening.
        if self._userFile and self.isUserScriptInitialized:
            self._executeUserCallback('finalizeUserScript', CallbackContext(self), True)
            logger.reportInfo('User script finalized.')

        if not self.isListening: return
        self._listenerThread.stop()

        # Wait for listener thread to end (to be sure that no callback
        # request originating from linknx can reach the user script anymore).
        while not self._listenerThread.isStopped:
            time.sleep(0.5)
        self._listenerThread = None

        if self._userFile:
            self._executeUserCallback('endUserScript', CallbackContext(self), True)
            logger.reportInfo('User script ended.')
Example #31
0
    def waitForRemoteConnectionReady(self):
        """
        Wait for Linknx's XML server to accept incoming connections.

        This method should be called if unsure about when the Linknx has been started. It may take some time to get ready.
        This method attempts to connect to Linknx during 10 seconds and raises an Exception if Linknx still is unreachable after this delay.

        """
        # Ask for config.
        logger.reportInfo("Start connecting to linknx on {0}.".format(self.address))
        attemptId = 0
        maxAttemptCount = 10
        while attemptId < maxAttemptCount:
            attemptId += 1
            try:
                conf = self.config

                # Linknx is ready if we reach this point.
                logger.reportInfo("Linknx is up and ready, let's start.")
                return
            except ConnectionRefusedError:
                logger.reportInfo("Linknx is not yet ready...  (attempt {0}/{1})".format(attemptId, maxAttemptCount))

            except Exception as e:
                logger.reportException()

            time.sleep(1)

        raise Exception("Linknx is not reachable.")
Example #32
0
    def waitForRemoteConnectionReady(self):
        """
        Wait for Linknx's XML server to accept incoming connections.

        This method should be called if unsure about when the Linknx has been started. It may take some time to get ready.
        This method attempts to connect to Linknx during 10 seconds and raises an Exception if Linknx still is unreachable after this delay.

        """
        # Ask for config.
        logger.reportInfo('Start connecting to linknx on {0}.'.format(
            self.address))
        attemptId = 0
        maxAttemptCount = 10
        while attemptId < maxAttemptCount:
            attemptId += 1
            try:
                conf = self.config

                # Linknx is ready if we reach this point.
                logger.reportInfo('Linknx is up and ready, let\'s start.')
                return
            except ConnectionRefusedError:
                logger.reportInfo(
                    'Linknx is not yet ready...  (attempt {0}/{1})'.format(
                        attemptId, maxAttemptCount))

            except Exception as e:
                logger.reportException()

            time.sleep(1)

        raise Exception('Linknx is not reachable.')
Example #33
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
Example #34
0
    def startListening(self):

        """
        Start the communicator. It is then waiting for incoming information from Linknx.

        If provided, the initializeUserScript function of the user file is called. Its context contains the arguments that were optionally given to the __init__ method.

        """
        if self.isListening: return

        # Make sure linknx is ready.
        self.linknx.waitForRemoteConnectionReady()

        # Start listening early to avoid communication errors from linknx. Those
        # errors are never harmful but the user may be surprized and worried
        # about them! 
        self._listenerThread = Communicator.Listener(self._address, self)
        self._listenerThread.start()
        timeout = time.time() + 4
        while not self._listenerThread.isReady and time.time() < timeout:
            time.sleep(0.3)
        if not self._listenerThread.isReady:
            raise Exception('Could not initialize listening socket.')

        # Initialize user-provided script. The purpose of this callback is to
        # let the user initialize its script by reading state from linknx (and
        # possibly anywhere else). Thus, linknx should not raise events yet,
        # since user script would likely be partially initialized. The
        # isUserScriptInitialized flag is used for that purpose.
        if self._loadUserFile():
            logger.reportInfo('Initializing user script...')
            try:
                self._executeUserCallback('initializeUserScript', CallbackContext(self, args=self._userScriptArgs), True)
            except Exception as e:
                logger.reportException('User script initialization failed, communicator will stop immediately.')
                self.stopListening()
                return
            logger.reportInfo('User script initialized.')
        self.isUserScriptInitialized = True
Example #35
0
    def addSensorToAlert(self, sensor):
        if self.isInhibited:
            logger.reportInfo('{0} will not join {1} since alert is currently inhibited (cf value of {2}).'.format(sensor, self, self.inhibitionObject))
            return

        with self._lock:
            logger.reportInfo('Sensor {0} joins {1}'.format(sensor, self))

            # Decide whether sensor should go through an initial prealert state.
            if self.status in (Alert.Status.STOPPED, Alert.Status.INITIALIZING):
                self._sensorsInPrealert.add(sensor)
                self.invalidateStatus()
            elif self.status in (Alert.Status.PAUSED, Alert.Status.ACTIVE):
                if not sensor in self._sensorsInAlert:
                    # Sensor joins the alert.
                    self._sensorsInAlert.add(sensor)
                    self.invalidateStatus()
                else:
                    # Sensor is retriggered during its alert. Extend alert
                    # duration.
                    self._sensorTimers[sensor].extend()
            self.updateStatus()
Example #36
0
    def waitUntil(self, endTime, reason, assertions=[], assertStartMargin=0, assertEndMargin=0):
        state = 0 # 0: undefined, 1: before assertions, 2: during assertions, 3: after assertions
        with AssertionsHandle(self, assertions):
            startTime = time.time()
            assertStart = startTime + assertStartMargin
            assertEnd = endTime - assertEndMargin
            while time.time() < endTime:
                currentTime = time.time()
                if currentTime < assertStart and assertStartMargin != 0:
                    newState = 1
                    status = 'before assertions'
                    duration = assertStart - currentTime
                elif currentTime > assertEnd and assertEndMargin != 0:
                    newState = 3
                    status = 'after assertions'
                    duration = endTime - currentTime
                else:
                    newState = 2
                    status = 'with assertions' if assertions else 'no assertions'
                    duration = assertEnd - currentTime

                # Notify progress.
                if newState != state:
                    state = newState
                    logger.reportInfo('{0} ({2}) (during {1} seconds)'.format(reason, round(duration, 1), status))

                # Check all pending assertions.
                for assertion in self.currentAssertions:
                    try:
                        if state == 2:
                            assertion()
                    except:
                        logger.reportInfo('Exception caught in waitUntil after {0}s'.format(time.time() - startTime))
                        raise

                # Sleep until optimal end of iteration.
                time.sleep(0.1)
Example #37
0
    def _updateModeFromLinknx(self):
        """ Update integral mode to reflect the current mode in linknx.
        """
        with self.suspendAlertStatusUpdates():
            modeValue = self.modeValue
            newMode = self.getMode(modeValue)

            if self._isTerminated:
                self.disableAllSensors()
                return

            # Notify mode change.
            hasModeChanged = self._currentMode == None or self._currentMode != newMode
            if not hasModeChanged:
                self._currentMode = newMode
                return

            # Mode left event.
            if self._currentMode != None:
                self._currentMode.notifyLeft()
            self._currentMode = newMode
            logger.reportInfo('Current alarm mode is now {0}'.format(
                self._currentMode))

            # Update sensors enabled state.
            for sensor in self.sensors:
                if sensor.isRequiredByCurrentMode():
                    if not sensor.isEnabled:
                        sensor.startActivationTimer()
                else:
                    sensor.stopActivationTimer(
                    )  # Issue 23: to help prevent data race with the activation timer.
                    sensor.isEnabled = False

            # Mode entered event.
            if self._currentMode != None:
                self._currentMode.notifyEntered()
Example #38
0
def loadPlugins():
    global _plugins
    pluginDirectory = os.path.dirname(__file__)

    pluginModules = glob.glob(os.path.join(pluginDirectory, '*.py'))

    # Scan all modules in the 'plugins' subdirectory and instanciate all classes
    # that inherit Plugin.
    for moduleFile in pluginModules:
        if os.path.basename(moduleFile) == '__init__.py': continue
        module = importlib.import_module('homewatcher.plugins.{0}'.format(
            os.path.splitext(os.path.basename(moduleFile))[0]))
        for symbolName in dir(module):
            symbol = vars(module)[symbolName]
            if isinstance(symbol, type) and issubclass(
                    symbol, homewatcher.plugin.Plugin):
                logger.reportInfo('Loading {0}'.format(symbol))
                plugin = symbol()
                try:
                    plugin.load()
                    logger.reportInfo('{0} loaded.'.format(symbol))
                except Exception as e:
                    logger.reportException(
                        'Failed to load plugin {0}'.format(plugin))
Example #39
0
    def run(self):
        statusFormat = 'Logger thread for std{0} of {1} PID=<{2}> is {3}.'.format('out' if self.readsStdout else 'err', self.name, self.process.pid, '{0}')
        logger.reportInfo(statusFormat.format('started'))
        while self.process.returncode is None:
            stream = self.process.stdout if self.readsStdout else self.process.stderr
            line = stream.readline()
            if line == '':
                time.sleep(1)
                continue

            line = line.rstrip('\n')
            log = '[{0}.{3} pid={1}] {2}'.format(self.name, self.process.pid, line, 'out' if self.readsStdout else 'err')
            logger.reportInfo(log)
        logger.reportInfo(statusFormat.format('terminated'))
Example #40
0
    def cleanConfig(self):
        # Delete all pyknx rules before creating only those that apply to the
        # current config.
        rulesNode = self._getOrAddConfigElement(self.config, 'rules')
        prefixLength = len(self._communicatorName)
        configuredAtLeastOne = False
        for ruleNode in rulesNode.getElementsByTagName('rule'):
            ruleId = ruleNode.getAttribute('id')
            if ruleId[:prefixLength] == self._communicatorName:
                configuredAtLeastOne = True
                logger.reportInfo('Clean rule ' + ruleId + ' coming from a previous configure.')
                rulesNode.removeChild(ruleNode)

        if not configuredAtLeastOne:
            logger.reportInfo('Input XML config does not define any pyknx rule. Nothing to clean.')

        servicesNode = self._getOrAddConfigElement(self.config, 'services')
        ioportsNode = self._getOrAddConfigElement(servicesNode, 'ioports')
        for ioportNode in ioportsNode.getElementsByTagName('ioport'):
            if ioportNode.getAttribute('id') == self._communicatorName:
                logger.reportInfo('Clean ' + ioportNode.toxml())
                ioportsNode.removeChild(ioportNode)
Example #41
0
    def testPostponedActivation(self):
        """ Test that exercises the postponing of the activation of a sensor whenever its canEnabled property returns False. """

        logger.reportInfo('\n\n*********INITIALIZE testPostponedActivation********************')

        daemon = self.alarmDaemon

        # Prepare useful sensors.
        garageDoor = daemon.getSensorByName('GarageDoorOpening')
        entranceDoor = daemon.getSensorByName('EntranceDoorOpening')

        # Initialize state to a known one.
        self.alarmModeObject.value = 1 # Presence.
        entranceDoor.watchedObject.value = True # Open entrance.

        self.waitDuring(1, 'Initialization.')

        # Go to away mode.
        self.emailInfo = None # In case mode initialization has raised an email.
        self.changeAlarmMode('Away', '*****@*****.**')

        # Neither door nor garage should be active for now.
        def assertNoAlert():
            self.assertAlert(sensorsInPrealert=[], sensorsInAlert=[], sensorsInPersistentAlert=[])
        def assertNotEnabled():
            self.assertFalse(entranceDoor.isEnabled, '{0} should not be enabled for now since it is open.'.format(entranceDoor))
            self.assertFalse(garageDoor.isEnabled, '{0} should not be enabled for now since it depends on the entrance door which is open.'.format(garageDoor))
        self.waitDuring(4, 'Wait for a while to make sure neither door nor garage are enabled.', assertions=[assertNoAlert, assertNotEnabled])

        # Close door.
        entranceDoor.watchedObject.value = False

        # No immediate activation is expected!
        self.waitDuring(4, 'Wait for a while to make sure doors do not get enabled for now.', assertions=[assertNoAlert, assertNotEnabled])
        assertEnabled = lambda sensor, isEnabled, format: self.assertEqual(sensor.isEnabled, isEnabled, format.format(sensor))
        assertPaused = lambda sensor, isPaused, format: self.assertEqual(sensor._activationTimer.isPaused, isPaused, format.format(sensor))
        disabledFormat = '{0} should not be enabled for now since its activation delay is not over.'
        enabledFormat = '{0} should be enabled since its activation delay is now over.'
        pausedFormat = 'Activation timer for {0} should be paused.'
        runningFormat = 'Activation timer for {0} should be running.'
        assertEnabled(entranceDoor, False, disabledFormat)
        assertEnabled(garageDoor, False, disabledFormat)
        assertPaused(entranceDoor, False, runningFormat)
        assertPaused(garageDoor, False, runningFormat)

        # Reopening door should cancel activation.
        entranceDoor.watchedObject.value = True
        self.waitDuring(2, 'Let activation timer go to pause.')
        for i in range(2):
            assertEnabled(entranceDoor, False, disabledFormat)
            assertEnabled(garageDoor, False, disabledFormat)
            assertPaused(entranceDoor, True, pausedFormat)
            assertPaused(garageDoor, True, pausedFormat)
            self.waitDuring(2, 'Check that state is stable.')

        # Close again and wait for activation.
        doorClosingTime = time.time()
        entranceDoor.watchedObject.value = False
        self.waitDuring(1, 'Let linknx handle door close event.')
        assertEnabled(entranceDoor, False, disabledFormat)
        assertEnabled(garageDoor, False, disabledFormat)
        assertPaused(entranceDoor, False, runningFormat)
        assertPaused(garageDoor, False, runningFormat)

        # Entrance door gets enabled first.
        assertions=[lambda: assertEnabled(entranceDoor, False, disabledFormat), lambda:    assertEnabled(garageDoor, False, disabledFormat), lambda: assertPaused(entranceDoor, False, runningFormat), lambda: assertPaused(garageDoor, False, runningFormat)]
        self.waitUntil(doorClosingTime + 5 + 0.5, 'Wait for doors to be enabled.', assertions=assertions, assertStartMargin=0, assertEndMargin=1)
        assertEnabled(entranceDoor, True, enabledFormat)
        assertEnabled(garageDoor, True, enabledFormat)
        self.assertFalse(entranceDoor.isActivationPending(), 'Activation timer should now be released.')
        self.assertFalse(garageDoor.isActivationPending(), 'Activation timer should now be released.')
Example #42
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)
Example #43
0
    def testAlertLifeCycle(self):
        logger.reportInfo('\n\n*********INITIALIZE testAlertLifeCycle ********************')

        # Prepare useful sensors.
        kitchenWindow = self.alarmDaemon.getSensorByName('KitchenWindowOpening')
        livingWindow = self.alarmDaemon.getSensorByName('LivingRoomWindowOpening')

        intrusionAlert = self.alarmDaemon.getAlertByName('Intrusion')

        # Initialize state to a known one.
        self.alarmModeObject.value = 1 # Presence.
        kitchenWindow.watchedObject.value = False
        livingWindow.watchedObject.value = False

        self.waitDuring(1, 'Initialization.')

        # Go to away mode.
        self.emailInfo = None # In case mode initialization has raised an email.
        self.changeAlarmMode('Away', '*****@*****.**')

        # Wait for window activation.
        self.waitDuring(2, 'Wait for activation of window sensors.')
        self.assertTrue(kitchenWindow.isEnabled)
        self.assertTrue(livingWindow.isEnabled)

        def assertAlertEvents(firedEvents, alertName, resetsToOff=True):
            eventObjectIds = {}
            eventObjectIds[configuration.AlertEvent.Type.PREALERT_STARTED] = '{0}AlertStarted'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.ALERT_ACTIVATED] = '{0}AlertActivated'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.ALERT_DEACTIVATED] = '{0}AlertDeactivated'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.ALERT_PAUSED] = '{0}AlertPaused'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.ALERT_RESUMED] = '{0}AlertResumed'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.ALERT_STOPPED] = '{0}AlertStopped'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.ALERT_ABORTED] = '{0}AlertAborted'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.ALERT_RESET] = '{0}AlertReset'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.SENSOR_JOINED] = '{0}SensorJoined'.format(alertName)
            eventObjectIds[configuration.AlertEvent.Type.SENSOR_LEFT] = '{0}SensorLeft'.format(alertName)

            eventObjects = self.linknx.getObjects(objectIds=eventObjectIds.values())
            eventObjectValues = eventObjects.getValues()

            # Check all events are in the dictionary. If not, that denotes a
            # coding error in the test.
            for eventType in configuration.AlertEvent.Type.getAll():
                self.assertTrue(eventType in eventObjectIds)

            for eventType, eventObjectId in eventObjectIds.items():
                eventState = eventObjectValues[eventObjectId]
                expectedState = eventType in firedEvents
                self.assertEqual(eventState, expectedState, 'Event {0} should be {1}.\nState of all event objects is following:{2}'.format(eventObjectId, expectedState, eventObjectValues))
            if resetsToOff:
                for eventType, eventObjectId in eventObjectIds.items():
                    self.linknx.getObject(eventObjectId).value = False

            # Clear email so that test does not complain about emails not
            # been treated.
            self.emailInfo = None

        self.assertAlert([], [], [])
        assertAlertEvents([], 'Intrusion')

        # Prealert.
        prealertStartTime = time.time()
        kitchenWindow.watchedObject.value = True
        self.waitDuring(0.1, "Let 'alert started' event be raised.")
        assertAlertEvents((configuration.AlertEvent.Type.PREALERT_STARTED,), 'Intrusion')
        self.waitUntil(prealertStartTime + kitchenWindow.getPrealertDuration() + 0.2, 'Waiting for prealert to expire.', [lambda: self.assertAlert([kitchenWindow],[],[]), lambda: assertAlertEvents([], 'Intrusion', resetsToOff=False)], 0.2, 0.4) 
        kitchenWindow.watchedObject.value = False # Release sensor trigger now to be able to trigger it again in a while.
        assertAlertEvents((configuration.AlertEvent.Type.SENSOR_JOINED, configuration.AlertEvent.Type.ALERT_ACTIVATED), 'Intrusion')

        # Alert.
        self.waitUntil(prealertStartTime + kitchenWindow.getPrealertDuration() + kitchenWindow.getAlertDuration() + 0.8, 'Waiting for alert to expire', [lambda: self.assertAlert([],[kitchenWindow],[kitchenWindow]), lambda: assertAlertEvents([], 'Intrusion')], 0.2, 1.0)
        assertAlertEvents((configuration.AlertEvent.Type.ALERT_PAUSED, configuration.AlertEvent.Type.SENSOR_LEFT, configuration.AlertEvent.Type.ALERT_DEACTIVATED), 'Intrusion')

        # Paused.
        self.assertAlert([], [], [kitchenWindow])

        # Resumed.
        alertResumeTime = time.time()
        kitchenWindow.watchedObject.value = True
        self.waitDuring(0.3, 'Waiting for alert to resume', [])
        kitchenWindow.watchedObject.value = False # Release sensor trigger now to be able to trigger it again in a while.
        assertAlertEvents((configuration.AlertEvent.Type.ALERT_RESUMED, configuration.AlertEvent.Type.SENSOR_JOINED, configuration.AlertEvent.Type.ALERT_ACTIVATED), 'Intrusion')

        # Alert.
        self.waitUntil(alertResumeTime + kitchenWindow.getAlertDuration() + 0.5, 'Waiting for alert to expire', [lambda: self.assertAlert([],[kitchenWindow],[kitchenWindow]), lambda: assertAlertEvents([], 'Intrusion')], 0.2, 0.7)
        assertAlertEvents((configuration.AlertEvent.Type.ALERT_PAUSED, configuration.AlertEvent.Type.SENSOR_LEFT, configuration.AlertEvent.Type.ALERT_DEACTIVATED), 'Intrusion')

        # Paused.
        self.assertAlert([], [], [kitchenWindow])

        # Stopped.
        self.alarmDaemon.getAlertByName('Intrusion').persistenceObject.value = False
        self.waitDuring(0.4, 'Waiting for alert to stop.')
        assertAlertEvents((configuration.AlertEvent.Type.ALERT_RESET, configuration.AlertEvent.Type.ALERT_STOPPED), 'Intrusion')

        # Raise a new alert. Should begin with a prealert.
        # Prealert.
        prealertStartTime = time.time()
        kitchenWindow.watchedObject.value = True
        self.waitDuring(0.1, "Let 'alert started' event be raised.")
        assertAlertEvents((configuration.AlertEvent.Type.PREALERT_STARTED,), 'Intrusion')
        self.waitUntil(prealertStartTime + kitchenWindow.getPrealertDuration() + 0.2, 'Waiting for prealert to expire.', [lambda: self.assertAlert([kitchenWindow],[],[]), lambda: assertAlertEvents([], 'Intrusion', resetsToOff=False)], 0.2, 0.4) 
        kitchenWindow.watchedObject.value = False # Release sensor trigger now to be able to trigger it again in a while.
        assertAlertEvents((configuration.AlertEvent.Type.SENSOR_JOINED, configuration.AlertEvent.Type.ALERT_ACTIVATED), 'Intrusion')

        # Alert. Stop it in the middle of the alert to test manual alert
        # abortion.
        self.alarmDaemon.getAlertByName('Intrusion').persistenceObject.value = False
        self.waitDuring(0.4, 'Waiting for alert to stop.')
        assertAlertEvents((configuration.AlertEvent.Type.SENSOR_LEFT, configuration.AlertEvent.Type.ALERT_DEACTIVATED, configuration.AlertEvent.Type.ALERT_RESET, configuration.AlertEvent.Type.ALERT_STOPPED), 'Intrusion')

        # Raise a new alert. Should begin with a prealert.
        # Prealert.
        prealertStartTime = time.time()
        kitchenWindow.watchedObject.value = True
        self.waitDuring(0.1, "Let 'alert started' event be raised.")
        assertAlertEvents((configuration.AlertEvent.Type.PREALERT_STARTED,), 'Intrusion')
        prealertDuration = kitchenWindow.getPrealertDuration()
        self.waitUntil(prealertStartTime + prealertDuration / 2.0, 'Waiting for half of the prealert to expire.', [lambda: self.assertAlert([kitchenWindow],[],[]), lambda: assertAlertEvents([], 'Intrusion', resetsToOff=False)], 0.2, 0.4)

        # Abort alert before it becomes active.
        self.changeAlarmMode('Presence', '*****@*****.**')
        self.alarmDaemon.getAlertByName('Intrusion').persistenceObject.value = False
        self.waitDuring(prealertDuration + 0.4, 'Waiting for alert to stop.')
        assertAlertEvents((configuration.AlertEvent.Type.ALERT_ABORTED, configuration.AlertEvent.Type.ALERT_STOPPED), 'Intrusion')

        # Check the Temperature alert since it has no persistence.
        temperatureSensor = self.alarmDaemon.getSensorByName('OutdoorTemperature')
        temperatureSensor.watchedObject.value = 31.0
        self.waitDuring(0.8, 'Let the Temperature alert be raised.')
        assertAlertEvents((configuration.AlertEvent.Type.SENSOR_JOINED, configuration.AlertEvent.Type.SENSOR_LEFT, configuration.AlertEvent.Type.ALERT_DEACTIVATED, configuration.AlertEvent.Type.PREALERT_STARTED, configuration.AlertEvent.Type.ALERT_STOPPED, configuration.AlertEvent.Type.ALERT_RESET, configuration.AlertEvent.Type.ALERT_ACTIVATED), 'Temperature')
Example #44
0
 def fireEvent(self, eventType):
     logger.reportInfo('Firing event {0} for {1}'.format(eventType, self))
     self.eventManager.fireEvent(
         eventType, 'Alert {0}: {1}'.format(self.name, eventType), self)
Example #45
0
 def terminate(self):
     logger.reportInfo('Terminating homewatcher daemon...')
     self._isTerminated = True
     self.disableAllSensors()
Example #46
0
 def terminate(self):
     logger.reportInfo('Terminating homewatcher daemon...')
     self._isTerminated = True
     self.disableAllSensors()