From 579383cd5ac84b714c6623a8578b9b88ecc30085 Mon Sep 17 00:00:00 2001 From: Mike Hornung Date: Sun, 1 Dec 2024 23:33:47 -0800 Subject: [PATCH 1/2] Add new channels from an add URL with the new --ch-add-url option --- meshtastic/__main__.py | 13 +++++++++- meshtastic/node.py | 58 ++++++++++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index f5390d53..6aa906c7 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -680,6 +680,10 @@ def onConnected(interface): # handle changing channels + if args.ch_add_url: + closeNow = True + interface.getNode(args.dest, **getNode_kwargs).setURL(args.ch_add_url, addOnly=True) + if args.ch_add: channelIndex = mt_config.channel_index if channelIndex is not None: @@ -1443,7 +1447,7 @@ def addConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: "--set-ham", help="Set licensed Ham ID and turn off encryption", action="store" ) - group.add_argument("--seturl", help="Set a channel URL", action="store") + group.add_argument("--seturl", help="Set all channels with a URL", action="store") return parser @@ -1461,6 +1465,13 @@ def addChannelConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPa default=None, ) + group.add_argument( + "--ch-add-url", + help="Add secondary channels from a supplied URL", + metavar="URL", + default=None, + ) + group.add_argument( "--ch-del", help="Delete the ch-index channel", action="store_true" ) diff --git a/meshtastic/node.py b/meshtastic/node.py index fa02c6a3..742b5826 100644 --- a/meshtastic/node.py +++ b/meshtastic/node.py @@ -12,6 +12,7 @@ Timeout, camel_to_snake, fromPSK, + genPSK256, our_exit, pskToString, stripnl, @@ -337,14 +338,19 @@ def getURL(self, includeAll: bool = True): s = s.replace("=", "").replace("+", "-").replace("/", "_") return f"https://meshtastic.org/e/#{s}" - def setURL(self, url): + def setURL(self, url, addOnly: bool = False): """Set mesh network URL""" if self.localConfig is None: our_exit("Warning: No Config has been read") # URLs are of the form https://meshtastic.org/d/#{base64_channel_set} # Split on '/#' to find the base64 encoded channel settings - splitURL = url.split("/#") + if addOnly: + splitURL = url.split("/?add=true#") + else: + splitURL = url.split("/#") + if len(splitURL) == 1: + our_exit(f"Warning: Invalid URL '{url}'") b64 = splitURL[-1] # We normally strip padding to make for a shorter URL, but the python parser doesn't like @@ -361,20 +367,40 @@ def setURL(self, url): if len(channelSet.settings) == 0: our_exit("Warning: There were no settings.") - i = 0 - for chs in channelSet.settings: - ch = channel_pb2.Channel() - ch.role = ( - channel_pb2.Channel.Role.PRIMARY - if i == 0 - else channel_pb2.Channel.Role.SECONDARY - ) - ch.index = i - ch.settings.CopyFrom(chs) - self.channels[ch.index] = ch - logging.debug(f"Channel i:{i} ch:{ch}") - self.writeChannel(ch.index) - i = i + 1 + if addOnly: + # Add new channels with names not already present + # Don't change existing channels + for ch in channelSet.settings: + channelExists = self.getChannelByName(ch.name) + if channelExists or ch.name == "": + print("Ignoring existing channel from add URL") + next + else: + newChannel = self.getDisabledChannel() + if not newChannel: + our_exit("Warning: No free channels were found") + chs = channel_pb2.ChannelSettings() + chs.name = ch.name + chs.psk = ch.psk + newChannel.settings.CopyFrom(chs) + newChannel.role = channel_pb2.Channel.Role.SECONDARY + print(f"Adding new channel '{ch.name}' to device") + self.writeChannel(newChannel.index) + else: + i = 0 + for chs in channelSet.settings: + ch = channel_pb2.Channel() + ch.role = ( + channel_pb2.Channel.Role.PRIMARY + if i == 0 + else channel_pb2.Channel.Role.SECONDARY + ) + ch.index = i + ch.settings.CopyFrom(chs) + self.channels[ch.index] = ch + logging.debug(f"Channel i:{i} ch:{ch}") + self.writeChannel(ch.index) + i = i + 1 p = admin_pb2.AdminMessage() p.set_config.lora.CopyFrom(channelSet.lora_config) From 84bec5a7c4a8ed9133dbb54dfcd763b03550e8be Mon Sep 17 00:00:00 2001 From: Ian McEwen Date: Wed, 19 Feb 2025 10:15:27 -0700 Subject: [PATCH 2/2] fix up misc. lint/type/test issues, streamline, align argument names and groupings --- meshtastic/__main__.py | 26 ++++++++++++++++---------- meshtastic/node.py | 35 +++++++++++++++-------------------- meshtastic/tests/test_node.py | 4 ++-- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 6aa906c7..6c34f903 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -674,9 +674,9 @@ def onConnected(interface): closeNow = True export_config(interface) - if args.seturl: + if args.ch_set_url: closeNow = True - interface.getNode(args.dest, **getNode_kwargs).setURL(args.seturl) + interface.getNode(args.dest, **getNode_kwargs).setURL(args.ch_set_url, addOnly=False) # handle changing channels @@ -1447,7 +1447,20 @@ def addConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: "--set-ham", help="Set licensed Ham ID and turn off encryption", action="store" ) - group.add_argument("--seturl", help="Set all channels with a URL", action="store") + group.add_argument( + "--ch-set-url", "--seturl", + help="Set all channels and set LoRa config from a supplied URL", + metavar="URL", + action="store" + ) + + group.add_argument( + "--ch-add-url", + help="Add secondary channels and set LoRa config from a supplied URL", + metavar="URL", + default=None, + ) + return parser @@ -1465,13 +1478,6 @@ def addChannelConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPa default=None, ) - group.add_argument( - "--ch-add-url", - help="Add secondary channels from a supplied URL", - metavar="URL", - default=None, - ) - group.add_argument( "--ch-del", help="Delete the ch-index channel", action="store_true" ) diff --git a/meshtastic/node.py b/meshtastic/node.py index 742b5826..f3fd1a97 100644 --- a/meshtastic/node.py +++ b/meshtastic/node.py @@ -12,7 +12,6 @@ Timeout, camel_to_snake, fromPSK, - genPSK256, our_exit, pskToString, stripnl, @@ -338,10 +337,10 @@ def getURL(self, includeAll: bool = True): s = s.replace("=", "").replace("+", "-").replace("/", "_") return f"https://meshtastic.org/e/#{s}" - def setURL(self, url, addOnly: bool = False): + def setURL(self, url: str, addOnly: bool = False): """Set mesh network URL""" - if self.localConfig is None: - our_exit("Warning: No Config has been read") + if self.localConfig is None or self.channels is None: + our_exit("Warning: config or channels not loaded") # URLs are of the form https://meshtastic.org/d/#{base64_channel_set} # Split on '/#' to find the base64 encoded channel settings @@ -370,22 +369,18 @@ def setURL(self, url, addOnly: bool = False): if addOnly: # Add new channels with names not already present # Don't change existing channels - for ch in channelSet.settings: - channelExists = self.getChannelByName(ch.name) - if channelExists or ch.name == "": - print("Ignoring existing channel from add URL") - next - else: - newChannel = self.getDisabledChannel() - if not newChannel: - our_exit("Warning: No free channels were found") - chs = channel_pb2.ChannelSettings() - chs.name = ch.name - chs.psk = ch.psk - newChannel.settings.CopyFrom(chs) - newChannel.role = channel_pb2.Channel.Role.SECONDARY - print(f"Adding new channel '{ch.name}' to device") - self.writeChannel(newChannel.index) + for chs in channelSet.settings: + channelExists = self.getChannelByName(chs.name) + if channelExists or chs.name == "": + print(f"Ignoring existing or empty channel \"{chs.name}\" from add URL") + continue + ch = self.getDisabledChannel() + if not ch: + our_exit("Warning: No free channels were found") + ch.settings.CopyFrom(chs) + ch.role = channel_pb2.Channel.Role.SECONDARY + print(f"Adding new channel '{chs.name}' to device") + self.writeChannel(ch.index) else: i = 0 for chs in channelSet.settings: diff --git a/meshtastic/tests/test_node.py b/meshtastic/tests/test_node.py index 61ab84a7..9e824d7b 100644 --- a/meshtastic/tests/test_node.py +++ b/meshtastic/tests/test_node.py @@ -270,7 +270,7 @@ def test_setURL_empty_url(capsys): assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() - assert re.search(r"Warning: There were no settings.", out, re.MULTILINE) + assert re.search(r"Warning: config or channels not loaded", out, re.MULTILINE) assert err == "" @@ -304,7 +304,7 @@ def test_setURL_valid_URL_but_no_settings(capsys): assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 out, err = capsys.readouterr() - assert re.search(r"Warning: There were no settings", out, re.MULTILINE) + assert re.search(r"Warning: config or channels not loaded", out, re.MULTILINE) assert err == ""