def create(self, configPath, userPath, command): self._configPath = configPath self._userPath = userPath self._command = command if not os.path.exists(self._configPath): _log = Log() _log.create("", False, True, LogLevel.Debug , LogLevel.Debug, LogLevel.Debug) _log.write(LogLevel.Error, "Cannot Find a path to the configuration files at {0}. Exiting...".format(self._configPath)) exit(1) # Add the default options self.AddOptionString("ConfigPath", configPath, False) # Path to the OpenZWave config folder. self.AddOptionString("UserPath",userPath, False) # Path to the user's data folder. self.AddOptionBool("Logging",True) # Enable logging of library activity. self.AddOptionString("LogFileName","OZWEmul_Log.txt",False) # Name of the log file (can be changed via Log::SetLogFileName) self.AddOptionBool("AppendLogFile",False) # Append new session logs to existing log file (False = overwrite) self.AddOptionBool("ConsoleOutput",True) # Display log information on console (as well as save to disk) self.AddOptionInt("SaveLogLevel", LogLevel.Detail) # Save (to file) log messages equal to or above Detail self.AddOptionInt("QueueLogLevel", LogLevel.Debug) # Save (in RAM) log messages equal to or above Debug self.AddOptionInt("DumpTriggerLevel", LogLevel.Never) # Default is to never dump RAM-stored log messages self.AddOptionBool("Associate",True) # Enable automatic association of the controller with group one of every device. self.AddOptionString("Exclude","", True) # Remove support for the listed command classes. self.AddOptionString("Include","",True) # Only handle the specified command classes. The Exclude option is ignored if anything is listed here. self.AddOptionBool("NotifyTransactions",False) # Notifications when transaction complete is reported. self.AddOptionString("Interface","",True) # Identify the serial port to be accessed (TODO: change the code so more than one serial port can be specified and HID) self.AddOptionBool("SaveConfiguration",True) # Save the XML configuration upon driver close. self.AddOptionInt("DriverMaxAttempts",0) self.AddOptionInt("PollInterval",30000) # 30 seconds (can easily poll 30 values in this time; ~120 values is the effective limit for 30 seconds) self.AddOptionBool("IntervalBetweenPolls",False) # if False, try to execute the entire poll list within the PollInterval time frame # if True, wait for PollInterval milliseconds between polls self.AddOptionBool("SuppressValueRefresh",False) # if True, notifications for refreshed (but unchanged) values will not be sent self.AddOptionBool("PerformReturnRoutes",True) # if true, return routes will be updated self.AddOptionString("NetworkKey", "", False) self.AddOptionBool("RefreshAllUserCodes", False) # if true, during startup, we refresh all the UserCodes the device reports it supports. If False, we stop after we get the first "Available" slot (Some devices have 250+ usercode slots! - That makes our Session Stage Very Long ) self.AddOptionInt("RetryTimeout", RETRY_TIMEOUT) # How long do we wait to timeout messages sent self.AddOptionBool("EnableSIS", True) # Automatically become a SUC if there is no SUC on the network. self.AddOptionBool("AssumeAwake", True) # Assume Devices that Support the Wakeup CC are awake when we first query them.... self.AddOptionBool("NotifyOnDriverUnload", False) # Should we send the Node/Value Notifications on Driver Unloading - Read comments in Driver::~Driver() method about possible race conditions
class Manager(object): """Manager as singleton, and singleton options link""" instance = None initialized = False def __new__(cls, *args, **kargs): cls._log = None if OPTIONS is not None and OPTIONS.AreLocked: if Manager.instance is None: Manager.instance = object.__new__(cls, *args, **kargs) Manager.initialized = False else: Manager.initialized = True cls._options = OPTIONS return Manager.instance cls._log = Log() cls._log.create("", False, True, LogLevel.Debug, LogLevel.Debug, LogLevel.Debug) cls._log.write(LogLevel.Error, cls, "Options have not been created and locked. Exiting...") exit(1) return None def __init__(self): global MANAGER if not self.initialized: self._nodes = [] self.drivers = [] self._DeviceClass = [] self.zwNetworks = {} self.paramsConfig = {} self._stop = threading.Event() # find, configPath = self._options.GetOptionAsString( "ConfigPath") # self.readXmlDeviceClasses(configPath) # self.manufacturers = Manufacturers(configPath) # self.cmdClassRegistered = CommandClasses(self) MANAGER = self def __del__(self): global MANAGER MANAGER = None self.instance = None self.initialized = False self._stop.set() def Create(self): # Create the log file (if enabled) find, logging = self._options.GetOptionAsBool("Logging") if not find: logging = False find, userPath = self._options.GetOptionAsString("UserPath") if not find: userPath = "" find, logFileNameBase = self._options.GetOptionAsString("LogFileName") if not find: logFileNameBase = "OZWEmule_Log.txt" find, bAppend = self._options.GetOptionAsBool("AppendLogFile") if not find: bAppend = False find, bConsoleOutput = self._options.GetOptionAsBool("ConsoleOutput") if not find: bConsoleOutput = True find, nSaveLogLevel = self._options.GetOptionAsInt("SaveLogLevel") if not find: nSaveLogLevel = LogLevel.Debug #LogLevel.Detail find, nQueueLogLevel = self._options.GetOptionAsInt("QueueLogLevel") if not find: nQueueLogLevel = LogLevel.StreamDetail # LogLevel.Debug find, nDumpTrigger = self._options.GetOptionAsInt("DumpTriggerLevel") if not find: nDumpTrigger = LogLevel.Warning logFilename = userPath + logFileNameBase self._log = Log() self._log.create(logFilename, bAppend, bConsoleOutput, nSaveLogLevel, nQueueLogLevel, nDumpTrigger) self._log.setLoggingState(logging) self._options.setLog(self._log) find, configPath = self._options.GetOptionAsString("ConfigPath") self.readXmlDeviceClasses(configPath) self.manufacturers = Manufacturers(configPath) self.cmdClassRegistered = CommandClasses(self) self.cmdClassRegistered.RegisterCommandClasses() # try : # self.paramsConfig = readJsonFile('../data/config_emulation.json') # self._log.write(LogLevel.Always, self,"Config parameters loaded : {0}".format(self.paramsConfig)) # except: # self._log.write(LogLevel.Warning, self,"No correct file config for emulation in data path.") self.loadXmlConfig() # Scene.ReadScenes() self._log.write( LogLevel.Always, self, "OpenZwave-emulator Version {0} Starting Up".format( self.getVersionAsString())) for homeId in self.zwNetworks: self.startDriver(homeId) def GetValAsHex(self, value, nChar=2): print if type(value) != 'list': data = [value] else: data = value return GetDataAsHex(data, nChar) def checkVirtualCom(self, homeId): """Check if a virtual port emulator is running""" if 'configData' in self.zwNetworks[homeId]: configData = self.zwNetworks[homeId]['configData'] if 'virtualcom' in configData: if configData['virtualcom']['modul'] == 'socat': try: pids = psutil.pids() for i in pids: p = psutil.Process(i) if p.name() == 'socat': chk_zwavectrl = False chk_emulator = False for lig in p.cmdline(): if lig.find(configData['virtualcom'] ['ports']['zwavectrl']) != -1: chk_zwavectrl = True if lig.find(configData['virtualcom'] ['ports']['emulator']) != -1: chk_emulator = True if chk_zwavectrl and chk_emulator: self._log.write( LogLevel.Always, self, "Virtual port socat running on {0} for zwave serial port {1}." .format( configData['virtualcom']['ports'] ['emulator'], configData['virtualcom']['ports'] ['zwavectrl'])) return True except Exception as e: self._log.write(LogLevel.Warning, self, "Socat Process error : {0}".format(e)) self._log.write(LogLevel.Always, self, "Virtual communication system not running.") return False def startVirtualCom(self, homeId, start=False): if 'configData' in self.zwNetworks[ homeId] and 'virtualcom' in self.zwNetworks[homeId][ 'configData']: virtualcom = self.zwNetworks[homeId]['configData']['virtualcom'] if start or virtualcom['start'] == 'auto': if not self.checkVirtualCom(homeId): if virtualcom['modul'] == 'socat': if virtualcom['ports']['zwavectrl'] and virtualcom[ 'ports']['emulator']: self._log.write( LogLevel.Info, self, "Starting Virtual communication system...") subprocess.Popen([ 'socat', '-d', '-d', 'PTY,ignoreeof,echo=0,raw,link={0}'.format( virtualcom['ports']['zwavectrl']), 'PTY,ignoreeof,echo=0,raw,link={0}'.format( virtualcom['ports']['emulator']) ]) self._stop.wait(1) if not self.checkVirtualCom(homeId): self._log.write( LogLevel.Error, self, "Error on starting Virtual communication system." ) else: self._log.write( LogLevel.Warning, self, "Virtual communication configuration not handled : {0}" .format(virtualcom)) else: self._log.write( LogLevel.Always, self, "Virtual communication system not in auto start-up.") else: self._log.write( LogLevel.Warning, self, "No configuration set for virtual communication on zwave HomeId {0}" .format(homeId)) def getVersionAsString(self): return "{0}.{1}.{2}".format(ozw_vers_major, ozw_vers_minor, ozw_vers_revision) def readXmlNetwork(self, fileConf): return networkFileConfig(fileConf) def readXmlDeviceClasses(self, pathConf): self._DeviceClass = DeviceClasses(pathConf) def loadXmlConfig(self): dataDir = '../data' files = os.listdir(dataDir) xmlFormat = r"^zwcfg_0x[0-9,a-f,A-F]{8}.xml$" for f in files: if re.match(xmlFormat, f) is not None: if f not in self.zwNetworks: self._log.write( LogLevel.Always, self, "Find and loading {0} openzwave file config....". format(dataDir + "/" + f)) xmlData = self.readXmlNetwork(dataDir + "/" + f) driverData = xmlData.getDriver(0) homeId = driverData['homeId'] self.zwNetworks[homeId] = {'xmlData': xmlData} configFile = f.replace('xml', 'json') try: self.zwNetworks[homeId]['configData'] = readJsonFile( dataDir + "/" + configFile) self._log.write( LogLevel.Always, self, "Config for emulation loaded : {0}".format( self.zwNetworks[homeId]['configData'])) except: self._log.write( LogLevel.Error, self, "Error on readind config file for emulation :{0}". format(dataDir + "/" + configFile)) print driverData nodes = self.zwNetworks[homeId]['xmlData'].listeNodes() for node in nodes: xmlNode = self.zwNetworks[homeId]['xmlData'].getNode( node) self.addXmlNode(homeId, xmlNode['id'], xmlNode) self.startVirtualCom(homeId) self.drivers.append( Driver(self, self.getNode(homeId, driverData['nodeId']), driverData)) print " +++ driver added +++" else: self._log.write( LogLevel.Warning, self, "Zwave network allready loaded :{0}".format(f)) def resetZwNetwork(self, oldHomeId, newHomeId): ctrl = self.GetDriver(oldHomeId) self.zwNetworks[newHomeId] = self.zwNetworks.pop(oldHomeId) self.zwNetworks[newHomeId]['configData']['controller'][ 'fakeneighbors'] = {} nodes = [] for node in self.zwNetworks[newHomeId]['configData']['nodes']: if node['nodeid'] == ctrl._node.nodeId: nodes = [node] break self.zwNetworks[newHomeId]['configData']['nodes'] = nodes def getZwVersion(self, homeId): if homeId: if 'controller' in self.zwNetworks[homeId]['configData']: if 'zwversion' in self.zwNetworks[homeId]['configData'][ 'controller']: return self.zwNetworks[homeId]['configData']['controller'][ 'zwversion'] return ZWVERSION return "" def getSerialAPIVersion(self, homeId): if homeId: if 'controller' in self.zwNetworks[homeId]['configData']: if 'serialapiversion' in self.zwNetworks[homeId]['configData'][ 'controller']: return self.zwNetworks[homeId]['configData']['controller'][ 'serialapiversion'] return SERIALAPIVERSION return "" def getRFChipVersion(self, homeId): if homeId: if 'controller' in self.zwNetworks[homeId]['configData']: if 'rfchipversion' in self.zwNetworks[homeId]['configData'][ 'controller']: return self.zwNetworks[homeId]['configData']['controller'][ 'rfchipversion'] return RFCHIPVERSION return "" def getFakeNeighbors(self, homeId, nodeId): if homeId: if 'controller' in self.zwNetworks[homeId]['configData']: if 'fakeneighbors' in self.zwNetworks[homeId]['configData'][ 'controller']: if str(nodeId) in self.zwNetworks[homeId]['configData'][ 'controller']['fakeneighbors']: return self.zwNetworks[homeId]['configData'][ 'controller']['fakeneighbors'][str(nodeId)] return [] return [] def getEmulNodeData(self, homeId, nodeId): if homeId: if 'nodes' in self.zwNetworks[homeId]['configData']: for n in self.zwNetworks[homeId]['configData']['nodes']: if 'nodeid' in n and n['nodeid'] == nodeId: return n return { "nodeid": nodeId, "comment": "Auto comment", "failed": False, "timeoutwakeup": 0, "wakeupduration": 0, "pollingvalues": [], "cmdclssextraparams": {} } def matchHomeID(self, homeId): """Evalue si c'est bien un homeID, retourne le homeID ou None""" if type(homeId) in [long, int]: return "0x%0.8x" % homeId homeIDFormat = r"^0x[0-9,a-f]{8}$" if re.match(homeIDFormat, homeId.lower()) is not None: return homeId.lower() return None def addXmlNode(self, homeId, nodeId, xmlNode): for n in self._nodes: if n.homeId == homeId and n.nodeId == nodeId: self._log.write( LogLevel.Warning, self, "Node {0} on homeId {1} from xml config file allready exist, abording add." .format(nodeId, homeId)) return None node = Node(self, homeId, nodeId, xmlNode) self._nodes.append(node) self._log.write( LogLevel.Info, self, "Node {0} on homeId {1} added from xml config file.".format( nodeId, homeId)) def GetCommandClassId(self, cmdClass): return self.cmdClassRegistered.GetCommandClassId(cmdClass) def getNode(self, homeId, nodeId): for n in self._nodes: if (n.homeId == homeId or self.matchHomeID(n.homeId) == homeId) and n.nodeId == nodeId: return n return None def getNodesOfhomeId(self, homeId): """Return all nodes from zwave network""" retval = [] for n in self._nodes: if (n.homeId == homeId or self.matchHomeID(n.homeId) == homeId): retval.append(n) return retval def getListNodeId(self, homeId): listN = [] for n in self._nodes: if n.homeId == homeId: listN.append(n.nodeId) return listN def copyNode(self, node): """Copy a node object add it on manager list nodes and set it not include. Return the new node object. """ # newNode = copy.copy(node) # newNode.homeId = 0 nodeId = self.getFirstFreeNodeIdBeforeInclude() newNode = Node(self, 0, nodeId, node.GetXmlData) newNode.ClearAddingNode() newNode.Reset() self._nodes.append(newNode) self._log.write( LogLevel.Info, self, "Manager create new node by copy node {0}.{1}.".format( node.homeId, node.nodeId)) return newNode def getFirstFreeNodeIdBeforeInclude(self): """"Return the fisrt nodeId free to add node in manager list """ listId = [] for n in self._nodes: if n.homeId == 0: listId.append(n.nodeId) for id in range(1, 255): if id not in listId: break return id def includeNewNode(self, homeId, node): """Include a new node in zwave network""" ctrl = self.GetDriver(homeId) return ctrl.includeNode(node) def resetHomeId(self, oldHomeId, newHomeId): for n in self._nodes: if (n.homeId == oldHomeId or self.matchHomeID(n.homeId) == oldHomeId): n.homeId = newHomeId def startDriver(self, homeId): if self.checkVirtualCom(homeId): serialport = self.zwNetworks[homeId]['configData']['virtualcom'][ 'ports']['emulator'] if self.drivers: for driver in self.drivers: if driver.serialport is None: # Object Driver not assigned and ready to set serialport driver.setSerialport(serialport) self._log.write( LogLevel.Info, self, "Added driver for controller {0}".format( serialport)) driver.Start() return True elif driver.serialport == serialport: self._log.write( LogLevel.Warning, self, "Cannot add driver for controller {0} - driver already exists" .format(serialport)) break self._log.write( LogLevel.Warning, self, "Cannot add driver for controller {0} - no emulate driver available" .format(serialport)) else: self._log.write( LogLevel.Info, Warning, "Cannot add driver for controller {0} - no emulate driver loaded" .format(serialport)) return False def GetDriver(self, homeId): for driver in self.drivers: if homeId == driver.homeId: return driver return None def IsSupportedCommandClasses(self, clsId): return self.cmdClassRegistered.IsSupported(clsId) def SetProductDetails(self, node, manufacturerId, productType, productId): manufacturerName = "Unknown: id=%.4x" % manufacturerId productName = "Unknown: type=%.4x, id=%.4x" % (productType, productId) configPath = "" # Try to get the real manufacturer and product names manufacturer = self.manufacturers.getManufacturer(manufacturerId) if manufacturer: # Replace the id with the real name manufacturerName = manufacturer['name'] # Get the product for p in manufacturer['products']: if (int(productId, 16) == int(p['id'], 16)) and (int( productType, 16) == int(p['type'], 16)): productName = p['name'] configPath = p['config'] if 'config' in p else "" # Set the values into the node # Only set the manufacturer and product name if they are # empty - we don't want to overwrite any user defined names. if node.GetManufacturerName == "": node.SetManufacturerName(manufacturerName) if node.GetProductName == "": node.SetProductName(productName) node.SetManufacturerId("%.4x" % manufacturerId) node.SetProductType("%.4x" % productType) node.SetProductId("%.4x" % productId) return configPath