diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 0ef16be8..51162e20 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -251,6 +251,9 @@ def onConnected(interface): print("Connected to radio") if args.setlat or args.setlon or args.setalt: + if args.dest != BROADCAST_ADDR: + print("Setting latitude, longitude, and altitude of remote nodes is not supported.") + return closeNow = True alt = 0 @@ -599,6 +602,9 @@ def onConnected(interface): print("Writing modified configuration to device") if args.export_config: + if args.dest != BROADCAST_ADDR: + print("Exporting configuration of remote nodes is not supported.") + return # export the configuration (the opposite of '--configure') closeNow = True export_config(interface) @@ -801,10 +807,14 @@ def setSimpleConfig(modem_preset): return interface.showNodes() - if args.qr: + if args.qr or args.qr_all: closeNow = True - url = interface.localNode.getURL(includeAll=False) - print(f"Primary channel URL {url}") + url = interface.getNode(args.dest, True).getURL(includeAll=args.qr_all) + if args.qr_all: + urldesc = "Complete URL (includes all channels)" + else: + urldesc = "Primary channel URL" + print(f"{urldesc}: {url}") qr = pyqrcode.create(url) print(qr.terminal()) @@ -813,6 +823,9 @@ def setSimpleConfig(modem_preset): have_tunnel = platform.system() == "Linux" if have_tunnel and args.tunnel: + if args.dest != BROADCAST_ADDR: + print("A tunnel can only be created using the local node.") + return # pylint: disable=C0415 from . import tunnel @@ -1154,7 +1167,16 @@ def initParser(): group.add_argument( "--qr", - help="Display the QR code that corresponds to the current channel", + help=( + "Display a QR code for the node's primary channel (or all channels with --qr-all). " + "Also shows the shareable channel URL." + ), + action="store_true", + ) + + group.add_argument( + "--qr-all", + help="Display a QR code and URL for all of the node's channels.", action="store_true", ) diff --git a/meshtastic/node.py b/meshtastic/node.py index 02f726a7..0f6fb995 100644 --- a/meshtastic/node.py +++ b/meshtastic/node.py @@ -313,6 +313,8 @@ def getURL(self, includeAll: bool = True): ): channelSet.settings.append(c.settings) + if len(self.localConfig.ListFields()) == 0: + self.requestConfig(self.localConfig.DESCRIPTOR.fields_by_name.get('lora')) channelSet.lora_config.CopyFrom(self.localConfig.lora) some_bytes = channelSet.SerializeToString() s = base64.urlsafe_b64encode(some_bytes).decode("ascii") diff --git a/meshtastic/tests/test_mesh_interface.py b/meshtastic/tests/test_mesh_interface.py index 7de9c923..dd7d2385 100644 --- a/meshtastic/tests/test_mesh_interface.py +++ b/meshtastic/tests/test_mesh_interface.py @@ -6,7 +6,7 @@ import pytest -from .. import mesh_pb2, BROADCAST_ADDR, LOCAL_ADDR +from .. import mesh_pb2, config_pb2, BROADCAST_ADDR, LOCAL_ADDR from ..mesh_interface import MeshInterface from ..node import Node @@ -36,12 +36,15 @@ def test_MeshInterface(capsys): "lastHeard": 1640204888, } + iface.nodes = {NODE_ID: node} iface.nodesByNum = {NODE_NUM: node} myInfo = MagicMock() iface.myInfo = myInfo + iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) + iface.showInfo() iface.localNode.showInfo() iface.showNodes() diff --git a/meshtastic/tests/test_node.py b/meshtastic/tests/test_node.py index 0bbba1d2..5bacd9d6 100644 --- a/meshtastic/tests/test_node.py +++ b/meshtastic/tests/test_node.py @@ -6,7 +6,7 @@ import pytest -# from ..admin_pb2 import AdminMessage +from .. import localonly_pb2, config_pb2 from ..channel_pb2 import Channel # pylint: disable=E0611 from ..node import Node from ..serial_interface import SerialInterface @@ -19,21 +19,26 @@ # from ..util import Timeout -# TODO -# @pytest.mark.unit -# def test_node(capsys): -# """Test that we can instantiate a Node""" -# anode = Node('foo', 'bar') -# radioConfig = RadioConfig() -# anode.radioConfig = radioConfig -# anode.showChannels() -# anode.showInfo() -# out, err = capsys.readouterr() -# assert re.search(r'Preferences', out) -# assert re.search(r'Channels', out) -# assert re.search(r'Primary channel URL', out) -# assert err == '' - +@pytest.mark.unit +def test_node(capsys): + """Test that we can instantiate a Node""" + iface = MagicMock(autospec=SerialInterface) + with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo: + mo.localNode.getChannelByName.return_value = None + mo.myInfo.max_channels = 8 + anode = Node(mo, "bar", noProto=True) + lc = localonly_pb2.LocalConfig() + anode.localConfig = lc + lc.lora.CopyFrom(config_pb2.Config.LoRaConfig()) + anode.moduleConfig = localonly_pb2.LocalModuleConfig() + anode.showInfo() + out, err = capsys.readouterr() + assert re.search(r'Preferences', out) + assert re.search(r'Module preferences', out) + assert re.search(r'Channels', out) + assert re.search(r'Primary channel URL', out) + assert not re.search(r'remote node', out) + assert err == '' # TODO # @pytest.mark.unit diff --git a/meshtastic/tests/test_serial_interface.py b/meshtastic/tests/test_serial_interface.py index 4fc52bcb..739380c6 100644 --- a/meshtastic/tests/test_serial_interface.py +++ b/meshtastic/tests/test_serial_interface.py @@ -6,6 +6,7 @@ import pytest from ..serial_interface import SerialInterface +from .. import config_pb2 @pytest.mark.unit @@ -20,6 +21,7 @@ def test_SerialInterface_single_port( ): """Test that we can instantiate a SerialInterface with a single port""" iface = SerialInterface(noProto=True) + iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) iface.showInfo() iface.localNode.showInfo() iface.close() diff --git a/meshtastic/tests/test_tcp_interface.py b/meshtastic/tests/test_tcp_interface.py index 9084e1d3..03d317c0 100644 --- a/meshtastic/tests/test_tcp_interface.py +++ b/meshtastic/tests/test_tcp_interface.py @@ -5,6 +5,7 @@ import pytest +from .. import config_pb2 from ..tcp_interface import TCPInterface @@ -13,6 +14,7 @@ def test_TCPInterface(capsys): """Test that we can instantiate a TCPInterface""" with patch("socket.socket") as mock_socket: iface = TCPInterface(hostname="localhost", noProto=True) + iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig()) iface.myConnect() iface.showInfo() iface.localNode.showInfo()