diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index 1a7dd6eae..deb76fe75 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -991,20 +991,44 @@ def _sendPacket( nodeNum = self.myInfo.my_node_num else: our_exit("Warning: No myInfo found.") - # A simple hex style nodeid - we can parse this without needing the DB - elif isinstance(destinationId, str) and len(destinationId) >= 8: - # assuming some form of node id string such as !1234578 or 0x12345678 - # always grab the last 8 items of the hexadecimal id str and parse to integer - nodeNum = int(destinationId[-8:], 16) - else: - if self.nodes: - node = self.nodes.get(destinationId) - if node is None: - our_exit(f"Warning: NodeId {destinationId} not found in DB") + elif isinstance(destinationId, str): + parsed = False + if destinationId.startswith("!") or destinationId.lower().startswith("0x"): + try: + val_str = destinationId.lstrip("!").lower() + if val_str.startswith("0x"): + val_str = val_str[2:] + if len(val_str) > 8: + val_str = val_str[-8:] + nodeNum = int(val_str, 16) + parsed = True + except ValueError: + pass + elif destinationId.isdigit(): + try: + if len(destinationId) == 8: + nodeNum = int(destinationId, 16) + else: + nodeNum = int(destinationId) + parsed = True + except ValueError: + pass + elif len(destinationId) >= 8: + try: + nodeNum = int(destinationId[-8:], 16) + parsed = True + except ValueError: + pass + + if not parsed: + if self.nodes: + node = self.nodes.get(destinationId) + if node is None: + our_exit(f"Warning: NodeId {destinationId} not found in DB") + else: + nodeNum = node["num"] else: - nodeNum = node["num"] - else: - logger.warning("Warning: There were no self.nodes.") + logger.warning("Warning: There were no self.nodes.") meshPacket.to = nodeNum meshPacket.want_ack = wantAck diff --git a/meshtastic/tests/test_mesh_interface.py b/meshtastic/tests/test_mesh_interface.py index 7f0a9f5b1..a07545b1c 100644 --- a/meshtastic/tests/test_mesh_interface.py +++ b/meshtastic/tests/test_mesh_interface.py @@ -16,7 +16,12 @@ from ..slog import LogSet from ..powermon import SimPowerSupply except ImportError: - pytest.skip("Can't import LogSet or SimPowerSupply", allow_module_level=True) + import sys + from unittest.mock import MagicMock + sys.modules['meshtastic.slog'] = MagicMock() + sys.modules['meshtastic.powermon'] = MagicMock() + LogSet = MagicMock() + SimPowerSupply = MagicMock() # TODO # from ..config import Config @@ -443,6 +448,40 @@ def test_sendPacket_with_destination_starting_with_a_bang(caplog): iface._sendPacket(meshPacket, destinationId="!1234") assert re.search(r"Not sending packet", caplog.text, re.MULTILINE) +@pytest.mark.unit +@pytest.mark.usefixtures("reset_mt_config") +def test_sendPacket_parsing(caplog): + """Test _sendPacket() parsing of explicit hex, implicit hex, and decimal node IDs""" + iface = MeshInterface(noProto=True) + + # Test valid explicit hex + p = iface._sendPacket(mesh_pb2.MeshPacket(), destinationId="!0x1234567") + assert p.to == 19088743 + + # Test implicit hex (>= 8 chars) + p = iface._sendPacket(mesh_pb2.MeshPacket(), destinationId="abcdef12") + assert p.to == 2882400018 + + # Test implicit hex backward compat (exactly 8 digits) + p = iface._sendPacket(mesh_pb2.MeshPacket(), destinationId="12345678") + assert p.to == 305419896 + + # Test decimal (>8 digits) + p = iface._sendPacket(mesh_pb2.MeshPacket(), destinationId="305419896") + assert p.to == 305419896 + + # Test decimal (short) + p = iface._sendPacket(mesh_pb2.MeshPacket(), destinationId="1234567") + assert p.to == 1234567 + + # Test explicit short hex + p = iface._sendPacket(mesh_pb2.MeshPacket(), destinationId="!123") + assert p.to == 291 + + # Test invalid falls back to DB + iface.nodes = {"Bob": {"num": 999}} + p = iface._sendPacket(mesh_pb2.MeshPacket(), destinationId="Bob") + assert p.to == 999 @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config")