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.')
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.")
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
def assertShellCommand(self, command, expectedStdOut=None, expectedStdErr=None, expectedReturnCode=None, stdin=None): shouldSucceed = expectedStdErr == None and (expectedReturnCode == None or expectedReturnCode == 0) # Redirect command's stdout to a file so that we can compare with what # is expected. stdoutHandle, stdoutFilename = tempfile.mkstemp() stderrHandle, stderrFilename = tempfile.mkstemp() try: with open(stdoutFilename, 'w+') as stdoutFD: with open(stderrFilename, 'w+') as stderrFD: returnCode = subprocess.call(command, stdout=stdoutFD, stderr=stderrFD, stdin=stdin) if (returnCode == 0) != shouldSucceed: logger.reportError('Command {command} output is {out}'.format(command=command, out=stdoutFD.readlines())) logger.reportError('stderr is {out}'.format(out=stderrFD.readlines())) self.fail('Command was expected to {failOrSucceed} but it {status}. Return code is {returnCode}'.format(failOrSucceed='succeed' if shouldSucceed else 'fail', status='succeeded' if returnCode == 0 else 'failed', returnCode=returnCode)) # Compare output to what is expected. if expectedStdOut != None: self.assertFilesAreEqual(stdoutFilename, expectedStdOut) else: with open(stdoutFilename, 'r') as stdoutFD: lines = stdoutFD.readlines() self.assertEqual(os.stat(stdoutFilename).st_size, 0, 'No output was expected on standard output from this command. Output is {0}'.format(lines)) if expectedStdErr != None: self.assertFilesAreEqual(stderrFilename, expectedStdErr) else: with open(stderrFilename, 'r') as stderrFD: lines = stderrFD.readlines() self.assertEqual(os.stat(stderrFilename).st_size, 0, 'No output was expected on standard error from this command. Output is {0}'.format(lines)) if expectedReturnCode != None: self.assertEqual(returnCode, expectedReturnCode) finally: os.remove(stdoutFilename) os.remove(stderrFilename)
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
# Parse command line arguments. communicatorAddress = ('127.0.0.1',1029) arguments = [] verbosity = logging.INFO for option, value in options: if option == '-c' or option == '--comm-addr': communicatorAddress = parseAddress(value, option) elif option == '-a' or option == '--argument': arguments.append(value) elif option == '--help': printUsage() sys.exit(1) elif option == '-v' or option == '--verbose': verbosity = logger.parseLevel(value) else: logger.reportError('Unrecognized option ' + option) sys.exit(2) if not remainder: logger.reportError('Missing function name.') printUsage() sys.exit(3) if len(remainder) > 1: logger.reportError('Too many arguments: {0}'.format(remainder)) printUsage() sys.exit(4) functionName = remainder[0] # Init logger.
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
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)
def assertShellCommand(self, command, expectedStdOut=None, expectedStdErr=None, expectedReturnCode=None, stdin=None): shouldSucceed = expectedStdErr == None and (expectedReturnCode == None or expectedReturnCode == 0) # Redirect command's stdout to a file so that we can compare with what # is expected. stdoutHandle, stdoutFilename = tempfile.mkstemp() stderrHandle, stderrFilename = tempfile.mkstemp() try: with open(stdoutFilename, 'w+') as stdoutFD: with open(stderrFilename, 'w+') as stderrFD: returnCode = subprocess.call(command, stdout=stdoutFD, stderr=stderrFD, stdin=stdin) if (returnCode == 0) != shouldSucceed: logger.reportError( 'Command {command} output is {out}'.format( command=command, out=stdoutFD.readlines())) logger.reportError( 'stderr is {out}'.format(out=stderrFD.readlines())) self.fail( 'Command was expected to {failOrSucceed} but it {status}. Return code is {returnCode}' .format(failOrSucceed='succeed' if shouldSucceed else 'fail', status='succeeded' if returnCode == 0 else 'failed', returnCode=returnCode)) # Compare output to what is expected. if expectedStdOut != None: self.assertFilesAreEqual(stdoutFilename, expectedStdOut) else: with open(stdoutFilename, 'r') as stdoutFD: lines = stdoutFD.readlines() self.assertEqual( os.stat(stdoutFilename).st_size, 0, 'No output was expected on standard output from this command. Output is {0}' .format(lines)) if expectedStdErr != None: self.assertFilesAreEqual(stderrFilename, expectedStdErr) else: with open(stderrFilename, 'r') as stderrFD: lines = stderrFD.readlines() self.assertEqual( os.stat(stderrFilename).st_size, 0, 'No output was expected on standard error from this command. Output is {0}' .format(lines)) if expectedReturnCode != None: self.assertEqual(returnCode, expectedReturnCode) finally: os.remove(stdoutFilename) os.remove(stderrFilename)
import logging from xml.dom.minidom import parseString from threading import * from pyknx import logger from pyknx.linknx import * def printUsage(): print(__doc__) if __name__ == '__main__': logger.initLogger(None, logging.INFO, usesDetailedLogging=False) try: options, remainder = getopt.getopt(sys.argv[1:], 'r:w:h:p:v:R:', ['read=', 'write=', 'regex=', 'host=', 'port=','verbose=','help']) except getopt.GetoptError as err: logger.reportError(sys.exc_info()[1]) sys.exit(2) print('*******************') print('DEPRECATION NOTICE:') print('*******************') print('This script is now deprecated and its functionalities have been split into the two scripts pyknxread.py and pyknxwrite.py. An additional script named pyknxexecute.py allows for executing actions (which functionality is not handled by pyknxclient.py at all).') # Parse command line arguments. reads = False writes = False objectIds = [] isRegex = False host = 'localhost' port = 1028 verbosity = logging.WARNING
from pyknx.linknx import * def printUsage(): print(__doc__) if __name__ == '__main__': logger.initLogger(None, logging.INFO, usesDetailedLogging=False) try: options, remainder = getopt.getopt(sys.argv[1:], 'r:w:h:p:v:R:', [ 'read=', 'write=', 'regex=', 'host=', 'port=', 'verbose=', 'help' ]) except getopt.GetoptError as err: logger.reportError(sys.exc_info()[1]) sys.exit(2) print('*******************') print('DEPRECATION NOTICE:') print('*******************') print( 'This script is now deprecated and its functionalities have been split into the two scripts pyknxread.py and pyknxwrite.py. An additional script named pyknxexecute.py allows for executing actions (which functionality is not handled by pyknxclient.py at all).' ) # Parse command line arguments. reads = False writes = False objectIds = [] isRegex = False host = 'localhost'