class HomeManager(object): def __init__(self, device_path, ozw_log_level, logger): self.logger = logger options = ZWaveOption(device_path, config_path="./venv/lib/python3.6/site-packages/python_openzwave/ozw_config", user_path=".", cmd_line="") options.set_log_file("OZW.log") options.set_append_log_file(False) options.set_save_log_level(ozw_log_level) options.set_console_output(False) options.set_logging(True) options.lock() self.options = options self.network = ZWaveNetwork(options, log=None, autostart=False) self.client = InfluxDBClient(database=DATABASE) def start(self): self.logger.info("Starting network...") self.network.start() def stop_signal(self, signum, frame): self.stop() def stop(self): self.logger.info("Stopping network...") self.network.nodes[3].values[72057594098484979].data = 3600 self.network.stop() self.logger.info("Stopped") def connect_signals(self): dispatcher.connect(self.signal_network_ready, self.network.SIGNAL_NETWORK_READY) signal.signal(signal.SIGINT, self.stop_signal) # Note -- the name of the network parameter must not change! def signal_network_ready(self, network): if self.network is not network: return else: del network ozw_debug(self.logger, self.network) self.logger.info("Network is ready!") self.network.nodes[3].values[72057594098484979].data = 15 self.start_polling() @staticmethod def is_sensor(node): return isinstance(node, ZWaveNodeSensor) and not len(node.get_sensors()) is 0 def start_polling(self): Timer(TIME_INTERVAL, self.start_polling).start() labels_to_be_polled = {'Luminance', 'Relative Humidity', 'Temperature', 'Ultraviolet', 'Alarm Level', 'Burglar'} for node_id, node in self.network.nodes.items(): if self.is_sensor(node): for val_id in self.network.nodes[node_id].values: val = self.network.nodes[node_id].values[val_id] if val.label in labels_to_be_polled: self.logger.info("Received value refresh %s: %s", val.id_on_network, val) self.client.write_points(value_refresh_to_influxdb_json(node, val))
def createNetwork(options, log=None, autostart=True, kvals=True): network = ZWaveNetwork(options, log=None) time_started = 0 for i in range(0, 100): if network.state >= network.STATE_AWAKED: break else: time_started += 1 time.sleep(1.0) if network.state < network.STATE_AWAKED: print("Network is not awake but continue anyway") for i in range(0, 300): if network.state >= network.STATE_READY: print("home ID {}".format(home_id)) break else: sys.stdout.write(".") time_started += 1 #sys.stdout.write(network.state_str) #sys.stdout.write("(") #sys.stdout.write(str(network.nodes_count)) #sys.stdout.write(")") #sys.stdout.write(".") sys.stdout.flush() time.sleep(1.0) if not network.is_ready: print("network is not ready but start anyway") network.start() return network
def startup_zwave(self, args={}): ffEvent(self._id,{'zwave':'starting_up'}) zwaveSetup = ZWaveOption(self._port, self._configFile) zwaveSetup.set_console_output(False) zwaveSetup.lock() network = ZWaveNetwork(zwaveSetup, autostart=False) network.start() stdout.write("Waking up Zwave (This can take up to 5 minutes)") for i in xrange(3): if network.state >= network.STATE_AWAKED: logging.info('Zwave Network Awake') break else: stdout.write(".") stdout.flush() sleep(1) for x in xrange(3): if network.state >= network.STATE_READY: ffEvent(self._id,{'zwave':'network_ready'}) else: stdout.write(".") stdout.flush() sleep(.5) return network
def main(): global LIGHT, PUSHOVER logging.basicConfig(level=logging.INFO, format="%(asctime)-15s %(levelno)d %(message)s") # TODO: Put in argparse device = None if device is None: device = discover_device() check_device(device) CONFIG = read_config("config.json") if CONFIG.pushover.apikey is not None: logging.info("Setting up Pushover") PUSHOVER = Pushover(CONFIG.pushover.apikey, CONFIG.pushover.userkeys) connect_signals() options = create_zwave_options(device=device) network = ZWaveNetwork(options, log=None, autostart=False) queue = threading.Thread(target=DataQueue.worker, kwargs={"location": "home.db"}, name="db") queue.start() try: network.start() # Start REPL with a union where globals override locals local = locals().copy() local.update(globals()) code.interact(local=local) except KeyboardInterrupt: pass finally: logging.info("\nStopping network ...") network.stop() logging.info("Stopping data queue ...") DataQueue.stop() queue.join()
class Backend(): def __init__(self): ################### instanciation de l'objet backend ######################################################## ###### options needed for python openzwave library like config files path, logging, device = configpi.interface options = ZWaveOption(device, config_path="/home/pi/git-repo/python-openzwave/openzwave/config", user_path=".", cmd_line="") options.set_log_file("OZW.log") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level('Warning') options.set_logging(True) options.lock() # creation of the object network using the options entity already created self.network = ZWaveNetwork(options, autostart=False) ###### These dispatchers associate a method to a signal. the signals are generated by the library python-openzwave. ###### Once the signal is reveived. It's associated method is executed (see "_node_added" example below in "_network_started" method) dispatcher.connect(self._network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self._network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(self._network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) ###### backend object attributes # self.devices = OrderedDict() ### will contain the list of nodes in the network # self.sensors = OrderedDict() ### will contain the list of sensors (only) in the network self.node_added = False self.node_removed = False self.timestamps = {} ### will contain the time of the last values' update for each sensor self.queryStages = { ### the diffrent stages that a node object gets through before being ready "None" : 1, # Query process hasn't started for this node "ProtocolInfo" : 2, # Retrieve protocol information "Probe" : 3, # Ping device to see if alive "WakeUp" : 4, # Start wake up process if a sleeping node "ManufacturerSpecific1" : 5, # Retrieve manufacturer name and product ids if ProtocolInfo lets us "NodeInfo" : 6, # Retrieve info about supported, controlled command classes "SecurityReport" : 7, # Retrieve a list of Command Classes that require Security "ManufacturerSpecific2" : 8, # Retrieve manufacturer name and product ids "Versions" : 9, # Retrieve version information "Instances" : 10, # Retrieve information about multiple command class instances "Static" : 11, # Retrieve static information (doesn't change) "Probe1" : 12, # Ping a device upon starting with configuration "Associations" : 13, # Retrieve information about associations "Neighbors" : 14, # Retrieve node neighbor list "Session" : 15, # Retrieve session information (changes infrequently) "Dynamic" : 16, # Retrieve dynamic information (changes frequently) "Configuration" : 17, # Retrieve configurable parameter information (only done on request) "Complete" : 18 # Query process is completed for this node } ####################################################################################################################### ############# NETWORK ################################################################################################# ####################################################################################################################### def _network_started(self, network): # executed once the software representation is started. the discovery of the network components has begun. they will be mapped into objects print("network started - %d nodes were found." % network.nodes_count) # these dispatchers associate a method to a signal. the signals are generated by the library python-openzwave. # a signal may contain a number of parameters that are passed to the method associated to the signal. # for exemple, the dispatcher below associates the signal "SIGNAL_NODE_ADDED" to the method "_node_added" that is implemented below (line 111). # the signal "SIGNAL_NODE_ADDED" transports two parameters which are the objects network and node. # once this signal is received, these two parameters will be passed to the method "_node_added" and the method will be executed. dispatcher.connect(self._node_added, ZWaveNetwork.SIGNAL_NODE_ADDED) dispatcher.connect(self._node_removed, ZWaveNetwork.SIGNAL_NODE_REMOVED) dispatcher.connect(self._debug_node_naming, ZWaveNetwork.SIGNAL_NODE_NAMING) dispatcher.connect(self._debug_node_new, ZWaveNetwork.SIGNAL_NODE_NEW) dispatcher.connect(self._debug_node_protocol_info, ZWaveNetwork.SIGNAL_NODE_PROTOCOL_INFO) def _network_failed(self, network): # executed once the software representation has failed print("network failed :(") def _network_ready(self, network): # executed once the software representation is ready print("network : ready : %d nodes were found." % network.nodes_count) print("network : controller is : %s" % network.controller) # dispatcher.connect(self._node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self._node_event, ZWaveNetwork.SIGNAL_NODE_EVENT) dispatcher.connect(self._value_update, ZWaveNetwork.SIGNAL_VALUE) def _node_added(self, network, node): # executed when node is added to the software representation. it's executed after the method "_debug_node_new" (see below) print('node added: %s.' % node.node_id) self.timestamps["timestamp" + str(node.node_id)] = "No Measurement received yet" self.node_added = True def _node_removed(self, network, node): # executed when node is removed from the software representation print('node removed: %s.' % node.name) self.node_removed = True def _debug_node_naming(self, network, node): # executed when node is named print('node %s named: %s.' % (node.node_id, node.name)) def _debug_node_new(self, network, node_id): # executed when a new node is detected in the network print('New node is node: %s.' % node_id) def _debug_node_protocol_info(self, network, node): # executed when discovering the features of a new node print('node Protocol Info: %s.' % node.node_id)which def _node_update(self, network, node): # executed when the nodes features are received : product_name, manufacturer_id, ... print('node update: %s.' % node) def _node_event(self, network, node, signal, sender): # executed when the motion sensor's state is changed print('node event %s from node %s.' % (signal, node.node_id)) if node.isReady and node.product_name == "MultiSensor 6": values = node.get_values("All","User","All",True,"All") for value in values.itervalues(): if value.label == "Sensor": motion_value = value.data print('motion sensor value is now %s' %motion_value) def _value_update(self, network, node, value): # executed when a new value from a node is received print('Node %s: value update: %s is %s.' % (node.node_id, value.label, value.data)) self.timestamps["timestamp" + str(node.node_id)] = int(time.time()) def ordered_nodes_dict(self): #this method returns an ordered list of the network's nodes return OrderedDict(sorted(self.network.nodes.items())) ################################################################################################################ ######################## START AND STOP THE SOFTWARE REPRESENTATION ############################################ ################################################################################################################ def start(self): # this method starts the software representation global started if started: print "Already started" return started = True self.network.start() print "Z-Wave Network Starting..." for i in range(0, 300): if self.network.is_ready: break else: time.sleep(1.0) if not self.network.is_ready: print "Network is not ready but continue anyway" print "------------------------------------------------------------" print "Nodes in network : %s" % self.network.nodes_count print "------------------------------------------------------------" def stop(self): # this method stops the software representation global started started = False print "Stopping Z-Wave Network... " self.network.stop() def reset(self): # this method resets the controller node self.network.controller.hard_reset() return "Hard Reset Done" ######################################################################################################################### ############## YOUR WORK STARTS HERE #################################################################################### ######################################################################################################################### ######################################################################################################################### def network_preview(self): #### COMPLETE THIS METHOD ############## return "this method returns a JSON that lists all network nodes and gives some informations about each one of them like the ID, neighbors, ..." ########################################################################################################################### ########## Configuration of multisensors ################################################################################## ########################################################################################################################### def set_sensor(self, Grp_interval, Grp_reports, Wakeup_interval): #### COMPLETE THIS METHOD ############## return "this method configures the nodes whit a specific configuration set by us" def network_nodesConfiguration(self): #### COMPLETE THIS METHOD ############## return "this method returns a html containing the configuration of the network's nodes" def set_node_config_param(self, n, param, value): #### COMPLETE THIS METHOD ############## return "this method sets a configuration parameter to a precise value" def get_node_config_param(self, n, param): #### COMPLETE THIS METHOD ############## return "this method gets the value of a configuration parameter" ####################################################################################################################### ############# SENSORS ################################################################################################# ####################################################################################################################### def get_sensors(self): #### COMPLETE THIS METHOD ############## return "this method returns the list of sensors" def addNode(self): #### COMPLETE THIS METHOD ############## return "this method passes the controller to inclusion mode and gets it out of it after 20 seconds " def removeNode(self): #### COMPLETE THIS METHOD ############## return "this method passes the controller to exclusion mode and gets it out of it after 20 seconds " def allMeasures(self, n): #### COMPLETE THIS METHOD ############## return "this method gets all the measures of a specific sensor node" def temperature(self, n): #### HERE'S AN EXAMPLE OF A METHOD THAT GETS THE TEMPERATURE OF A SPECIFIC SENSOR NODE ############## for node in self.network.nodes.itervalues(): if node.node_id == n and node.isReady and n != 1 : values = node.get_values(0x31, "User", "All", True, False) for value in values.itervalues(): if value.label == "Temperature": val = round(value.data,1) return jsonify(controller = name, sensor = node.node_id, location = node.location, type = value.label.lower(), updateTime = self.timestamps["timestamp"+str(node.node_id)], value = val) return "Node not ready or wrong sensor node !" def humidity(self, n): #### HERE'S AN EXAMPLE OF A METHOD THAT GETS THE HUMIDITY OF A SPECIFIC SENSOR NODE ############## for node in self.network.nodes.itervalues(): if node.node_id == n and node.isReady and n != 1 : values = node.get_values(0x31, "User", "All", True, False) for value in values.itervalues(): if value.label == "Relative Humidity": val = int(value.data) return jsonify(controller = name, sensor = node.node_id, location = node.location, type = value.label.lower(), updateTime = self.timestamps["timestamp"+str(node.node_id)], value = val) return "Node not ready or wrong sensor node !" def luminance(self, n): for node in self.network.nodes.itervalues(): if node.node_id == n and node.isReady and n != 1 : values = node.get_values(0x31, "User", "All", True, False) for value in values.itervalues(): if value.label == "Luminance": val = int(value.data) return jsonify(controller = name, sensor = node.node_id, location = node.location, type = value.label.lower(), updateTime = self.timestamps["timestamp"+str(node.node_id)], value = val) return "Node not ready or wrong sensor node !" def motion(self, n): for node in self.network.nodes.itervalues(): if node.node_id == n and node.isReady and n != 1 : values = node.get_values(0x31, "User", "All", True, False) for value in values.itervalues(): if value.label == "Motion": val = int(value.data) return jsonify(controller = name, sensor = node.node_id, location = node.location, type = value.label.lower(), updateTime = self.timestamps["timestamp"+str(node.node_id)], value = val) return "Node not ready or wrong sensor node !" def battery(self, n): for node in self.network.nodes.itervalues(): if node.node_id == n and node.isReady and n != 1 : values = node.get_values(0x31, "User", "All", True, False) for value in values.itervalues(): if value.label == "Battery": val = int(value.data) return jsonify(controller = name, sensor = node.node_id, location = node.location, type = value.label.lower(), updateTime = self.timestamps["timestamp"+str(node.node_id)], value = val) return "Node not ready or wrong sensor node !" def get_nodes(self): #### COMPLETE THIS METHOD ############## return "this method returns the list of nodes " def set_node_location(self, n, value): #### COMPLETE THIS METHOD ############## return " this method sets the location of a specific sensor node" def set_node_name(self, n, value): #### COMPLETE THIS METHOD ############## return "this method sets the name of a specific sensor node" def get_node_location(self, n): #### COMPLETE THIS METHOD ############## return "this method gets the location of a specific sensor node" def get_node_name(self, n): #### COMPLETE THIS METHOD ############## return "this method gets the name of a specific sensor node" def get_neighbors(self, n): #### COMPLETE THIS METHOD ############## return "this method gets the list of neighbours of a specific sensor node"
class HomeManager(object): def __init__(self, device_path, ozw_log_level, logger): self.logger = logger options = ZWaveOption( device_path, config_path= "./venv/lib/python3.%d/site-packages/python_openzwave/ozw_config" % sys.version_info[1], user_path=".", cmd_line="") options.set_log_file("OZW.log") options.set_append_log_file(False) options.set_save_log_level(ozw_log_level) options.set_console_output(False) options.set_logging(True) options.lock() self.options = options self.network = ZWaveNetwork(options, log=None, autostart=False) self.csvfile = open('output.csv', 'a') self.writer = csv.writer(self.csvfile) self.stopping = False def start(self): self.logger.info("Starting network...") self.network.start() def stop_signal(self, signum, frame): self.stop() def stop(self): if self.stopping: return else: self.stopping = True self.logger.info("Stopping network...") self.network.stop() self.csvfile.close() self.logger.info("Stopped") def connect_signals(self): dispatcher.connect(self.signal_network_ready, self.network.SIGNAL_NETWORK_READY) signal.signal(signal.SIGINT, self.stop_signal) # Note -- the name of the network parameter must not change! def signal_network_ready(self, network): if self.network is not network: return else: del network ozw_debug(self.logger, self.network) self.logger.info("Network is ready!") dispatcher.connect(self.signal_node_update, self.network.SIGNAL_NODE) dispatcher.connect(self.signal_value_refreshed, self.network.SIGNAL_VALUE) # Note -- the names of the network/node/value parameters must not change! def signal_value_refreshed(self, network, node, value): if self.network is not network: return else: del network self.logger.info("Received value refresh %s: %s", value.id_on_network, value) self.writer.writerow(value_refresh_to_csv(node, value)) self.csvfile.flush() # Note -- the names of the network/node parameters must not change! def signal_node_update(self, network, node): return @staticmethod def is_sensor(node): return isinstance(node, ZWaveNodeSensor) and not len(node.get_sensors()) is 0
class Open_zwave(HAInterface): VERSION = '0.0.2' awake = False ready = False nodesdisplayed = False def louie_network_ready(network): self._logger.info(">>>>>>> Hello from network : I'm ready : %d nodes were found." % self._network.nodes_count) self._logger.info(">>>>>>> Hello from network : my controller is : %s" % self._network.controller) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.louie_value_update, ZWaveNetwork.SIGNAL_VALUE) def louie_node_update(network, node): self._logger.info('>>>>>>> Hello from node : %s.' % node) def louie_value_update(network, node, value): self._logger.info('>>>>>>> Hello from value : %s.' % value) def __init__(self, *args, **kwargs): self._serialDevicePath = kwargs.get('serialDevicePath', None) self._options = ZWaveOption(self._serialDevicePath, \ config_path="/usr/local/etc/openzwave/", \ user_path=".", cmd_line="") self._options.set_log_file("OZW_Log.log") self._options.set_append_log_file(False) self._options.set_console_output(False) #self._options.set_save_log_level(log) self._options.set_save_log_level('Info') self._options.set_logging(True) self._options.set_notify_transactions(True) self._options.lock() self._network = ZWaveNetwork(self._options, log=None,autostart=False) dispatcher.connect(self.louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) self._network.start() super(Open_zwave, self).__init__(self, *args, **kwargs) def _printNetwork(self, node): print print "------------------------------------------------------" print "%s - Name : %s" % (self._network.nodes[node].node_id, self._network.nodes[node].name) print "%s - Manufacturer name / id : %s / %s" % ( self._network.nodes[node].node_id, self._network.nodes[node].manufacturer_name, self._network.nodes[node].manufacturer_id) print "%s - Product name / id / type : %s / %s / %s" % ( self._network.nodes[node].node_id, self._network.nodes[node].product_name, self._network.nodes[node].product_id, self._network.nodes[node].product_type) print "%s - Version : %s" % (self._network.nodes[node].node_id, self._network.nodes[node].version) print "%s - Command classes : %s" % (self._network.nodes[node].node_id, self._network.nodes[node].command_classes_as_string) print "%s - Capabilities : %s" % (self._network.nodes[node].node_id, self._network.nodes[node].capabilities) print "%s - Neighbors : %s" % (self._network.nodes[node].node_id, self._network.nodes[node].neighbors) print "%s - Can sleep : %s" % (self._network.nodes[node].node_id, self._network.nodes[node].can_wake_up()) def _init(self, *args, **kwargs): super(Open_zwave, self)._init(self, *args, **kwargs) def _readInterface(self, lastPacketHash): if (self._network.state >= self._network.STATE_AWAKED and not self.awake): self.awake = True self._logger.info("Network Awaked") if (self._network.state >= self._network.STATE_READY and not self.ready): self.ready = True self._logger.info("Network Ready") self._logger.info("********************************* NETWORK READY ************************************") if not self.awake: time.sleep(1.0) self._logger.debug("Not awaked") return if self.awake and not self.ready: time.sleep(1.0) self._logger.debug("Not ready") return if not self.nodesdisplayed and self.ready: for node in self._network.nodes: self._printNetwork(node) self.nodesdisplayed = True time.sleep(1) def version(self): self._logger.info("Open_zwave Pytomation Driver version " + self.VERSION) self._logger.info("Use openzwave library : %s" % self._network.controller.ozw_library_version) self._logger.info("Use python library : %s" % self._network.controller.python_library_version) self._logger.info("Use ZWave library : %s" % self._network.controller.library_description) def on(self, address): node = int(address) for val in self._network.nodes[node].get_switches() : self._logger.info("Activate switch") self._network.nodes[node].set_switch(val,True) for val in self._network.nodes[node].get_dimmers() : self._logger.info("Activate dimmer : %s" % self._network.nodes[node]) self._network.nodes[node].set_dimmer(val,99) def off(self, address): node = int(address) for val in self._network.nodes[node].get_switches() : self._logger.info("Activate switch") self._network.nodes[node].set_switch(val,False) for val in self._network.nodes[node].get_dimmers() : self._logger.info("Activate dimmer : %s" % self._network.nodes[node]) self._network.nodes[node].set_dimmer(val,0) def status(self, address): node = int(address) for val in self._network.nodes[node].get_dimmers() : level = self._network.nodes[nodeid].get_dimmer_level(val)
class ZWave(SmartPlugin): """ Main class of the Plugin. Does all plugin specific stuff and provides the update functions for the items """ PLUGIN_VERSION = '1.4.2' def __init__(self, sh): """ Initalizes the plugin. The parameters describe for this method are pulled from the entry in plugin.conf. :param sh: **Deprecated**: The instance of the smarthome object. For SmartHomeNG versions **beyond** 1.3: **Don't use it**! :param *args: **Deprecated**: Old way of passing parameter values. For SmartHomeNG versions **beyond** 1.3: **Don't use it**! :param **kwargs:**Deprecated**: Old way of passing parameter values. For SmartHomeNG versions **beyond** 1.3: **Don't use it**! If you need the sh object at all, use the method self.get_sh() to get it. There should be almost no need for a reference to the sh object any more. The parameters *args and **kwargs are the old way of passing parameters. They are deprecated. They are implemented to support older plugins. Plugins for SmartHomeNG v1.4 and beyond should use the new way of getting parameter values: use the SmartPlugin method `get_parameter_value(parameter_name)` instead. Anywhere within the Plugin you can get the configured (and checked) value for a parameter by calling `self.get_parameter_value(parameter_name)`. It returns the value in the datatype that is defined in the metadata. """ if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5': self.logger = logging.getLogger(__name__) try: self._sh = sh self.listenOn = {} self._device = self.get_parameter_value('device') self._config_path = self.get_parameter_value('config_path') self._logging = self.get_parameter_value('zlogging') logfile = self.get_parameter_value('loglevel') try: self._logfile = '{}/log/{}'.format(self.get_vardir(), logfile) except Exception: self._logfile = '{}/var/log/{}'.format(self._sh.get_basedir(), logfile) self._loglevel = self.get_parameter_value('loglevel') self._sec_strategy = self.get_parameter_value('sec_strategy') self._ready = False self.logger.debug( 'Initialized: logpath={}, loglevel={}, configpath={}, device={}, sec_strategy={}' .format(self._logfile, self._loglevel, self._config_path, self._device, self._sec_strategy)) except Exception as err: self.logger.error(err) self._init_complete = False return if not REQUIRED_PACKAGE_IMPORTED: self.logger.warning("Unable to import Python package 'openzwave'") self._init_complete = False def run(self): """ Run method for the plugin """ self.logger.debug('run method called') self.alive = True try: options = ZWaveOption(self._device, config_path=self._config_path, user_path='./var/ozw', cmd_line='') except Exception as e: self.logger.error('error on create ZWaveOption - {}'.format(e)) self.alive = False return try: options.set_log_file(self._logfile) options.set_save_log_level(self._loglevel) options.set_logging(self._logging) options.set_append_log_file(False) options.set_console_output(False) options.set_security_strategy(self._sec_strategy) options.lock() except Exception as e: self.logger.error('error on option.set_* - {}'.format(e)) self.logger.debug('run -> create network') try: self._network = ZWaveNetwork(options, autostart=False) except Exception as e: self.logger.error('error on create Network Object - {}'.format(e)) self.logger.debug('run -> connect event handler') try: dispatcher.connect(self.zwave_value_update, ZWaveNetwork.SIGNAL_VALUE_CHANGED) except Exception as e: self.logger.error('error on connect event handler - {}'.format(e)) self.logger.debug('run -> start network') try: self._network.start() except Exception as e: self.alive = False self.logger.error('error on start network - {}'.format(e)) self.logger.info('use openzwave library: {}'.format( self._network.controller.ozw_library_version)) self.logger.info('use python library: {}'.format( self._network.controller.python_library_version)) self.logger.info('use ZWave library: {}'.format( self._network.controller.library_description)) while self.alive: if self._network.state != self._network.STATE_READY: self.logger.debug( 'wait until network is ready... current state is: {}'. format(self._network.state_str)) if self._network.state == self._network.STATE_FAILED: self.alive = False return # Dump network information on STATE_READY if self._network.state == self._network.STATE_READY and self._ready is False: self.logger.info( 'controller ready : {} nodes were found.'.format( self._network.nodes_count)) self.logger.info('controller node id : {}'.format( self._network.controller.node.node_id)) self.logger.info('controller node version : {}'.format( self._network.controller.node.version)) self.logger.info('Network home id : {}'.format( self._network.home_id_str)) self.logger.info('Nodes in network : {}'.format( self._network.nodes_count)) self.logger.info("zwave: Start refresh values") for __id in self.listenOn: __val = self._network.get_value(__id) self.logger.info("zwave: id : '{}', val: '{}'".format( __id, __val)) for __item in self.listenOn[__id][ITEMS]: __item(__val.data, 'ZWave') self._ready = True time.sleep(3.0) def stop(self): """ Stop method for the plugin """ self.logger.debug("zwave: stop method called") self._network.stop() self.alive = False def parse_item(self, item): """ Default plugin parse_item method. Is called when the plugin is initialized. The plugin can, corresponding to its attribute keywords, decide what to do with the item in future, like adding it to an internal array for future reference :param item: The item to process. :return: If the plugin needs to be informed of an items change you should return a call back function like the function update_item down below. An example when this is needed is the knx plugin where parse_item returns the update_item function when the attribute knx_send is found. This means that when the items value is about to be updated, the call back function is called with the item, caller, source and dest as arguments and in case of the knx plugin the value can be sent to the knx with a knx write function within the knx plugin. """ if self.has_iattr(item.conf, 'zwave_node') and self.has_iattr( item.conf, 'zwave_value'): node_id = int( self.get_iattr_value(item.conf, 'zwave_node').strip()) value_id = int( self.get_iattr_value(item.conf, 'zwave_value').strip()) self.logger.debug('connecting item {} to node {} value {}'.format( item, node_id, value_id)) if value_id not in self.listenOn: self.listenOn[value_id] = { NID: node_id, ITEMS: [item], LOGICS: [] } elif item not in self.listenOn[value_id][ITEMS]: self.listenOn[value_id][ITEMS].append(item) return self.update_item def parse_logic(self, logic): """ Default plugin parse_logic method """ if 'xxx' in logic.conf: # self.function(logic['name']) pass def update_item(self, item, caller=None, source=None, dest=None): """ Write items values :param item: item to be updated towards the plugin :param caller: if given it represents the callers name :param source: if given it represents the source :param dest: if given it represents the dest """ if self.has_iattr(item.conf, 'zwave_node') and self.has_iattr( item.conf, 'zwave_value'): self.logger.debug( "zwave: update_item was called with item '{}' from caller '{}', source '{}' and dest '{}'" .format(item, caller, source, dest)) self.logger.debug( "zwave: item value is '{}' from type '{}'".format( item(), type(item()))) try: self._network._manager.setValue(int(item.conf['zwave_value']), item()) except Exception as e: self.logger.error('update_item error - {}'.format(e)) def zwave_value_update(self, network, node, value): """ Dispatcher to Trigger Item Updates :param network: the network object :param node: the node object which is updated :param value: the value object which is updated """ value_id = value.value_id self.logger.debug( 'zwave_value_update called for value_id={} and value={}'.format( value_id, value.data)) self.logger.debug('self.listenOn={}'.format(self.listenOn)) if value_id in self.listenOn: if self.listenOn[value_id][ITEMS] is not None: for item in self.listenOn[value_id][ITEMS]: try: item(value.data, 'ZWave') except Exception as e: self.logger.error( 'zwave_value_update error - {}'.format(e)) else: self.logger.debug('listener found, but no items bound') else: self.logger.debug('no listener defined')
class ZWaveDevice(DeviceLifetimeCycles): @typechecked() def __init__(self, zwave_config: ZwaveCommunicationCfg, root_logger: RootLogger) -> None: self.__zwave_config = zwave_config self.__root_logger = root_logger self.__network = None self.__state_change_callback = None def connect(self) -> None: options = ZWaveOption( self.__zwave_config.port, config_path=self.__zwave_config.openzwave_config_path, user_path=".", cmd_line="") options.set_console_output(False) options.set_save_log_level("None") options.set_logging(False) options.lock() self.__network = ZWaveNetwork(options, autostart=False) dispatcher.connect(self.__network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(self.__network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) dispatcher.connect(self.__value_update, ZWaveNetwork.SIGNAL_VALUE) self.__network.start() def disconnect(self) -> None: self.__root_logger.info('Disconnectiong Zwave device') self.__network.stop() @typechecked() def attach_state_change_callback(self, callback: Callable[[str, Any], None]): self.__state_change_callback = callback @typechecked() def change_switch(self, actuator_name: str, state: bool) -> bool: node, val = self.__get_node(actuator_name, 'switch') try: node.set_switch(val, state) except Exception as e: return False return True @typechecked() def change_dimmer(self, actuator_name: str, state: int) -> bool: try: node, val = self.__get_node(actuator_name, 'dimmer') node.set_dimmer(val, state) except Exception as e: return False return True @typechecked() def __get_node(self, actuator_name: str, type: str): for node in self.__network.nodes: for val in self.__get_device_by_type(self.__network.nodes[node], type): self.__root_logger.info('Zwave node: {0}'.format( self.__network.nodes[node].values[val].id_on_network)) if self.__network.nodes[node].values[ val].id_on_network != actuator_name: continue self.__root_logger.info( 'Changing zwave switch: {0}'.format(actuator_name)) return self.__network.nodes[node], val raise Exception( 'Zwave node with id {0} not found'.format(actuator_name)) @typechecked() def __get_device_by_type(self, node, type: str): if type == 'switch': return node.get_switches() elif type == 'dimmer': return node.get_dimmers() def __network_failed(self, network): self.__root_logger.info('Zwave network failed loading') def __network_ready(self, network): self.__root_logger.info( 'Zwave network ready, contoller name: {0}'.format( network.controller)) def __value_update(self, network, node, value): self.__root_logger.info('Id {0} for value: {1}'.format( value.id_on_network, value.data)) if None is self.__state_change_callback: return self.__state_change_callback(value.id_on_network, value.data)
def louie_value_update(network, node, value): print("Hello from value : {}.".format(value)) #Create a network object network = ZWaveNetwork(options, autostart=False) # We connect to the louie dispatcher dispatcher.connect(louie_network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(louie_network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) # Start the network network.start() # Wait for network print("***** Waiting for network to become ready : ") for i in range(0, 90): if network.state >= network.STATE_READY: print("***** Network is ready") break else: sys.stdout.write(".") sys.stdout.flush() time.sleep(1.0) time.sleep(5.0) ####################################
class Backend(): def __init__(self): ################### instanciation de l'objet backend ######################################################## ###### options needed for python openzwave library like config files path, logging, device = configpi.interface options = ZWaveOption( device, config_path="/home/pi/IoTLab/python-openzwave/openzwave/config", user_path=".", cmd_line="") #options = ZWaveOption(device, config_path="/home/iosis/IoTLab/python-openzwave/openzwave/config", user_path=".", cmd_line="") options.set_log_file("OZW.log") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level('Warning') options.set_logging(True) options.lock() # creation of the object network using the options entity already created self.network = ZWaveNetwork(options, autostart=False) ###### These dispatchers associate a method to a signal. the signals are generated by the library python-openzwave. ###### Once the signal is received. It's associated method is executed (see "_node_added" example below in "_network_started" method) dispatcher.connect(self._network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self._network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) ###### backend object attributes # self.devices = OrderedDict() ### will contain the list of nodes in the network # self.sensors = OrderedDict() ### will contain the list of sensors (only) in the network self.node_added = False self.node_removed = False self.timestamps = { } ### will contain the time of the last values' update for each sensor self.queryStages = { ### the diffrent stages that a node object gets through before being ready "None": 1, # Query process hasn't started for this node "ProtocolInfo": 2, # Retrieve protocol information "Probe": 3, # Ping device to see if alive "WakeUp": 4, # Start wake up process if a sleeping node "ManufacturerSpecific1": 5, # Retrieve manufacturer name and product ids if ProtocolInfo lets us "NodeInfo": 6, # Retrieve info about supported, controlled command classes "SecurityReport": 7, # Retrieve a list of Command Classes that require Security "ManufacturerSpecific2": 8, # Retrieve manufacturer name and product ids "Versions": 9, # Retrieve version information "Instances": 10, # Retrieve information about multiple command class instances "Static": 11, # Retrieve static information (doesn't change) "Probe1": 12, # Ping a device upon starting with configuration "Associations": 13, # Retrieve information about associations "Neighbors": 14, # Retrieve node neighbor list "Session": 15, # Retrieve session information (changes infrequently) "Dynamic": 16, # Retrieve dynamic information (changes frequently) "Configuration": 17, # Retrieve configurable parameter information (only done on request) "Complete": 18 # Query process is completed for this node } ####################################################################################################################### ############# LAUNCH ################################################################################################# ####################################################################################################################### def _network_started(self, network): # executed once the software representation is started. the discovery of the network components has begun. they will be mapped into objects print("network started - %d nodes were found." % network.nodes_count) # these dispatchers associate a method to a signal. the signals are generated by the library python-openzwave. # a signal may contain a number of parameters that are passed to the method associated to the signal. # for exemple, the dispatcher below associates the signal "SIGNAL_NODE_ADDED" to the method "_node_added" that is implemented below (line 111). # the signal "SIGNAL_NODE_ADDED" transports two parameters which are the objects network and node. # once this signal is received, these two parameters will be passed to the method "_node_added" and the method will be executed. dispatcher.connect(self._node_added, ZWaveNetwork.SIGNAL_NODE_ADDED) dispatcher.connect(self._node_removed, ZWaveNetwork.SIGNAL_NODE_REMOVED) def _network_ready(self, network): # executed once the software representation is ready print("network : ready : %d nodes were found." % network.nodes_count) print("network : controller is : %s" % network.controller) dispatcher.connect(self._value_update, ZWaveNetwork.SIGNAL_VALUE) def _node_added(self, network, node): # executed when node is added to the software representation. it's executed after the method "_debug_node_new" (see below) print('node added: %s.' % node.node_id) self.timestamps["timestamp" + str(node.node_id)] = "None" self.node_added = True def _node_removed(self, network, node): # executed when node is removed from the software representation print('node removed: %s.' % node.name) self.node_removed = True def _value_update(self, network, node, value): # executed when a new value from a node is received print('Node %s: value update: %s is %s.' % (node.node_id, value.label, value.data)) self.timestamps["timestamp" + str(node.node_id)] = int(time.time()) ################################################################################################################ ######################## START AND STOP THE SOFTWARE REPRESENTATION ############################################ ################################################################################################################ def start(self): # this method starts the software representation global started if started: print "Already started" return started = True self.network.start() print "Z-Wave Network Starting..." for i in range(0, 300): if self.network.state == self.network.STATE_READY: break else: time.sleep(1.0) if not self.network.is_ready: print "Network is not ready but continue anyway" print "------------------------------------------------------------" print "Nodes in network : %s" % self.network.nodes_count print "------------------------------------------------------------" def stop(self): # this method stops the software representation global started started = False print "Stopping Z-Wave Network... " self.network.stop() def reset(self): if self.network.nodes_count == 1: self.network.controller.hard_reset() return "Hard Reset Done" return "Cannot make Hard Reset while nodes included in network" ######################################################################################################################### ############## YOUR WORK STARTS HERE #################################################################################### ######################################################################################################################### ######################################################################################################################### ####################################################################################################################### ############# NETWORK ################################################################################################# ####################################################################################################################### def network_info(self): #### COMPLETE THIS METHOD ############## return "this method returns a JSON that lists all network nodes and gives some informations about each one of them like the ID, neighbors, ..." ####################################################################################################################### ############# NODES ################################################################################################# ####################################################################################################################### def ordered_nodes_dict(self): # returns an ordered list of the network's nodes sorted by node's id return OrderedDict(sorted(self.network.nodes.items())) def addNode(self): #### COMPLETE THIS METHOD ############## return "this method passes the controller to inclusion mode and gets it out of it after 20 seconds " def removeNode(self): #### COMPLETE THIS METHOD ############## return "this method passes the controller to exclusion mode and gets it out of it after 20 seconds " def get_nodes_list(self): return jsonify(self.network.nodes_to_dict()) def set_node_location(self, n, value): #### COMPLETE THIS METHOD ############## return " this method sets the location of a specific sensor node" def set_node_name(self, n, value): for node in self.network.nodes.itervalues(): if node.node_id == n and isReady(node) and n != 1: node.set_name(value) return jsonify(node_id=node.node_id, name=node.name) return "Node not ready or wrong sensor node !" def get_node_location(self, n): #### COMPLETE THIS METHOD ############## return "this method gets the location of a specific sensor node" def get_node_name(self, n): for node in self.network.nodes.itervalues(): if node.node_id == n and isReady(node) and n != 1: return jsonify(node_id=node.node_id, name=node.name) return "Node not ready or wrong sensor node !" def get_neighbours_list(self, n): nodes = [] for node in self.network.nodes.itervalues(): if node.node_id == n and isReady(node) and n != 1 and len( node.neighbors) > 0: for neighbor_id in node.neighbors: nodes.append(self.network.nodes[neighbor_id].to_dict()) response = dict((k, v) for k, v in enumerate(nodes)) return jsonify(response) def set_node_config_parameter(self, n, param, value, size): #### COMPLETE THIS METHOD ############## return "this method sets a configuration parameter to a given value" def get_node_config_parameter(self, n, param): #### COMPLETE THIS METHOD ############## return "this method gets the value of a configuration parameter" def get_nodes_Configuration(self): configurations = {'Network Home ID': self.network.home_id} for node in self.network.nodes.itervalues(): if isReady(node) and node.node_id != 1: key = "Node_" + str(node.node_id) configurations[key] = {} configurations[key]["Node ID"] = node.node_id values = node.get_values("All", "All", "All", "All", "All") for value in values.itervalues(): if value.label == "Enable Motion Sensor": configurations[key][ "Enable Motion Sensor"] = value.data if value.label == "Group 1 Interval": configurations[key]["Group 1 Interval"] = value.data if value.label == "Group 1 Reports": configurations[key]["Group 1 Reports"] = value.data if value.label == "Group 2 Interval": configurations[key]["Group 2 Interval"] = value.data if value.label == "Group 2 Reports": configurations[key]["Group 2 Reports"] = value.data if value.label == "Group 3 Interval": configurations[key]["Group 3 Interval"] = value.data if value.label == "Group 3 Reports": configurations[key]["Group 3 Reports"] = value.data if value.label == "Wake-up Interval": configurations[key]["Wake-up Interval"] = value.data return jsonify(configurations)
class ZWDeviceController(IOTDeviceController): def louie_network_started(network): print ( "Hello from network : I'm started : homeid %0.8x - %d nodes were found." % (network.home_id, network.nodes_count) ) def louie_network_failed(network): print ("Hello from network : can't load :(.") def louie_network_ready(network): print ("Hello from network : I'm ready : %d nodes were found." % network.nodes_count) print ("Hello from network : my controller is : %s" % network.controller) dispatcher.connect(louie_node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(louie_value_update, ZWaveNetwork.SIGNAL_VALUE) def louie_node_update(network, node): print ("Hello from node : %s." % node) def louie_value_update(network, node, value): print ("Hello from value : %s." % value) def __init__(self, name, location, nodefilename="/opt/seciot/nodenames.json"): IOTDeviceController.__init__(self, name) # Restore node stuff nodefile = open(nodefilename, "r") nodejson = nodefile.read() self.node_dict = json.loads(nodejson) # Init options device = "/dev/ttyACM0" sniff = 300.0 options = ZWaveOption(device, config_path="/opt/openzwave/config", user_path=".", cmd_line="") options.set_logging(False) options.set_console_output(False) options.lock() # Create a network object self.network = ZWaveNetwork(options, autostart=False) self.network.set_poll_interval(10, True) # We connect to the louie dispatcher dispatcher.connect(self.louie_network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self.louie_network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) self.network.start() # We wait for the network. print "***** Waiting for network to become ready : " for i in range(0, 300): if self.network.state >= self.network.STATE_READY: print "***** Network is ready" break else: sys.stdout.write(".") sys.stdout.flush() time.sleep(1.0) # We update the name of the controller self.network.controller.node.name = name self.network.controller.node.location = location def export(self): # list of devices # each device has zwavename, friendly name, device type, statepool, currentstates (timestamped) # return self.network.nodes return self.node_dict.keys() def readState(self, node): print "read", self.node_dict print "read", node if type(node) in [str, unicode]: nodenum = self.node_dict[node] else: print type(node) nodenum = node mynode = self.network.nodes[nodenum] state = None for switch in mynode.get_switches(): state = mynode.get_switch_state(switch) print node print state return state def setState(self, node, state): print "set", self.node_dict print "set", node if type(node) in [str, unicode]: nodenum = self.node_dict[node] else: nodenum = node if state in [1, "1", u"1", True]: boolstate = True elif state in [0, "0", u"0", False]: boolstate = False else: print state print type(state) raise error("bad") print state for switch in self.network.nodes[nodenum].get_switches(): self.network.nodes[nodenum].set_switch(switch, boolstate)
class Backend(): """Root parent backend class """ CONTROLLER_NODE_ID = 1 logger = None network = None _labels_xref = { # us => OZW 'battery': 'Battery Level', 'burglar': 'Burglar', 'humidity': 'Relative Humidity', 'level': 'Level', 'luminance': 'Luminance', 'motion': 'Sensor', 'temperature': 'Temperature', 'ultraviolet': 'Ultraviolet', } _rooms_location = { # roomId => roomLocation '31126': 'room1', '19547': 'room2' } _initials = { # all optional... These are used asr args for __init__() # public 'device': configpi.device, 'ozw_config_path': configpi.config_path, 'ozw_user_path': configpi.user_path, 'log_level': configpi.log_level, 'log_format': configpi.log_format, 'log_format_dbg': configpi.log_format_dbg, 're_dimmer': configpi.re_dimmer, 're_sensor': configpi.re_sensor, 'controller_name': configpi.name, 'network_ready_timeout': configpi.network_ready_timeout, 'controller_operation_timeout': configpi.controller_operation_timeout, } _initialized = False def __init__(self, **kwargs): """Attrs initialized here have names as in :dict:`_initials` Doctests ++++++++ >>> t.start() (True, 'OK') # >>> t.hard_reset(force=True) # (True, 'OK') # >>> print("*** Action needed for node to be _added_ ***") # doctest:+ELLIPSIS # *** ... # >>> t.add_node() # doctest:+ELLIPSIS # {...} """ if self._initialized: raise RuntimeErr("[Bug] backend already initialized!?") self._initialized = True # set defaults for attr in self._initials.keys(): setattr(self, attr, self._initials[attr]) # ...remainder. for k in kwargs.keys(): if not hasattr(self, k): raise AttributeError( "{}: no such attribute in definition of class {}.".format( k, self.__class__.__name__)) else: setattr(self, k, kwargs[k]) # we put all artifacts here user_path = os.path.expanduser(os.path.expandvars(self.ozw_user_path)) try: os.makedirs(self.ozw_user_path, exist_ok=True) except Exception as e: raise RuntimeError("Can't create user_path: {}".format(e)) self.logger = logging.getLogger(__name__) self.logger.setLevel(self.log_level) fh = logging.FileHandler("{}/{}.log".format(self.ozw_user_path, __name__), mode='w') fh.setLevel(self.log_level) fh.setFormatter( logging.Formatter(self.log_format_dbg if self.log_level <= logging. DEBUG else self.log_format)) self.logger.addHandler(fh) self.logger.debug('initializing OZW backend...') options = ZWaveOption(self.device, config_path=self.ozw_config_path, user_path=self.ozw_user_path) options.set_log_file('OZW.log') options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level( 'Debug' if self.log_level <= logging.DEBUG else 'Warning') options.set_logging(True) options.lock() self.network = ZWaveNetwork(options, autostart=False) # A dispatcher associates a callback method to a signal. Signals # are generated by the library python-openzwave. Once a signal is # received, its associated callback is executed (see "_node_added" # example below in "_network_started" method) dispatcher.connect(self._network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self._network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) # Yep! It's really 'RESETTED', wanna file a bug for bad english usage? ;-) dispatcher.connect(self._network_reset, ZWaveNetwork.SIGNAL_NETWORK_RESETTED) # dynamically set on add/remove events. See notification handlers below. self.node_added = None self.node_removed = None self.timestamps = { } # times of the last values' update for each sensor # The different stages that a node object gets through before being # ready. [BUG] Why do we need to explicitly list them? Anyway to get # them from the lib? self.queryStages = { "None": 1, # Query process hasn't started for this node "ProtocolInfo": 2, # Retrieve protocol information "Probe": 3, # Ping node to see if alive "WakeUp": 4, # Start wake up process if a sleeping node "ManufacturerSpecific1": 5, # Retrieve manufacturer name and product ids if ProtocolInfo lets us "NodeInfo": 6, # Retrieve info about supported, controlled command classes "NodePlusInfo": 7, # Retrieve Z-Wave+ info and update device classes (Added by Edin et Daniel) "SecurityReport": 8, # Retrieve a list of Command Classes that require Security "ManufacturerSpecific2": 9, # Retrieve manufacturer name and product ids "Versions": 10, # Retrieve version information "Instances": 11, # Retrieve information about multiple command class instances "Static": 12, # Retrieve static information (doesn't change) "Probe1": 13, # Ping a node upon starting with configuration "Associations": 14, # Retrieve information about associations "Neighbors": 15, # Retrieve node neighbor list "Session": 16, # Retrieve session information (changes infrequently) "Dynamic": 17, # Retrieve dynamic information (changes frequently) "Configuration": 18, # Retrieve configurable parameter information (only done on request) "Complete": 19 # Query process is completed for this node } def _is_network_started(self): """Check if self.network is started. See <http://openzwave.github.io/python-openzwave/network.html?highlight=state#openzwave.network.ZWaveNetwork.state> :returns: bool """ return self.network.state >= self.network.STATE_STARTED def _lookup_node(self, nid): """Look up a node in `self.network.nodes` by its ID. :param int nid: the wanted node's ID :returns: object: a :class:`openzwave.node` or None if the wanted node is not found """ self.logger.debug("nodes: {}".format(self.network.nodes)) return next( # index i is discarded (node for i, node in self.network.nodes.items() if node.node_id == nid), None # default ) def _lookup_sensor_node(self, nid): """Look up a sensor node in `self.network.nodes` by its ID. :param int nid: the wanted node's ID :returns: object: a :class:`openzwave.node` or None if the wanted node is not found :raises: RuntimeError: if the node is not {found | ready | sensor} """ node = self._lookup_node(nid) if not node: raise RuntimeError("No such node") if not (node.is_ready or self._has_timestamp(node)): raise RuntimeError("Not ready") if not self._is_sensor(node): raise RuntimeError("Not a sensor") return node def _lookup_dimmer_node(self, nid): """Look up a dimmer node in `self.network.nodes` by its ID. :param int nid: the wanted node's ID :returns: object: a :class:`openzwave.node` or None if the wanted node is not found :raises: RuntimeError: if the node is not {found | ready | dimmer} """ node = self._lookup_node(nid) if not node: raise RuntimeError("No such node") if not (node.is_ready or self._has_timestamp(node)): raise RuntimeError("Not ready") if not self._is_dimmer(node): raise RuntimeError("Not a dimmer") return node def _network_reset(self, network): """Callback executed when the controller is hard reset. :returns: None """ self.logger.info( "Network hard reset: home ID {:08x}, {} nodes found".format( network.home_id, network.nodes_count)) def _network_started(self, network): """Callback executed once the OZW network is _started_ (SIGNAL_NETWORK_STARTED is raised). The discovery of the network components has begun: they will be mapped into objects. WARNING! No guarantee of atomic execution. Avoid ANY I/O here (I'm looking at you, logger), unless it's the _last_ statement executed... :returns: None """ self.logger.info( "Network started: home ID {:08x}, {} nodes found".format( network.home_id, network.nodes_count)) def _network_ready(self, network): """Callback executed once the OZW network is ready for opertion (SIGNAL_NETWORK_READY is raised). WARNING! No guarantee of atomic execution. Avoid ANY I/O here (I'm looking at you, logger), unless it's the _last_ statement executed... :returns: None """ dispatcher.connect(self._node_added, ZWaveNetwork.SIGNAL_NODE_ADDED) dispatcher.connect(self._node_removed, ZWaveNetwork.SIGNAL_NODE_REMOVED) dispatcher.connect(self._value_update, ZWaveNetwork.SIGNAL_VALUE) def _node_added(self, network, node): """Callback executed when a node is added to the network (signal SIGNAL_NODE_ADDED is raised). On execution, `self.node_added` is set to the newly added node, an :class:`openzwave.node` obect. WARNING! No guarantee of atomic execution. Avoid ANY I/O here (I'm looking at you, logger), unless it's the _last_ statement executed... :returns: None """ self.node_added = node self._set_node_timestamp(node, None) self.logger.info('node added: {}'.format(node.node_id)) def _node_removed(self, network, node): """Callback executed when node is removed from the network (signal SIGNAL_NODE_REMOVED is raised). On execution, `self.node_removed` is set with the removed node, an :class:`openzwave.node` obect. WARNING! No guarantee of atomic execution. Avoid ANY I/O here (I'm looking at you, logger), unless it's the _last_ statement executed... :returns: None """ self.node_removed = node self.timestamps.pop(_tstamp_label(node), None) self._del_node_timestamp(node) self.logger.info('node removed: {}'.format(node.node_id)) def _value_update(self, network, node, value): """Callback executed whenever a node's reading value is changed, added, removed, etc. Node's timestamp is also updated. WARNING! No guarantee of atomic execution. Avoid ANY I/O here (I'm looking at you, logger), unless it's the _last_ statement executed... :returns: None """ self._set_node_timestamp(node, int(time.time())) self.logger.debug("timestamp: {}".format( self.get_node_timestamp(node))) ############################################################################ # @network ############################################################################ def start(self): """Start the software representation. It won't restart an already started network -- use `reset()` instead. :returns: tuple(bool, string): (status, reason) where: status: True on success, False if the network was already start reason: a textual explanation """ global started if started: msg = "System already started. Skipping..." self.logger.warning(msg) return (False, msg) self.network.start() self.logger.info( "Z-Wave Network Starting -- timeout in {}s. Please wait...".format( self.network_ready_timeout)) # [BUG] why this f***in polling? Must file an RFE for a callback-based # notification timeout = True for i in range(0, self.network_ready_timeout): if self.network.is_ready: self.logger.debug("Network ready after {}s".format(i)) timeout = False break else: time.sleep(1.0) if not self.network.is_ready: self.logger.warning(( "Network is not ready after {}s. " + "You should increase `network_ready_timeout`. Continuing anyway..." ).format(self.network_ready_timeout)) self.logger.info("Network _{}_ ready. Nodes discovered: {}".format( 'possibly' if timeout else 'really', self.network.nodes_count, )) started = True return (True, 'OK') def stop(self): """Stop the software representation :returns: tuple(bool, string): (status, reason) where: status: True on success, False otherwise reason: a textual explanation Doctests ++++++++ # >>> print("*** Action needed for node to be _removed_ ***") # doctest:+ELLIPSIS # *** ... # >>> t.remove_node() # doctest:+ELLIPSIS # {...} """ global started self.logger.info("Z-Wave Network stopping...") try: self.network.stop() except Exception as e: return (False, str(e)) started = False return (True, 'OK') def hard_reset(self, force=False): """Resets the controller and erases its network configuration settings. The controller becomes a primary controller ready to add nodes to a new network. Warning! This basically destroys the network -- use with care! :returns: tuple(bool, string): (status, reason) where: :raises: RuntimeError exception if network is not empty (nodes are included) while `force=False` """ if self.network.nodes_count == 1: self.network.controller.hard_reset() return (True, 'OK') elif force: self.logger.warning( "Forcing hard reset on a network with included nodes.") self.network.controller.hard_reset() return (True, 'OK') else: raise RuntimeError( "Cannot hard reset while network has included nodes.") def soft_reset(self): """Soft reset the controller. The software representation is untouched. :returns: tuple(bool, string): (status, reason) where: """ try: self.network.controller.soft_reset() except Exception as e: return (False, str(e)) return (True, 'OK') def network_info(self): """Get network's structure information summary. :returns: dict: with various info about the network and currently associated nodes """ result = { 'Network Home ID': self.network.home_id_str, } nodes = self.network.nodes for k, node in nodes.items(): percentage = round((self.queryStages[node.query_stage] / 19) * 100, 2) node = { k: { "Is Ready": node.is_ready, "Neighbours": self.get_neighbours_list(k), "Node ID": k, "Node location": node.location, "Node name": node.name, "Node type": node.product_type, "Product name": node.product_name, "Query Stage": node.query_stage, "Query Stage (%)": percentage } } result.update(node) return result def get_nodes_configuration(self): """Get an overview of the network and its nodes' configuration parameters (ID, Wake-up Interval, Group 1 Reports, Group 1 Interval, ...). :returns: dict: the nodes's configuration parameters """ result = {'Network Home ID': self.network.home_id_str} self.logger.debug("looking for nodes...") for node in self._my_nodes().values(): if node.is_ready and not self._is_controller(node): # Update of the software representation: retreive the last # status of the Z-Wave network node.request_all_config_params() # Get Config + System values values = node.get_values(class_id="All", genre="Config", readonly="All", writeonly=False, label="All") # de-obectify for json serialization. `values` is something like: # int(ID): { # 'label': str, # 'value_id': int(ID), # same as master key # 'node_id': int, # 'units': str, # 'genre': str, # 'data': str, # 'data_items': set(...), # this is not jsonify-able! # 'command_class': int, # 'is_read_only': bool, # 'is_write_only': bool, # 'type': str, # 'index': int # } # which must be inspected for deep serialization nodeValues = { clsid: json_prepare(data) for clsid, data in values.items() } nodeValues['Node type'] = str(node.type) result[node.node_id] = nodeValues return result def get_nodes_list(self): """Get a list of all the nodes in the network, where indexes are node IDs and values are product names. :returns: object: an `OrderedDict` indexed by node IDs with product names (or a "[not ready]" note) as values: { "1": "Z-Stick Gen5", "2": "MultiSensor 6", "3": "ZE27", "4": "[not ready]" } """ nodes = self.network.nodes values = OrderedDict() for k, node in nodes.items(): values[k] = node.name return values def get_sensors_list(self): """Get a list of sensor nodes in the network, where indexes are node IDs and values are product names. :returns: object: an `OrderedDict` indexed by node IDs with product names (or a "[not ready]" note) as values: { "2": "MultiSensor 6", "3": "MultiSensor 6" } """ nodes = self.network.nodes values = OrderedDict() for k, node in nodes.items(): if self._is_sensor(node): values[k] = node.name return values def get_dimmers_list(self): """Get a list of dimmer nodes in the network, where indexes are node IDs and values are product names. :returns: object: an `OrderedDict` indexed by node IDs with product names (or a "[not ready]" note) as values: { "2": "???", "3": "" } """ nodes = self.network.nodes values = OrderedDict() for k, node in nodes.items(): if self._is_dimmer(node): values[k] = node.name return values def set_dimmers_by_room(self, roomId, value): room = self._rooms_location[roomId] nodes = self.network.nodes for k, node in nodes.items(): if self._is_dimmer(node) and node.location == room: self.set_dimmer_level(node.node_id, value) return True ############################################################################ # @nodes ############################################################################ def _my_nodes(self): """Returns an ordered list of the all network's nodes sorted by node's ID. :returns: object: an :class:`OrderedDict` """ return OrderedDict(sorted(self.network.nodes.items())) def _is_controller(self, node): """Check if node is a controller. :param object node: a :class:`openzwave.node` :returns: bool """ return node.node_id == self.CONTROLLER_NODE_ID def _is_dimmer(self, node): """Check if node is a dimmer by matching its type against :attr:`self.re_dimmer`. :param object node: a :class:`openzwave.node` :returns: bool """ return re.search(self.re_dimmer, node.type, re.I) def _is_sensor(self, node): """Check if node is a sensor by matching its type against :attr:`self.re_sensor`. :param object node: a :class:`openzwave.node` :returns: bool """ return re.search(self.re_sensor, node.type, re.I) @staticmethod def _lookup_value(values, label): """Look up a (node's) value by label in a list of values. :param string label: the wanted value's label :param values set: a value set as returned by :func:`node.get_values()` :returns: depends on the value's type or None if the wanted value is not found """ return next( (value.date for value in values if value.label == label), None # default ) def _has_timestamp(self, node): """Check if a node has a timestamp, meaning that it should be ready and has received a first value update. :param object node: a :class:`openzwave.node` :returns: bool: True if an entry exists in `self.timestamps` """ self.logger.debug("timestamps: {}".format(self.timestamps)) return _tstamp_label(node) in self.timestamps def _get_node_timestamp(self, node): """Get the last update time of a node. :param object node: a :class:`openzwave.node` :returns: int: time as seconds-since-th-epoch ([FIX-ME] to be verfied) or None if no timestamp exists for `node` """ try: return self.timestamps[_tstamp_label(node)] except KeyError: return None def _set_node_timestamp(self, node, value): """Set the last update time of a node. :param object node: a :class:`openzwave.node` :param int value: time as seconds-since-the-epoch :returns: None """ self.timestamps[_tstamp_label(node)] = value def _del_node_timestamp(self, node): """Remove the last update time of a node. :param object node: a :class:`openzwave.node` :returns: int: time as seconds-since-th-epoch ([FIX-ME] to be verfied) or None if no timestamp exists for `node` """ return self.timestamps.pop(_tstamp_label(node), None) def add_node(self): # source pour la boucle de 20 secondes : https://stackoverflow.com/questions/24374620/python-loop-to-run-for-certain-amount-of-seconds/24374857 """Adds a node to the network by switching the controller into inclusion mode for 20 seconds. The node to add can not be a controller. Physical action is required on the node to be added. :returns: object: the added node's :class:`openzwave.node` object :raises: RuntimeError exception if * timeout occurs, or * network is not started """ if (started == False): raise RuntimeError("Network is not started") self.network.controller.add_node() t_end = time.time() + 20 while time.time() < t_end: if self.node_added != None: return self.node_added.to_dict() raise RuntimeError("Timeout") def remove_node(self): """Removes a node from the network by switching the controller into exclusion mode for 20 seconds. The node to remove can not be a controller. Physical action is required on the node to be removed. :returns: object: the removed node's :class:`openzwave.node` object :raises: RuntimeError exception if * timeout occurs, or * network is not started """ self.network.controller.remove_node() t_end = time.time() + 20 while time.time() < t_end: if self.node_removed != None: return self.node_removed.to_dict() def set_node_location(self, n, value): """Set a node's location. :param int n: the node's ID :param str value: the new location value :returns: str: The previous location value :raises: RuntimeError: if the node is not found """ node = self._lookup_node(n) if not node: raise RuntimeError("No such node") prev_loc = node.location node.location = value return prev_loc def set_node_name(self, n, value): """Set a node's name. :param int n: the node's ID :param str value: the new name value :returns: str: The previous name value :raises: RuntimeError: if the node is not found """ node = self._lookup_node(n) if not node: raise RuntimeError("No such node") prev_name = node.name node.name = value return prev_name def get_node_location(self, n): """Get a node's location. :param int n: the node's ID :returns: str: the location value on succes :raises: RuntimeError: if the node is not found """ node = self._lookup_node(n) if not node: raise RuntimeError("No such node") return node.location def get_node_name(self, n): """Get a node's name. :param int n: the node's ID :returns: int: the name value on succes :raises: RuntimeError: if the node is not found """ node = self._lookup_node(n) if (node == None): raise RuntimeError("Node not found") return node.name def get_neighbours_list(self, n): """Get a node's llist of neighbors. :param int n: the node's ID :returns: tuple: the neighbors' numerical ID list (might be empty) :raises: RuntimeError: if the node is not found """ node = self._lookup_node(n) if (node == None): raise RuntimeError("Node not found") value = [v for v in node.neighbors] return value def set_node_parameter(self, n, pindex, value, size): """Sets a node's configuration parameter. There's no guarantee that the parameter has been set -- you may check with `get_node_parameter()`. :param int n: the node's ID :param int pindex: the parameter's index :param int value: the parameter's value :param int value: the parameter's size :returns: bool: True on success. False if the command wan't sent for some reason (see OZW log) :raises: RuntimeError: if the node is not found """ node = self._lookup_node(n) if node.is_ready: node.request_all_config_params() node.set_config_param(pindex, value, size) return True return False def get_node_parameter(self, n, pindex): """Get a node's configuration parameter. :param int n: the node's ID :param int pindex: the parameter's index :returns: int: the parameter value on succes, or None if the parameters is not found :raises: RuntimeError: if the node is not {found | ready} """ node = self._lookup_node(n) if (node == None): raise RuntimeError("Node not found") if (node.is_ready == False): raise RuntimeError("Node not ready") if node.is_ready: node.request_all_config_params() values = node.get_values(class_id="All", genre="Config", type="All", readonly="All", writeonly="All", index=pindex, label="All") value = [v.data for v in values.values()][0] return value
class zwave(object): #Define some manager options options = ZWaveOption(device, \ config_path=constants.zwpath, \ user_path=".", cmd_line="") options.set_log_file("OZW_Log.log") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level(log) options.set_logging(True) options.lock() def louie_network_started(self, network): dicti = {'Log':'Z-Wave Network started'} # mySocket.sendto(str(dicti) ,(constants.server1,constants.broadPort)) mqtt_publish.mqtt_pub('Inputs/Satellite/' + constants.name + '/Log',dicti) print("Hello from network : I'm started : homeid {:08x} - {} nodes were found.".format(network.home_id, network.nodes_count)) def louie_network_failed(self, network): pass print("Hello from network : can't load :(.") def louie_network_ready(self, network): dicti = {'Log':'Z-Wave Network up running'} # mySocket.sendto(str(dicti) ,(constants.server1,constants.broadPort)) mqtt_publish.mqtt_pub('Inputs/Satellite/' + constants.name + '/Log',dicti) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.louie_scene_message, ZWaveNetwork.SIGNAL_NODE_EVENT) dispatcher.connect(self.louie_value_update, ZWaveNetwork.SIGNAL_VALUE) print('Dispatcher connected') def louie_scene_message(self, *args, **kwargs): print('scene happening') for count, thing in enumerate(args): print( '{0}. {1}'.format(count, thing)) for name, value in kwargs.items(): print( '{0} = {1}'.format(name, value)) def loui_ess_q_comp(self): pass # print('nodes ess queried') def loui_mess_comp(self): print('mess complete') def louie_node_update(self, network, node): pass print("Hello from node : {}.".format(node)) def louie_value_update(self, network, node, value): print "value changed" try: # print zw_config.inputs[node.home_id][value.value_id], int(value.data) dicti = {'Value': str(int(value.data))} dicti['Name'] = 'ZWave.' + str(node.home_id) + '.' + str(value.value_id) #print dicti # mySocket.sendto(str(dicti) ,(constants.server1,constants.broadPort)) mqtt_publish.mqtt_pub('Inputs/Satellite/' + constants.name + '/ZWave/' + str(int(node.home_id)) +'/'+ str(int(value.value_id)) ,dicti) except: print 'not understood', node, value.value_id, value.data # print("Hello from value : {}.".format( value )) # home_id: [0xeefad666] id: [72057594093060096] parent_id: [3] label: [Switch] data: [False]. # home_id: [0xeefad666] id: [72057594093273218] parent_id: [3] label: [Power] data: [0.0]. # home_id: [0xeefad666] id: [144115188131201026] parent_id: [3] label: [Energy] data: [0.00999999977648] # value.label = switch def __init__(self): pass def start(self): #Create a network object self.network = ZWaveNetwork(self.options, autostart=False) #We connect to the louie dispatcher dispatcher.connect(self.louie_network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self.louie_network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(self.louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) dispatcher.connect(self.louie_scene_message, ZWaveNetwork.SIGNAL_SCENE_EVENT) dispatcher.connect(self.loui_ess_q_comp, ZWaveNetwork.SIGNAL_ESSENTIAL_NODE_QUERIES_COMPLETE) dispatcher.connect(self.loui_mess_comp, ZWaveNetwork.SIGNAL_MSG_COMPLETE) self.network.start() #We wait for the network. # print("Waiting for network to become ready : ") for i in range(0,900): if self.network.state>=self.network.STATE_READY: print "Network is ready" break else: sys.stdout.write(".") sys.stdout.flush() time.sleep(1.0) #time.sleep(5.0) #We update the name of the controller #print("Update controller name") #network.controller.node.name = "Hello name" #time.sleep(5.0) #We update the location of the controller #print("Update controller location") #network.controller.node.location = "Hello location" # for node in self.network.nodes: # for val in self.network.nodes[node].get_switches() : # print("Switch : {}".format(self.network.nodes[node])) # print("Switch1: {}".format(val)) # 72057594093060096 # 144115188130988032 # network.nodes[node].set_switch(val,True) #We only activate the first switch #exit # for node in self.network.nodes: # for val in self.network.nodes[node].get_dimmers() : # print("Dimmer : {}".format(self.network.nodes[node])) # print("Switch1: {}".format(val)) # 72057594093076481 # 144115188131004513 # 144115188131004417 # 72057594093076577 # network.nodes[node].set_dimmer(val,80) #We only activate the first dimmer #exit def end_network(self): self.network.stop() def _set_switch(self,node_id , switch, wert): if wert == 'Toggle': cur_val = self.network.nodes[node_id].get_switch_state(switch) self.network.nodes[node_id].set_switch(switch, not cur_val) else: if eval(wert) > 0: self.network.nodes[node_id].set_switch(switch, True) else: self.network.nodes[node_id].set_switch(switch, bool(eval(wert))) return True def _set_dimmer(self,node_id , dimmer, wert): if wert == 'Toggle': cur_val = self.network.nodes[node_id].get_dimmer_level(dimmer) if cur_val == 0: self.network.nodes[node_id].set_dimmer(dimmer, 50) else: self.network.nodes[node_id].set_dimmer(dimmer, 0) else: self.network.nodes[node_id].set_dimmer(dimmer, eval(wert)) return True def set_device(self, data_ev): # TODO do threaded with stop criteria if data_ev.get('Device') in zw_config.switches: print data_ev return self._set_switch(zw_config.switches[data_ev['Device']][0],zw_config.switches[data_ev['Device']][1],data_ev['Value']) if data_ev.get('Device') in zw_config.dimmer: print data_ev return self._set_dimmer(zw_config.dimmer[data_ev['Device']][0],zw_config.dimmer[data_ev['Device']][1],data_ev['Value'])
class Open_zwave(HAInterface): VERSION = '0.0.4' def louie_network_ready(self, network): self._logger.info( ">>>>>>> Hello from network : I'm ready : %d nodes were found.". format(self._network.nodes_count)) self._logger.info( ">>>>>>> Hello from network : my controller is : {}".format( self._network.controller)) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.louie_value_update, ZWaveNetwork.SIGNAL_VALUE) def louie_node_update(self, network, node): self._logger.debug('>>>>>>> Hello from node : {}.'.format(node)) def louie_value_update(self, network, node, value): self._logger.debug('>>>>>>> Hello from value : {}'.format(value)) for lockvalue in self.get_door_locks(node.node_id).values(): if lockvalue.value_id == value.value_id: if value.data: self._onCommand(address=str(node.node_id), command=Command.LOCK) else: self._onCommand(address=str(node.node_id), command=Command.UNLOCK) for val in self._network.nodes[node.node_id].get_switches(): if val == value.value_id: if value.data: self._onCommand(address=str(node.node_id), command=Command.ON) else: self._onCommand(address=str(node.node_id), command=Command.OFF) for val in self._network.nodes[node.node_id].get_dimmers(): if val == value.value_id: #Poll dimmer to ensure ramp up/down completes level = value.data if self.dimmer_polled_value.has_key(val): self._logger.debug( '>>>>>>> Hello from level : {} {}'.format( level, self.dimmer_polled_value[val])) if level == self.dimmer_polled_value[val]: del self.dimmer_polled_value[val] if level < 2: self._onCommand(address=str(node.node_id), command=Command.OFF) elif level > 98: self._onCommand(address=str(node.node_id), command=Command.ON) else: self._onCommand(address=str(node.node_id), command=(Command.LEVEL, level)) else: self.dimmer_polled_value[val] = level time.sleep(1) value.refresh() else: time.sleep(1) self.dimmer_polled_value[val] = level value.refresh() def __init__(self, *args, **kwargs): self._serialDevicePath = kwargs.get('serialDevicePath', None) self._configpath = kwargs.get('config_path', "/etc/openzwave/") super(Open_zwave, self).__init__(self, *args, **kwargs) self.dimmer_polled_value = {} def _init(self, *args, **kwargs): self.awake = False self.ready = False self.nodesdisplayed = False self._options = ZWaveOption(self._serialDevicePath, \ config_path=self._configpath, \ user_path=".", cmd_line="") self._options.set_log_file("OZW_Log.log") self._options.set_append_log_file(False) self._options.set_console_output(False) #self._options.set_save_log_level(log) self._options.set_save_log_level('Info') self._options.set_logging(True) self._options.set_notify_transactions(True) self._options.lock() self._network = ZWaveNetwork(self._options, log=None, autostart=False) dispatcher.connect(self.louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) self._network.start() super(Open_zwave, self)._init(self, *args, **kwargs) def _printNetwork(self, node): node = self._network.nodes[node] self._logger.info( "------------------------------------------------------") self._logger.info("{} - Name : {}".format(node.node_id, node.name)) self._logger.info("{} - Manufacturer name / id : {} / {}".format( node.node_id, node.manufacturer_name, node.manufacturer_id)) self._logger.info( "{} - Product name / id / type : {} / {} / {}".format( node.node_id, node.product_name, node.product_id, node.product_type)) self._logger.info("{} - Version : {}".format(node.node_id, node.version)) self._logger.info("{} - Command classes : {}".format( node.node_id, node.command_classes_as_string)) self._logger.info("{} - Capabilities : {}".format( node.node_id, node.capabilities)) self._logger.info("{} - Neighbors : {}".format(node.node_id, node.neighbors)) self._logger.info("{} - Can sleep : {}".format(node.node_id, node.can_wake_up())) for value in self.get_door_locks(node.node_id, 'All').values(): self._logger.debug("{} - {} : {}".format(node.node_id, value.label, value.data)) def _readInterface(self, lastPacketHash): if (self._network.state >= self._network.STATE_AWAKED and not self.awake): self.awake = True self._logger.info("Network Awaked") if (self._network.state >= self._network.STATE_READY and not self.ready): self.ready = True self._logger.info("Network Ready") if not self.awake: self._logger.debug("Not awaked") elif self.awake and not self.ready: self._logger.debug("Not ready") elif not self.nodesdisplayed and self.ready: for node in self._network.nodes: self._printNetwork(node) self.update_status() self.nodesdisplayed = True time.sleep(1) def version(self): self._logger.info("Open_zwave Pytomation Driver version " + self.VERSION) self._logger.info("Use openzwave library : {}".format( self._network.controller.ozw_library_version)) self._logger.info("Use python library : {}".format( self._network.controller.python_library_version)) self._logger.info("Use ZWave library : {}".format( self._network.controller.library_description)) def get_door_locks(self, node, datatype='Bool'): return self._network.nodes[node].get_values(class_id=0x62, genre='User', \ type=datatype, readonly=False, writeonly=False) def lock(self, address): node = int(address) for value in self.get_door_locks(node).values(): self._logger.debug("Lock") value.data = True def unlock(self, address): node = int(address) for value in self.get_door_locks(node).values(): self._logger.debug("Unlock") value.data = False def on(self, address): node = int(address) for val in self._network.nodes[node].get_switches(): self._logger.debug("Activate switch") self._network.nodes[node].set_switch(val, True) for val in self._network.nodes[node].get_dimmers(): self._logger.debug("Activate dimmer : {}".format( self._network.nodes[node])) self._network.nodes[node].set_dimmer(val, 99) def off(self, address): node = int(address) for val in self._network.nodes[node].get_switches(): self._logger.debug("Deactivate switch") self._network.nodes[node].set_switch(val, False) for val in self._network.nodes[node].get_dimmers(): self._logger.debug("Deactivate dimmer : {}".format( self._network.nodes[node])) self._network.nodes[node].set_dimmer(val, 0) def level(self, address, level): node = int(address) for val in self._network.nodes[node].get_dimmers(): self._logger.debug("Set dimmer : {}".format( self._network.nodes[node])) self._network.nodes[node].set_dimmer(val, level) def status(self, address): node = int(address) for val in self._network.nodes[node].get_switches(): level = self._network.nodes[node].get_switch_state(val) if level: self._onState(address=address, state=State.ON) else: self._onState(address=address, state=State.OFF) for val in self._network.nodes[node].get_dimmers(): level = self._network.nodes[node].get_dimmer_level(val) if level < 2: self._onState(address=address, state=State.OFF) elif level > 98: self._onState(address=address, state=State.ON) else: self._onState(address=address, state=(State.LEVEL, level)) for value in self.get_door_locks(node).values(): if value.data: self._onState(address=address, state=State.LOCKED) else: self._onState(address=address, state=State.UNLOCKED) def update_status(self): for d in self._devices: self.status(d.address)
class ZwaveNetworkController(): """ This class is reponsible for starting and communicating with a zwave controller. """ def __init__(self): self.is_running = False def start(self, controllerDevice, config_path="./config", user_path=".", cmd_line=""): self.options = ZWaveOption(controllerDevice, config_path, user_path, cmd_line) self.options.set_log_file("OZW_Log.log") self.options.set_append_log_file(False) self.options.set_console_output(False) self.options.set_save_log_level("None") self.options.set_logging(True) self.options.lock() dispatcher.connect(self.zwave_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) dispatcher.connect(self.zwave_network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self.zwave_network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(self.zwave_node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.zwave_value_update, ZWaveNetwork.SIGNAL_VALUE) self.network = ZWaveNetwork(self.options, autostart=False) self.network.start() #self.is_running is set to True on zwave_network_ready() def stop(self): self.network.stop() self.is_running = False def send_command(self, command): # SWITCH-SET|node-id|val-id||True or False print('send_command | {}'.format(command)) if self.network.is_ready == True: fields = command.split("|") cCommand = fields[0] cNodeId = int(fields[1]) cValueId = int(fields[2]) cVal = True if fields[3] == 'True' else False if cCommand == 'SWITCH-SET': print('switching to {}'.format(cVal)) self.network.nodes[cNodeId].set_switch(cValueId, cVal) else: print("send_command : failed. unsupported command {}".format( cCommand)) else: print("send_command : failed. network not ready.") def zwave_network_started(self, network): print("network started : homeid {:08x} - {} nodes were found.".format( network.home_id, network.nodes_count)) def zwave_network_failed(self, network): print("network : loading failed.") def zwave_network_ready(self, network): print("network : ready : {} nodes were found.".format( self.network.nodes_count)) print("network : my controller is : {}".format( self.network.controller)) self.network.controller.node.name = "HomeSweetHome" self.network.controller.node.location = "Room1" self.is_running = True print('Switches are') for node in self.network.nodes: for val in self.network.nodes[node].get_switches(): zNode = self.network.nodes[node] netReadyEvent = Event() netReadyEvent.timestamp = time.time() netReadyEvent.source = 'zw_net' netReadyEvent.destination = 'aws_iot' netReadyEvent.payload = "NET-READY|{}|{}|{}".format( zNode.node_id, val, zNode.get_switch_state(val)) print('to: {}, payload = {}'.format(netReadyEvent.source, netReadyEvent.payload)) main_queue.put(netReadyEvent) def zwave_node_update(self, network, node): print("Hello from node : {}.".format(node)) def zwave_value_update(self, network, node, value): print("Hello from value : {}.".format(value)) @property def is_running(self): return self._is_running @is_running.setter def is_running(self, value): self._is_running = value
class ZWave: def __init__(self, error_handler, sig_handler): self.ctrl = [] self.net = None self.home_id = None self.uid2node_val = {} self.raw_opts = reqparse.Namespace() self.opts = None self.opts_locked = False self.err_handler = error_handler #self.node_cache = {} for sig, handler in net_signals: if handler is None: dispatcher.connect(sig_handler, signal=sig, sender=dispatcher.Any) def get_node(self, node_id, silentfail=False): if not self.net: self.err_handler(414) #out = None #if not node_id in self.node_cache: # out = ZWaveCoreNode(self.net.nodes.get(node_id)) #self.node_cache[node_id] = out #if out is None: # raise Exception("shit") #print (f"soifjoidsojidf: {node_id}") inner = self.net.nodes.get(node_id) out = ZWaveCoreNode(inner) return out def get_main_ctrl(self): return None if len(self.ctrl) == 0 else self.ctrl[0] def __contains__(self, node_id): return node_id in self.net.nodes def __getitem__(self, node_id): return self.get_node(node_id) def get_node_details(self, node_id, fields=None, silentfail=False): if not self.net: self.err_handler(414) node = self.get_node(node_id) fields = fields or ["node_id", "name", "query_stage"] return dict((f, getattr(node, f)) for f in fields if hasattr(node, f)) def update_options(self, opts): self.raw_opts.update(opts) def clear_options(self): self.opts_locked = False self.opts = None self.raw_apts = reqparse.Namespace() def set_options(self): opts = self.raw_opts self.opts = ZWaveOption(device=opts.get("device"), config_path=opts.get("config_path"), user_path=opts.get("user_path"), cmd_line=opts.get("cmd_line")) for key, val in opts.items(): if key not in ["device", "config_path", "user_path", "cmd_line"]: getattr(self.opts, "set_" + key)(val) self.opts.lock() self.opts_locked = True def start(self): if not self.opts_locked: raise ZWaveCentralException("ZWave options not locked") self.net = ZWaveNetwork(self.opts, autostart=False) self.ctrl.append(self.net.controller) self.home_id = self.net.home_id self.net.start()
class ZWaveController(): network = None def setup(self, updateCallback): dispatcher.connect(self.onNetworkReady, ZWaveNetwork.SIGNAL_NETWORK_READY) dispatcher.connect(self.onNetworkStart, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self.onNetworkFailed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) # TODO: make udev.symlink rule to a specific port (USB0/1) # Uncomment this to run on PC (remember to update the zwave config path) #options = ZWaveOption("/dev/ttyUSB0", \ # config_path="/home/<USER>/software/python-openzwave-0.2.6/openzwave/config", \ options = ZWaveOption("/dev/serial/by-path/platform-bcm2708_usb-usb-0:1.2:1.0-port0", \ config_path="/home/pi/software/python-openzwave-0.2.6/openzwave/config", \ user_path=".", cmd_line="") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level('Debug') options.set_poll_interval(30); options.set_suppress_value_refresh(False) options.addOptionBool("AssumeAwake", True) options.set_logging(False) options.lock() self.network = ZWaveNetwork(options, autostart=False) self.onDeviceUpdateCallback = updateCallback self.network.start() self.addedConnections = False Timer(2*60, self.setupConnections).start() def tearDown(self): network.stop() def getDeviceList(self): devices = [] for node in self.network.nodes: if node == 1: continue # don't add the controller devices.append(self.buildDevice(node)) return devices def buildDevice(self, node): dev = {} dev['id'] = int(self.network.home_id)*1000 + node dev['type'] = 'unknown' dev['product_name'] = self.network.nodes[node].product_name if self.getValueForLabel(node, 'Switch'): dev['type'] = 'appliance' val = self.getValueForLabel(node, 'Energy') dev['consumption_accumulated'] = type(val) != "None" and val or 0 val = self.getValueForLabel(node, 'Power') dev['consumption_current'] = type(val) != "None" and val or 0 if self.getValueForLabel(node, 'Switch') == 'True': dev['state'] = 'on' else: dev['state'] = 'off' if self.getValueForLabel(node, 'Sensor'): dev['type'] = 'sensor' dev['temperature'] = self.getValueForLabel(node, 'Temperature') dev['luminance'] = self.getValueForLabel(node, 'Luminance') dev['presence'] = "undetected" dev['battery_level'] = self.getValueForLabel(node, 'Battery Level') return dev def getValueForLabel(self, node, label): for v in self.network.nodes[node].values: if self.network.nodes[node].values[v].label == label: #self.network.nodes[node].refresh_value(v); return str(self.network.nodes[node].values[v].data_as_string) return None def setDeviceState(self, device_id, state): node = device_id%1000 if not self.network.nodes[node]: return for val in self.network.nodes[node].get_switches() : self.network.nodes[node].set_switch(val, True if state=='on' else False) def setupConnections(self): self.addedConnections = True dispatcher.connect(self.onNodeUpdate, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.onNodeUpdateValue, ZWaveNetwork.SIGNAL_VALUE) dispatcher.connect(self.onNodeUpdateValue, ZWaveNetwork.SIGNAL_NODE_EVENT) dispatcher.connect(self.onNodeUpdateValue, ZWaveNetwork.SIGNAL_VALUE_CHANGED) dispatcher.connect(self.onNodeUpdateValue, ZWaveNetwork.SIGNAL_VALUE_REFRESHED) # Event Handlers def onNetworkStart(self, network): print("network started : homeid %0.8x - %d nodes were found." % \ (network.home_id, network.nodes_count)) def onNetworkFailed(self, network): print("network can't load :(") def onNetworkReady(self, network): print("network : I'm ready : %d nodes were found." % network.nodes_count) print("network : my controller is : %s" % network.controller) self.network = network if not self.addedConnections: self.setupConnections() def onNodeUpdate(self, network, node): print('node UPDAAAATEEE : %s.' % node) self.network = network def onNodeUpdateValue(self, network, node, value): print('node : %s.' % node) print('value: %s.' % value) if node.node_id == 1: return # don't send controller notifications dev = self.buildDevice(node.node_id) if type(value) is int: if dev['type'] == 'sensor' and value == 255: dev['presence'] = 'detected' self.network = network self.onDeviceUpdateCallback(dev) if type(value) is ZWaveValue: if dev['type'] == 'appliance' and value.label == 'Switch': state = value.data and 'on' or 'off' dev['state'] = state self.network = network self.onDeviceUpdateCallback(dev) if dev['type'] == 'appliance' and value.label == 'Power': power = str(value.data) if dev['state'] == 'off' or (dev['state'] == 'on' and float(power) != 0): dev['consumption_current'] = power self.network = network self.onDeviceUpdateCallback(dev) else: self.network = network print('WHAATF do i do with this? %s', power) if dev['type'] == 'appliance' and value.label == 'Energy': energy = str(value.data) dev['consumption_accumulated'] = energy self.network = network self.onDeviceUpdateCallback(dev) if dev['type'] == 'sensor' and value.label == 'Temperature': temperature = str(value.data) dev['temperature'] = temperature self.network = network self.onDeviceUpdateCallback(dev) if dev['type'] == 'sensor' and value.label == 'Luminance': luminance = str(value.data) dev['luminance'] = luminance self.network = network self.onDeviceUpdateCallback(dev) if value.label == 'Battery Level': battery = str(value.data) dev['battery_level'] = battery self.network = network self.onDeviceUpdateCallback(dev)
class Open_zwave(HAInterface): VERSION = '0.0.4' def louie_network_ready(self, network): self._logger.info(">>>>>>> Hello from network : I'm ready : %d nodes were found.".format(self._network.nodes_count)) self._logger.info(">>>>>>> Hello from network : my controller is : {}".format(self._network.controller)) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.louie_value_update, ZWaveNetwork.SIGNAL_VALUE) def louie_node_update(self, network, node): self._logger.debug('>>>>>>> Hello from node : {}.'.format(node)) def louie_value_update(self, network, node, value): self._logger.debug('>>>>>>> Hello from value : {}'.format(value)) for lockvalue in self.get_door_locks(node.node_id).values(): if lockvalue.value_id == value.value_id: if value.data: self._onCommand(address=str(node.node_id), command=Command.LOCK) else: self._onCommand(address=str(node.node_id), command=Command.UNLOCK) for val in self._network.nodes[node.node_id].get_switches(): if val == value.value_id: if value.data: self._onCommand(address=str(node.node_id), command=Command.ON) else: self._onCommand(address=str(node.node_id), command=Command.OFF) for val in self._network.nodes[node.node_id].get_dimmers() : if val == value.value_id: #Poll dimmer to ensure ramp up/down completes level = value.data if self.dimmer_polled_value.has_key(val): self._logger.debug('>>>>>>> Hello from level : {} {}'.format(level, self.dimmer_polled_value[val])) if level == self.dimmer_polled_value[val]: del self.dimmer_polled_value[val] if level < 2: self._onCommand(address=str(node.node_id), command=Command.OFF) elif level > 98: self._onCommand(address=str(node.node_id), command=Command.ON) else: self._onCommand(address=str(node.node_id), command=(Command.LEVEL,level)) else: self.dimmer_polled_value[val] = level time.sleep(1) value.refresh() else: time.sleep(1) self.dimmer_polled_value[val] = level value.refresh() def __init__(self, *args, **kwargs): self._serialDevicePath = kwargs.get('serialDevicePath', None) self._configpath = kwargs.get('config_path', "/etc/openzwave/") super(Open_zwave, self).__init__(self, *args, **kwargs) self.dimmer_polled_value = {} def _init(self, *args, **kwargs): self.awake = False self.ready = False self.nodesdisplayed = False self._options = ZWaveOption(self._serialDevicePath, \ config_path=self._configpath, \ user_path=".", cmd_line="") self._options.set_log_file("OZW_Log.log") self._options.set_append_log_file(False) self._options.set_console_output(False) #self._options.set_save_log_level(log) self._options.set_save_log_level('Info') self._options.set_logging(True) self._options.set_notify_transactions(True) self._options.lock() self._network = ZWaveNetwork(self._options, log=None,autostart=False) dispatcher.connect(self.louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) self._network.start() super(Open_zwave, self)._init(self, *args, **kwargs) def _printNetwork(self, node): node = self._network.nodes[node] self._logger.info("------------------------------------------------------") self._logger.info("{} - Name : {}".format(node.node_id, node.name)) self._logger.info("{} - Manufacturer name / id : {} / {}".format( node.node_id, node.manufacturer_name, node.manufacturer_id)) self._logger.info("{} - Product name / id / type : {} / {} / {}".format( node.node_id, node.product_name, node.product_id, node.product_type)) self._logger.info("{} - Version : {}".format(node.node_id, node.version)) self._logger.info("{} - Command classes : {}".format(node.node_id, node.command_classes_as_string)) self._logger.info("{} - Capabilities : {}".format(node.node_id, node.capabilities)) self._logger.info("{} - Neighbors : {}".format(node.node_id, node.neighbors)) self._logger.info("{} - Can sleep : {}".format(node.node_id, node.can_wake_up())) for value in self.get_door_locks(node.node_id, 'All').values(): self._logger.debug("{} - {} : {}".format(node.node_id,value.label,value.data)) def _readInterface(self, lastPacketHash): if (self._network.state >= self._network.STATE_AWAKED and not self.awake): self.awake = True self._logger.info("Network Awaked") if (self._network.state >= self._network.STATE_READY and not self.ready): self.ready = True self._logger.info("Network Ready") if not self.awake: self._logger.debug("Not awaked") elif self.awake and not self.ready: self._logger.debug("Not ready") elif not self.nodesdisplayed and self.ready: for node in self._network.nodes: self._printNetwork(node) self.update_status() self.nodesdisplayed = True time.sleep(1) def version(self): self._logger.info("Open_zwave Pytomation Driver version " + self.VERSION) self._logger.info("Use openzwave library : {}".format(self._network.controller.ozw_library_version)) self._logger.info("Use python library : {}".format(self._network.controller.python_library_version)) self._logger.info("Use ZWave library : {}".format(self._network.controller.library_description)) def get_door_locks(self, node, datatype = 'Bool'): return self._network.nodes[node].get_values(class_id=0x62, genre='User', \ type=datatype, readonly=False, writeonly=False) def lock(self, address): node = int(address) for value in self.get_door_locks(node).values(): self._logger.debug("Lock") value.data = True def unlock(self, address): node = int(address) for value in self.get_door_locks(node).values(): self._logger.debug("Unlock") value.data = False def on(self, address): node = int(address) for val in self._network.nodes[node].get_switches() : self._logger.debug("Activate switch") self._network.nodes[node].set_switch(val,True) for val in self._network.nodes[node].get_dimmers() : self._logger.debug("Activate dimmer : {}".format(self._network.nodes[node])) self._network.nodes[node].set_dimmer(val,99) def off(self, address): node = int(address) for val in self._network.nodes[node].get_switches() : self._logger.debug("Deactivate switch") self._network.nodes[node].set_switch(val,False) for val in self._network.nodes[node].get_dimmers() : self._logger.debug("Deactivate dimmer : {}".format(self._network.nodes[node])) self._network.nodes[node].set_dimmer(val,0) def level(self, address, level): node = int(address) for val in self._network.nodes[node].get_dimmers() : self._logger.debug("Set dimmer : {}".format(self._network.nodes[node])) self._network.nodes[node].set_dimmer(val, level) def status(self, address): node = int(address) for val in self._network.nodes[node].get_switches() : level = self._network.nodes[node].get_switch_state(val) if level: self._onState(address=address, state=State.ON) else: self._onState(address=address, state=State.OFF) for val in self._network.nodes[node].get_dimmers() : level = self._network.nodes[node].get_dimmer_level(val) if level < 2: self._onState(address=address, state=State.OFF) elif level > 98: self._onState(address=address, state=State.ON) else: self._onState(address=address, state=(State.LEVEL,level)) for value in self.get_door_locks(node).values(): if value.data: self._onState(address=address, state=State.LOCKED) else: self._onState(address=address, state=State.UNLOCKED) def update_status(self): for d in self._devices: self.status(d.address)
class Backend(): def __init__(self): ###### options needed for python openzwave library like config files path, logging, device = configpi.interface options = ZWaveOption( device, config_path="/home/pi/IoTLab/python-openzwave/openzwave/config", user_path=".", cmd_line="") options.set_log_file("OZW.log") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level('Warning') options.set_logging(True) options.lock() # creation of the object network using the options entity already created self.network = ZWaveNetwork(options, autostart=False) ###### These dispatchers associate a method to a signal. the signals are generated by the library python-openzwave. ###### Once the signal is received. It's associated method is executed (see "_node_added" example below in "_network_started" method) dispatcher.connect(self._network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self._network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) ###### backend object attributes # self.devices = OrderedDict() ### will contain the list of nodes in the network # self.sensors = OrderedDict() ### will contain the list of sensors (only) in the network self.node_added = False self.node_removed = False self.timestamps = { } ### will contain the time of the last values' update for each sensor self.queryStages = { ### the diffrent stages that a node object gets through before being ready "None": 1, # Query process hasn't started for this node "ProtocolInfo": 2, # Retrieve protocol information "Probe": 3, # Ping device to see if alive "WakeUp": 4, # Start wake up process if a sleeping node "ManufacturerSpecific1": 5, # Retrieve manufacturer name and product ids if ProtocolInfo lets us "NodeInfo": 6, # Retrieve info about supported, controlled command classes "SecurityReport": 7, # Retrieve a list of Command Classes that require Security "ManufacturerSpecific2": 8, # Retrieve manufacturer name and product ids "Versions": 9, # Retrieve version information "Instances": 10, # Retrieve information about multiple command class instances "Static": 11, # Retrieve static information (doesn't change) "Probe1": 12, # Ping a device upon starting with configuration "Associations": 13, # Retrieve information about associations "Neighbors": 14, # Retrieve node neighbor list "Session": 15, # Retrieve session information (changes infrequently) "Dynamic": 16, # Retrieve dynamic information (changes frequently) "Configuration": 17, # Retrieve configurable parameter information (only done on request) "Complete": 18 # Query process is completed for this node } ####################################################################################################################### ############# LAUNCH ################################################################################################# ####################################################################################################################### def _network_started(self, network): # executed once the software representation is started. the discovery of the network components has begun. they will be mapped into objects print("network started - %d nodes were found." % network.nodes_count) # these dispatchers associate a method to a signal. the signals are generated by the library python-openzwave. # a signal may contain a number of parameters that are passed to the method associated to the signal. # for exemple, the dispatcher below associates the signal "SIGNAL_NODE_ADDED" to the method "_node_added" that is implemented below (line 111). # the signal "SIGNAL_NODE_ADDED" transports two parameters which are the objects network and node. # once this signal is received, these two parameters will be passed to the method "_node_added" and the method will be executed. dispatcher.connect(self._node_added, ZWaveNetwork.SIGNAL_NODE_ADDED) dispatcher.connect(self._node_removed, ZWaveNetwork.SIGNAL_NODE_REMOVED) def _network_ready(self, network): # executed once the software representation is ready print("network : ready : %d nodes were found." % network.nodes_count) print("network : controller is : %s" % network.controller) dispatcher.connect(self._value_update, ZWaveNetwork.SIGNAL_VALUE) def _node_added(self, network, node): # executed when node is added to the software representation. it's executed after the method "_debug_node_new" (see below) print('node added: %s.' % node.node_id) self.timestamps["timestamp" + str(node.node_id)] = "None" self.node_added = True def _node_removed(self, network, node): # executed when node is removed from the software representation print('node removed: %s.' % node.name) self.node_removed = True def _value_update(self, network, node, value): # executed when a new value from a node is received print('Node %s: value update: %s is %s.' % (node.node_id, value.label, value.data)) self.timestamps["timestamp" + str(node.node_id)] = int(time.time()) ################################################################################################################ ######################## START AND STOP THE SOFTWARE REPRESENTATION ############################################ ################################################################################################################ def start(self): # this method starts the software representation global started if started: print "Already started" return started = True self.network.start() print "Z-Wave Network Starting..." for i in range(0, 300): if self.network.state == self.network.STATE_READY: break else: time.sleep(1.0) if not self.network.is_ready: print "Network is not ready but continue anyway" print "------------------------------------------------------------" print "Nodes in network : %s" % self.network.nodes_count print "------------------------------------------------------------" def stop(self): # this method stops the software representation global started started = False print "Stopping Z-Wave Network... " self.network.stop() def reset(self): if self.network.nodes_count == 1: self.network.controller.hard_reset() return "Hard Reset Done" return "Cannot make Hard Reset while nodes included in network"
class Backend(): def __init__(self): device = "/dev/ttyUSB0" options = ZWaveOption(device, config_path="/home/rich/openzwave/config", user_path=".", cmd_line="") options.set_log_file("OZW_Log.log") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level('Debug') options.set_logging(True) options.lock() self.values = {} dispatcher.connect(self._network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self._network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(self._network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) self.network = ZWaveNetwork(options, autostart=False) def _network_started(self, network): print("network started - %d nodes were found." % network.nodes_count) def _network_failed(self, network): print("network failed :(") def _network_ready(self, network): print("network : ready : %d nodes were found." % network.nodes_count) print("network : controller is : %s" % network.controller) dispatcher.connect(self._node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self._node_event, ZWaveNetwork.SIGNAL_NODE_EVENT) dispatcher.connect(self._value_update, ZWaveNetwork.SIGNAL_VALUE) def _node_update(self, network, node): print('node update: %s.' % node) def _node_event(self, network, node, signal, sender): print('node event %s from node %s.' % (signal, node.node_id)) value = self.network.get_value(IR_SENSOR_VALUE).data print('value is now %s' % value) self.log_access_with_value(value) def log_access_with_value(self, value): print("logging sensor trigger to file") with open("triggers.csv", "a") as sensor_log_file: sensor_log_file.write("%s,%s\n" % (datetime.today().strftime("%d/%m/%Y %H:%M:%S"), value)) def _value_update(self, network, node, value): print('value update: %s is %s.' % (value.label, value.data)) self.values[value.label] = value.data if value.label == 'Temperature': self.process_temp_change(value.data) def process_temp_change(self, value): if value > 25 and not self.get_switch_status(3): print('too hot - turn on fan') self.switch_on(3) elif value < 25 and self.get_switch_status(3): print('cool enough - turn off fan') self.switch_off(3) def log_values(self): print('Writing sensor log') try: t = self.get_temperature() h = self.get_humidity() l = self.get_brightness() with open("sensors.csv", "a") as sensor_log_file: sensor_log_file.write("%s,%s,%s,%s\n" % (datetime.today().strftime("%d/%m/%Y %H:%M:%S"), t, h, l)) except: print('Failed to log values') self.start_timer() def get_sensor_values(self): lines = open("sensors.csv", "r").readlines() return_list = [] for line in lines: line = line[:-1] # remove newline d = {'Date': line.split(',')[0], 'Temperature': line.split(',')[1], 'Humidity': line.split(',')[2], 'Lux': line.split(',')[3]} return_list.append(d) return return_list def switch_on(self, name): print("Activating switch %s" % name) parsed_id = 0 try: parsed_id = int(name) except ValueError: pass for key, node in self.network.nodes.iteritems(): if node.name == name or node.node_id == parsed_id: for val in node.get_switches(): node.set_switch(val, True) def switch_off(self, name): print("Deactivating switch %s" % name) parsed_id = 0 try: parsed_id = int(name) except ValueError: pass for key, node in self.network.nodes.iteritems(): if node.name == name or node.node_id == parsed_id: for val in node.get_switches(): node.set_switch(val, False) def get_switch_status(self, name): print("Querying switch %s" % name) parsed_id = 0 try: parsed_id = int(name) except ValueError: pass for key, node in self.network.nodes.iteritems(): if node.name == name or node.node_id == parsed_id: for val in node.get_switches(): state = node.get_switch_state(val) return state def get_temperature(self): return self.network.nodes[2].get_sensor_value(TEMPERATURE_VALUE) def get_humidity(self): return self.network.nodes[2].get_sensor_value(HUMIDITY_VALUE) def get_brightness(self): return self.network.nodes[2].get_sensor_value(LUX_VALUE) def start_timer(self): t = Timer(900, self.log_values) t.start() def start(self): global started if started: return started = True self.network.start() self.start_timer() print "Starting..." for i in range(0, 90): if self.network.state >= self.network.STATE_READY: break else: sys.stdout.write(".") sys.stdout.flush() time.sleep(1.0) def stop(self): print "Stopping..." self.network.stop()
class myZWave: def __init__(self): self.lastReport = {} self.startTime = time.time() def setup(self): self.logs = myLogs('zwave-daemon') device = "/dev/ttyUSB0" log = "Info" # Debug #Define some manager options # see docs at the end of file options = ZWaveOption(device, \ #config_path="openzwave/config", \ user_path="/home/scripts/zwave/home",\ cmd_line="" ) #options.set_log_file("OZW_Log.log") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level('None') #options.set_queue_log_level("None") #options.set_save_log_level('Info') options.set_logging(False) options.lock() self.ZWave = ZWaveNetwork(options, log=None, autostart=False) def start(self): self.setup() #self.ZWave.controller.soft_reset(); #self.logs.log("continue"); #sys.exit(1); dispatcher.connect(self.louie_network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self.louie_network_resetted, ZWaveNetwork.SIGNAL_NETWORK_RESETTED) dispatcher.connect(self.louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) self.ZWave.start() for i in range(0, 300): if self.ZWave.state >= self.ZWave.STATE_STARTED: self.logs.log("Startup time: " + str(self.getRunTime())) break else: self.logs.log("***** STARTING *****") #sys.stdout.write(".") #sys.stdout.write("starting.") #sys.stdout.flush() time.sleep(1.0) if self.ZWave.state < self.ZWave.STATE_STARTED: self.logs.log(".") self.logs.log( "Can't initialise driver! Look at the logs in OZW_Log.log. Runtime: " + str(self.getRunTime())) sys.exit(1) for i in range(0, 600): #if self.ZWave.state>=self.ZWave.STATE_AWAKED: # wait fot awaked nodes only: if self.ZWave.state >= self.ZWave.STATE_READY: self.logs.log("Got ready by " + str(self.getRunTime())) break else: time.sleep(1.0) if not self.ZWave.is_ready: #if self.ZWave.state < self.ZWave.STATE_AWAKED: self.logs.log("Runtime: " + str(self.getRunTime())) self.logs.log( "Can't start network! Look at the logs in OZW_Log.log") self.logs.log("Resetting controller in 5 sec. Then reinit") self.ZWave.controller.soft_reset() sys.exit(1) self.logs.log("Collecting switches...") self.collectSwitches() def getRunTime(self): return (time.time() - self.startTime) def stop(self): self.ZWave.stop() self.logs.log("####### STOPPED ######") def nodeCmd(self, id, cmd): return getattr(self.ZWave.nodes[id], cmd)() def nodeProp(self, id, cmd): return getattr(self.ZWave.nodes[id], cmd) def switch(self, id, state): try: node = self.switches[str(id)] self.logs.log('Switching node ' + str(node) + ' sw: ' + str(id) + ' to ' + str(state)) newState = True if state == 1 else False self.ZWave.nodes[int(node)].set_switch(int(id), newState) except: e = sys.exc_info()[0] self.logs.log("UNKNOWN SWITCH: " + str(id) + ' Error:' + str(e)) #self.ZWave.nodes[2].set_switch(216172782152138752, True) def collectSwitches(self): self.switches = {} for node in self.ZWave.nodes: for val in self.ZWave.nodes[node].get_switches(): self.switches[str(val)] = node self.logs.log("* Found switch " + str(val) + ' on node ' + str(node)) self.logs.log(self.switches) def logSwitches(self): self.logs.log("** List of Swithces ** ") self.logs.log(self.switches) def logPowerLevels(self): _nodes = {} for node in self.ZWave.nodes: for val in self.ZWave.nodes[node].get_power_levels(): _nodes[str(val)] = node self.logs.log("** List of PowerLevels ** ") self.logs.log(_nodes) def logSensors(self): _nodes = {} for node in self.ZWave.nodes: for val in self.ZWave.nodes[node].get_sensors(): _nodes[str(val)] = node self.logs.log("** List of Sensors ** ") self.logs.log(_nodes) # Event Listeners def louie_network_started(self): self.logs.log('//////////// ZWave network is started ////////////') self.logs.log( 'Louie signal : OpenZWave network is started : homeid {:08x} - {} nodes were found.' .format(self.ZWave.home_id, self.ZWave.nodes_count)) #self.ZWave.controller.soft_reset(); def louie_network_resetted(self): #self.logs.log('Louie signal : OpenZWave network is resetted.') pass def louie_network_ready(self): self.logs.log('//////////// ZWave network is ready ////////////') self.logs.log( 'Louie signal : ZWave network is ready : {} nodes were found.'. format(self.ZWave.nodes_count)) self.logs.log('Louie signal : Controller : {}'.format( self.ZWave.controller)) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.louie_value_update, ZWaveNetwork.SIGNAL_VALUE) dispatcher.connect(self.louie_ctrl_message, ZWaveController.SIGNAL_CONTROLLER) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_NODE_READY) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_NODE_REMOVED) dispatcher.connect(self.louie_node_update, ZWaveNetwork.SIGNAL_SCENE_EVENT) dispatcher.connect(self.louie_my_update, ZWaveNetwork.SIGNAL_NOTIFICATION) dispatcher.connect(self.louie_my_update, ZWaveNetwork.SIGNAL_VALUE_REFRESHED) def louie_node_update(self, network, node): #self.logs.log('Louie signal : Node update : {}.'.format(node)) pass def louie_my_update(self, network, node=False, t=False, f=False): self.logs.log('Louie signal : Node update : {}.'.format(node)) def louie_value_update(self, network, node, value): now = datetime.datetime.now() log = str(now) + 'Value update: {}.'.format(value) #if value.data == False and value.value_id != 216172782152138752: #self.ZWave.nodes[2].set_switch(216172782152138752, False) cmd = '/home/scripts/zwave/event-handler.py' state = str(value.data) zwaveID = str(value.value_id) if (zwaveID not in self.lastReport or self.lastReport[zwaveID]['val'] != state or (time.time() - self.lastReport[zwaveID]['time']) > 2): self.lastReport[zwaveID] = {'time': time.time(), 'val': state} subprocess.Popen([cmd, str(value.value_id), state], stdout=open('/dev/null', 'w'), stderr=open('/dev/null', 'w')) self.logs.log(log + " ☉") else: self.logs.log(log) def louie_ctrl_message(self, state, message, network, controller): #self.logs.log('Louie signal : Controller message : {}.'.format(message)) pass
def louie_node_update(network, node): print("Hello from node : {}.".format(node)) def louie_value_update(network, node, value): print("Hello from value : {}.".format( value )) #Create a network object network = ZWaveNetwork(options, autostart=False) #We connect to the louie dispatcher dispatcher.connect(louie_network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(louie_network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) network.start() #We wait for the network. print("***** Waiting for network to become ready : ") for i in range(0,90): if network.state>=network.STATE_READY: print("***** Network is ready") break else: sys.stdout.write(".") sys.stdout.flush() time.sleep(1.0) time.sleep(5.0) for node in network.nodes:
class Network(object): _instance = None def _init(self, device, config, timeout): self.network = None self.status = 1 self.timeout = timeout self.device = device self.config = config options = ZWaveOption(self.device, config_path=self.config, user_path=".", cmd_line="") options.set_log_file("OZW_Log.log") options.set_append_log_file(False) options.set_console_output(False) options.set_save_log_level('Debug') options.set_logging(True) options.lock() self.network = ZWaveNetwork(options, autostart=False) dispatcher.connect(louie_network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(louie_network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(louie_network_ready, ZWaveNetwork.SIGNAL_NETWORK_READY) self.network.start() def __new__(class_, device=None, config=None, timeout=None): if not isinstance(class_._instance, class_): if not device: return None class_._instance = object.__new__(class_) class_._instance._init(device, config, timeout) return class_._instance def __del__(self): self.network.stop() def __iter__(self): return self def next(self): self.timeout -= 1 if self.timeout == 0: self.status = 1 raise StopIteration if self.network.state >= self.network.STATE_READY: self.status = 0 raise StopIteration return self.timeout def get_timout(self): return self.timeout def get_status(self): return self.status def stop(self): self.network.stop() def get_state(self): return self.network.state def is_stopped(self): return self.network.state == self.network.STATE_STOPPED def is_running(self): return self.network.state == self.network.STATE_READY
class ZWaveAgent(AbstractAgent): """A Zwave Agent for directly collecting data from a ZWave network. Requires openzwave to be installed (see tools folder) and ZWave Stick to be plugged in""" def __init__(self, configDir, osnInstance): AbstractAgent.__init__(self, configDir, osnInstance) log="Info" # should be read from config later configChanged = False if "zwave_device" not in self.configData: self.configData["zwave_device"] = "/dev/ttyACM0" configChanged = True if configChanged: self.serializeConfig() # handle zwave default device configurations self.zwaveDefaultConfigs = {} self.zwaveDefaultConfigFile = os.path.join(self.configDir, "zwavedefaultconfigs.config.json") self.readZwaveDefaultConfigs() self.device = self.configData["zwave_device"] self.logger.debug("Initiating ZWaveAgent with device %s." % self.device) self.zwaveOptions = "" try: self.zwaveOptions = ZWaveOption(self.device.encode('ascii'), \ config_path=expanduser("~")+"/ozw-install/python-open-zwave/openzwave/config", \ user_path=self.configDir, cmd_line="") self.zwaveOptions.set_log_file("../log/openzwave.log") # Todo: don't hardcode openzwave-path self.zwaveOptions.set_append_log_file(False) self.zwaveOptions.set_console_output(False) self.zwaveOptions.set_save_log_level(log) self.zwaveOptions.set_logging(False) self.zwaveOptions.lock() except BaseException as e: self.logger.info("Error setting up ZWave network. Correct device? Device properly connected? Device is: %s Exception message: %s" % (self.device, e)) self.inDiscoveryMode = False #scanning for available devices and sensors is slightly more complicated here... def networkStarted(self, network): self.logger.info("Network %0.8x started" % network.home_id) def networkFailed(self, network): self.logger.warning("Sorry, Network couldn't be started...") if self.inDiscoveryMode: self.logger("Discovery failed - terminating.") self.stop() def networkReady(self, network): self.logger.info("Network %0.8x is ready - %d nodes were found." % (self.network.home_id, self.network.nodes_count)) self.logger.info("Network controller %s is connected to %s" % (self.network.controller.node.product_name, self.device)) self.logger.info("\nNodes List:") self.logger.info("===========") configChanged = False for node in network.nodes: self.logger.info("Node %s: %s (battery: %s)" % (node, network.nodes[node].product_name, network.nodes[node].get_battery_level())) self.logger.info("Available Command Classes: %s" % network.nodes[node].command_classes) modelString = network.nodes[node].manufacturer_name + " " + network.nodes[node].product_name if node != self.network.controller.node_id: # not for controller node # Should usually only be necessary once in a lifetime for a given network, but repeating it on every startup doesn't ha$ self.configureNode(network, node) for sensor in network.nodes[node].get_sensors(): if ((not self.sensorConfigured(sensor)) and (self.inDiscoveryMode)): # we are in discovery mode and sensor is not configured yet, add default self.addDefaultSensor(sensor, network.nodes[node].get_sensors()[sensor].label, network.nodes[node].get_sensors()[sensor].units, {"sensorModel":modelString}) configChanged = True self.logger.debug("Sensor %s has %s of %s (Unit: %s)" % (sensor, network.nodes[node].get_sensors()[sensor].label, \ network.nodes[node].get_sensor_value(sensor), network.nodes[node].get_sensors()[sensor].units)) if self.inDiscoveryMode: # as discovery is more complicated for Zwave, we have to do it this way. # in discovery Mode, the config including new default configurations is serialized, then the agent Is stopped. if configChanged: # serialize for having all new sensors in config self.serializeConfig() self.isRunning = False # this ensures that runner stops this agent after discovery is completed else: dispatcher.connect(self.nodeUpdate, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.valueUpdate, ZWaveNetwork.SIGNAL_VALUE) def nodeUpdate(self, network, node): # maybe do something valuable here later... self.logger.debug('Received node update from node : %s.' % node) pass def valueUpdate(self, network, node, value): # not sure whether this might produce redundancies in case of one value_id appearing for multiple nodes... # nonetheless, staying with this for the moment self.sendValue(value.value_id, value.data) def configureNode(self,network, node): # Model-specific configuration of node. This definitely needs a complete rewrite later... self.logger.info("Setting specific configuration for product %s (Product ID: %s)..." % (network.nodes[node].product_name, network.nodes[node].product_id)) productId = network.nodes[node].product_id defaultConfig = self.getDefaultDeviceConfiguration(productId) if defaultConfig: # could also be empty in case this product has no default config yet self.logger.debug("Got default config.") for param in network.nodes[node].values.values(): # traverse through available parameters self.logger.debug("Checking if default config exists for %s" % param.value_id) if defaultConfig.has_key("%s" % param.value_id): # is this parameter specified in default config? we take the long value id as key to avoid misinterpretations self.logger.debug("Default config found. Now checking if default config contains a value") if defaultConfig["%s" % param.value_id].has_key("value"): self.logger.debug("Found value. Setting parameter <%s> to %s as specified in default config" % (param.label, defaultConfig["%s" % param.value_id]["value"])) param.data = defaultConfig["%s" % param.value_id]["value"] else: self.logger.info("No default configuration found for device with product id %s - creating dumb template from what is reported..." % productId) newConfig = {} for param in network.nodes[node].values.values(): # traverse through available parameters newConfig["product name"] = network.nodes[node].manufacturer_name + " " + network.nodes[node].product_name newConfig[param.value_id] = {} note = param.label if param.units: note = note + " (" + param.units + ")" newConfig[param.value_id]["note"] = note newConfig[param.value_id]["parameter index"] = param.index newConfig[param.value_id]["value"] = param.data self.zwaveDefaultConfigs["products"][productId] = newConfig self.serializeZwaveDefaultConfigs() def getDefaultDeviceConfiguration(self, productId): self.logger.debug("getting zwave default configs for product id %s" % productId) if self.zwaveDefaultConfigs["products"].has_key(productId): return self.zwaveDefaultConfigs["products"][productId] else: return {} def readZwaveDefaultConfigs(self): self.logger.debug("reading zwave default device configs from %s" % self.zwaveDefaultConfigFile) configChanged = False if os.path.isfile(self.zwaveDefaultConfigFile): # If configFile does not exist yet, default configs will be created and serialized later with open(self.zwaveDefaultConfigFile) as configFileHandle: self.zwaveDefaultConfigs = json.load(configFileHandle) if "products" not in self.zwaveDefaultConfigs: self.zwaveDefaultConfigs["products"]={} configChanged = True if (configChanged): self.serializeZwaveDefaultConfigs() def serializeZwaveDefaultConfigs(self): with open(self.zwaveDefaultConfigFile, "w") as configFileHandle: self.logger.info("Serializing zwave default device configs to %s." % self.zwaveDefaultConfigFile) json.dump(self.zwaveDefaultConfigs, configFileHandle, sort_keys = False, indent = 4, ensure_ascii=False) # data_file.close def run(self): self.isRunning = True #Create a network object self.network = ZWaveNetwork(self.zwaveOptions, autostart=False) #and connect our above handlers to respective events dispatcher.connect(self.networkStarted, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self.networkFailed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(self.networkReady, ZWaveNetwork.SIGNAL_NETWORK_READY) self.network.start() def discoverSensors(self): self.inDiscoveryMode = True # In this case, stuff is slightly more complicated as we have to manage the zwave network, too. # We thus here use the thread's run()-method and terinate once the discovery is complete. self.run() def stop(self): self.network.stop() self.isRunning = False
class Main(object): config = None device_to_node = {} node_to_device = {} node_to_logger = collections.defaultdict(lambda: default_logger) node_ready = {} timers = {} def network_started(self, network): default_logger.info("network started") dispatcher.connect(self.node_queries_complete, ZWaveNetwork.SIGNAL_NODE_QUERIES_COMPLETE) def network_failed(self, network): default_logger.info("network failed") def node_queries_complete(self, network, node): logger = self.node_to_logger[node.node_id] logger.info("node %d queries complete: %s %s", node.node_id, node.product_name, node.manufacturer_name) logger.info("- command classes: %s", node.command_classes_as_string) logger.info("- capabilities: %s", node.capabilities) logger.info("- neighbors: %s", node.neighbors) self.node_ready[node.node_id] = True def network_ready(self, network): default_logger.info("network ready: %d nodes were found", network.nodes_count) # connect to updates after initialization has finished dispatcher.connect(self.value_update, ZWaveNetwork.SIGNAL_VALUE) dispatcher.connect(self.node_update, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.node_event, ZWaveNetwork.SIGNAL_NODE_EVENT) dispatcher.connect(self.ctrl_message, ZWaveController.SIGNAL_CONTROLLER) def node_update(self, network, node): logger = self.node_to_logger[node.node_id] logger.info("node update: %s", node) def node_event(self, network, node, value): device = self.node_to_device.get(node.node_id) logger = self.node_to_logger[node.node_id] logger.info("node event: value: %s", value) if not device: return self.value_basic(logger, node, device, value) def value_update(self, network, node, value): device = self.node_to_device.get(node.node_id) logger = self.node_to_logger[node.node_id] logger.info("value update: %s=%s", value.label, value.data_as_string) if not device: return timer = self.timers.pop(device, None) if timer: timer.cancel() fn = getattr(self, 'value_%s' % value.label.replace(' ', '_'), None) if fn: fn(logger, node, device, value) def value_basic(self, logger, node, device, value): state = 'on' if value == 255 else 'off' logger.info('Basic sensor update: %s', state) if state is not None: self.pub_device_state(device, state, 'sensor') def value_Alarm_Type(self, logger, node, device, value): if 'COMMAND_CLASS_DOOR_LOCK' in node.command_classes_as_string: if value.data not in LOCK_ALARM_TYPE: logger.warning('Lock update unknown: %s', value.data) return logger.info('Lock update: %s', LOCK_ALARM_TYPE[value.data]) state = LOCK_ALARM_STATE.get(value.data) if state is not None: self.pub_device_state(device, state, 'lock') def value_Switch(self, logger, node, device, value): state = 'on' if value.data else 'off' logger.info('Switch update: %s', state) self.pub_device_state(device, state, 'ack') def value_Sensor(self, logger, node, device, value): # Neo CoolCam Door/Window sensors emit both Sensor and Access Control # for events, but use both for reliability. state = 'on' if value.data else 'off' logger.info('Sensor update: %s', state) self.pub_device_state(device, state, 'sensor') def value_Access_Control(self, logger, node, device, value): # Philio 4 in 1 Multi-Sensor only emits this for open/close. state = ACCESS_CONTROL_STATE.get(value.data) if state is None: logger.warning('Access control unknown: %s', value.data) return logger.info('Access control update: %s', state) self.pub_device_state(device, state, 'sensor') def value_Temperature(self, logger, node, device, value): if value.units == 'F': celsius = (value.data - 32) * 5 / 9 else: celsius = value.data logger.debug('Temperature: %.1fC', celsius) device = 'temp.' + device.split('.')[-1] message = { 'topic': 'temp', 'device': device, 'temp': celsius, } self.publish(message) def value_Luminance(self, logger, node, device, value): # label: [Luminance] data: [16.0] device = 'lux.' + device.split('.')[-1] message = { 'topic': 'lux', 'device': device, 'lux': value.data, } self.publish(message) def value_Battery_Level(self, logger, node, device, value): # label: [Battery Level] data: [100] message = { 'topic': 'openzwave', 'device': device, 'battery': value.data, } self.publish(message) def value_Burglar(self, logger, node, device, value): state = BURGLAR.get(value.data) if state is None: logger.warning("Burglar unknown: %s", value.data) return logger.info("motion update: %s", state) if state == 'Motion': device = 'pir.' + device.split('.')[-1] self.pub_device_state(device, 'on', 'sensor') # sensors do not send off, so trigger this on a timer delay if device in self.timers: self.timers[device].cancel() def switch_off(): logger.info("%s motion auto off", device) self.pub_device_state(device, 'off', 'sensor') timer = self.timers[device] = threading.Timer(60.0, switch_off) timer.start() def pub_device_state(self, device, command, topic): message = { 'topic': topic, 'device': device, 'command': command, } self.publish(message) def publish(self, message): topic = 'gohome/%s/%s' % (message['topic'], message['device']) message['timestamp'] = datetime.datetime.utcnow().strftime( '%Y-%m-%d %H:%M:%S.%f')[:-3] message = json.dumps(message) self.client.publish(topic, message, retain=True) def set_device_state(self, node_id, on): node = self.network.nodes.get(node_id) logger = self.node_to_logger[node_id] if not node: logger.warning('No node %d found', node_id) return by_label = {val.label: val for val in node.values.values()} if 'COMMAND_CLASS_DOOR_LOCK' in node.command_classes_as_string: logger.info('Unlocking...' if on else 'Locking...') by_label['Locked'].data = not on logger.info("Locked set to %s", not on) elif 'COMMAND_CLASS_SWITCH_BINARY' in node.command_classes_as_string: logger.info('Switching on...' if on else 'Switching off...') by_label['Switch'].data = on logger.info("Switch set to %s", on) else: logger.info("Node %d not in recognised classes", node_id) def ctrl_message(self, state, message, network, controller): default_logger.info('controller message: %s', message) def lock_node(self): def nodes_matching_class(name): return filter(lambda n: name in n.command_classes_as_string, self.network.nodes.values()) return next(nodes_matching_class('COMMAND_CLASS_DOOR_LOCK'), None) def on_mqtt_connect(self, client, userdata, flags, rc): default_logger.info('Connected to MQTT') client.subscribe('gohome/command/#') client.subscribe('gohome/config') def on_mqtt_message(self, client, userdata, msg): if msg.payload.startswith(b'{'): message = json.loads(msg.payload) else: message = yaml.safe_load(msg.payload) if 'topic' in message: topic = message['topic'] else: topic = msg.topic.split('/')[1] if topic == 'config': self.config = message self.node_to_device = { int(device['source'][6:]): _id for _id, device in self.config['devices'].items() if 'source' in device and device['source'].startswith('zwave.') } self.device_to_node = { device: node_id for node_id, device in self.node_to_device.items() } default_logger.info(str(self.node_to_device)) self.node_to_logger = collections.defaultdict( lambda: default_logger) for node_id, device in self.node_to_device.items(): self.node_to_logger[node_id] = logging.getLogger(device) default_logger.info('Configured devices') elif topic == 'command': if message['device'] not in self.device_to_node: return default_logger.info('Command received: %s', msg.payload.decode('utf-8')) device = message['device'] node_id = self.device_to_node[device] on = message['command'] == 'on' self.set_device_state(node_id, on) def repeat(): self.set_device_state(node_id, on) timer = self.timers[device] = threading.Timer(5.0, repeat) timer.start() def setup_mqtt(self): self.client = paho.Client() self.client.on_connect = self.on_mqtt_connect self.client.on_message = self.on_mqtt_message url = os.getenv('GOHOME_MQTT') if not url: default_logger.error("Please set GOHOME_MQTT") sys.exit() m = re.search(r'^tcp://([^:]+)(?::(\d+))?$', url) if not m: default_logger.error("Invalid value for GOHOME_MQTT: %s", url) sys.exit() hostname, port = m.groups() port = int(port) if port else 1883 default_logger.info('Connecting to mqtt server: %s:%d', hostname, port) self.client.connect(hostname, port=port) def run(self): # Connect to mqtt self.setup_mqtt() # Create a network object self.network = ZWaveNetwork(options, autostart=False) # Hook Ctrl-C to cleanly shutdown. # This ensures openzwave persists its state to the zwcfg xml file. def signal_handler(signal, frame): default_logger.info("Stopping zwave network") self.network.stop() default_logger.info("Stopping mqtt client") self.client.disconnect() signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) dispatcher.connect(self.network_started, ZWaveNetwork.SIGNAL_NETWORK_STARTED) dispatcher.connect(self.network_failed, ZWaveNetwork.SIGNAL_NETWORK_FAILED) dispatcher.connect(self.network_ready, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED) self.network.start() self.client.loop_forever() default_logger.info("Finished")