From f68e4112e117b7034fdcbdd98168699da228a7e7 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Sat, 18 Mar 2023 15:20:00 +0100 Subject: [PATCH] Remote get method works --- meshtastic/__main__.py | 90 ++++++++++++++++++++---------------- meshtastic/mesh_interface.py | 12 ++--- meshtastic/node.py | 38 +++++++++++++-- 3 files changed, 91 insertions(+), 49 deletions(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 5f68444..5e6c510 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -53,10 +53,11 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=W0613 print(f"Connection changed: {topic.getName()}") -def getPref(config, comp_name): +def getPref(interface, dest, comp_name): """Get a channel or preferences value""" name = splitCompoundName(comp_name) + fullConfig = name[0] == name[1] # We want the full config camel_name = meshtastic.util.snake_to_camel(name[1]) # Note: protobufs has the keys in snake_case, so snake internally @@ -64,26 +65,47 @@ def getPref(config, comp_name): logging.debug(f'snake_name:{snake_name} camel_name:{camel_name}') logging.debug(f'use camel:{Globals.getInstance().get_camel_case()}') - objDesc = config.DESCRIPTOR - print() - config_type = objDesc.fields_by_name.get(name[0]) - pref = False - if config_type: - pref = config_type.message_type.fields_by_name.get(snake_name) + localConfig = interface.getNode(BROADCAST_ADDR).localConfig + moduleConfig = interface.getNode(BROADCAST_ADDR).moduleConfig + found = False + for config in [localConfig, moduleConfig]: + objDesc = config.DESCRIPTOR + config_type = objDesc.fields_by_name.get(name[0]) + pref = False + if config_type: + pref = config_type.message_type.fields_by_name.get(snake_name) + if pref or fullConfig: + found = True + break - if (not pref) or (not config_type): + if not found: + if Globals.getInstance().get_camel_case(): + print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have an attribute {snake_name}.") + else: + print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have attribute {snake_name}.") + print("Choices are...") + printConfig(localConfig) + printConfig(moduleConfig) return False - # read the value - config_values = getattr(config, config_type.name) - pref_value = getattr(config_values, pref.name) - - if Globals.getInstance().get_camel_case(): - print(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}") - logging.debug(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}") - else: - print(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}") - logging.debug(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}") + if dest == BROADCAST_ADDR: + # read the value + config_values = getattr(config, config_type.name) + if not fullConfig: + pref_value = getattr(config_values, pref.name) + if Globals.getInstance().get_camel_case(): + print(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}") + logging.debug(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}") + else: + print(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}") + logging.debug(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}") + else: + print(f"{str(config_type.name)}: {str(config_values)}") + logging.debug(f"{str(config_type.name)}: {str(config_values)}") + else: + # Always request full config for remote node + interface.getNode(dest, False).requestConfig(config_type) + return True def splitCompoundName(comp_name): @@ -298,7 +320,7 @@ def onConnected(interface): if args.device_metadata: closeNow = True - interface.getNode(args.dest).getMetadata() + interface.getNode(args.dest, False).getMetadata() if args.begin_edit: closeNow = True @@ -597,32 +619,20 @@ def onConnected(interface): # If we aren't trying to talk to our local node, don't show it if args.dest == BROADCAST_ADDR: interface.showInfo() - - print("") - interface.getNode(args.dest).showInfo() - closeNow = True # FIXME, for now we leave the link up while talking to remote nodes - print("") + print("") + interface.getNode(args.dest).showInfo() + closeNow = True + print("") + else: + print("Showing info of remote node is not supported.") + print("Use the '--get' command for a specific configuration (e.g. 'lora') instead.") if args.get: closeNow = True - localConfig = interface.getNode(args.dest).localConfig - moduleConfig = interface.getNode(args.dest).moduleConfig - - # Handle the int/float/bool arguments for pref in args.get: - found = getPref(localConfig, pref[0]) - if not found: - found = getPref(moduleConfig, pref[0]) + found = getPref(interface, args.dest, pref[0]) - if not found: - if Globals.getInstance().get_camel_case(): - print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have an attribute {pref[0]}.") - else: - print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have attribute {pref[0]}.") - print("Choices are...") - printConfig(localConfig) - printConfig(moduleConfig) - else: + if found: print("Completed getting preferences") if args.nodes: diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index 13ee8cc..8568fb0 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -170,18 +170,18 @@ class MeshInterface: return table - def getNode(self, nodeId, requestConfig=True): + def getNode(self, nodeId, requestChannels=True): """Return a node object which contains device settings and channel info""" if nodeId in (LOCAL_ADDR, BROADCAST_ADDR): return self.localNode else: n = meshtastic.node.Node(self, nodeId) # Only request device settings and channel info when necessary - if requestConfig: - logging.debug("About to requestConfig") - n.requestConfig() + if requestChannels: + logging.debug("About to requestChannels") + n.requestChannels() if not n.waitForConfig(): - our_exit("Error: Timed out waiting for node config") + our_exit("Error: Timed out waiting for channels") return n def sendText(self, text: AnyStr, @@ -522,7 +522,7 @@ class MeshInterface: Done with initial config messages, now send regular MeshPackets to ask for settings and channels """ - self.localNode.requestConfig() + self.localNode.requestChannels() def _handleFromRadio(self, fromRadioBytes): """ diff --git a/meshtastic/node.py b/meshtastic/node.py index 821c191..d3f328a 100644 --- a/meshtastic/node.py +++ b/meshtastic/node.py @@ -61,13 +61,45 @@ class Node: print(f"Module preferences: {prefs}\n") self.showChannels() - def requestConfig(self): - """Send regular MeshPackets to ask for settings and channels.""" - logging.debug(f"requestConfig for nodeNum:{self.nodeNum}") + def requestChannels(self): + """Send regular MeshPackets to ask channels.""" + logging.debug(f"requestChannels for nodeNum:{self.nodeNum}") self.channels = None self.partialChannels = [] # We keep our channels in a temp array until finished self._requestChannel(0) + + def onResponseRequestSettings(self, p): + """Handle the response packets for requesting settings _requestSettings()""" + logging.debug(f'onResponseRequestSetting() p:{p}') + if "routing" in p["decoded"]: + if p["decoded"]["routing"]["errorReason"] != "NONE": + print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}') + self.iface._acknowledgment.receivedNak = True + else: + self.iface._acknowledgment.receivedAck = True + if "getConfigResponse" in p["decoded"]["admin"]: + print("Config is as follows:", p["decoded"]["admin"]['getConfigResponse']) + else: + print("Module Config is as follows:", p["decoded"]["admin"]['getModuleConfigResponse']) + + def requestConfig(self, configType): + print("Requesting config from remote node (this can take a while).") + print("Be sure:") + print(" 1. There is a SECONDARY channel named 'admin'.") + print(" 2. The '--seturl' was used to configure.") + print(" 3. All devices have the same modem config. (i.e., '--ch-longfast')") + + msgIndex = configType.index + if configType.containing_type.full_name == "LocalConfig": + p = admin_pb2.AdminMessage() + p.get_config_request = msgIndex + self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings) + else: + p = admin_pb2.AdminMessage() + p.get_module_config_request = msgIndex + self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings) + self.iface.waitForAckNak() def turnOffEncryptionOnPrimaryChannel(self): """Turn off encryption on primary channel."""