Skip to content

Commit d08be44

Browse files
committed
feat: Add --request-nodeinfo CLI flag
1 parent 358c09e commit d08be44

3 files changed

Lines changed: 77 additions & 0 deletions

File tree

‎meshtastic/__main__.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,21 @@ def onConnected(interface):
642642
telemetryType=telemType,
643643
)
644644

645+
if args.request_nodeinfo:
646+
if args.dest == BROADCAST_ADDR:
647+
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
648+
else:
649+
channelIndex = mt_config.channel_index or 0
650+
if checkChannel(interface, channelIndex):
651+
print(
652+
f"Sending NodeInfo request to {args.dest} on channelIndex:{channelIndex} (this could take a while)"
653+
)
654+
interface.requestNodeInfo(
655+
destinationId=args.dest,
656+
wantResponse=True,
657+
channelIndex=channelIndex,
658+
)
659+
645660
if args.request_position:
646661
if args.dest == BROADCAST_ADDR:
647662
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
@@ -2267,6 +2282,14 @@ def addRemoteActionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPar
22672282
metavar="TYPE",
22682283
)
22692284

2285+
group.add_argument(
2286+
"--request-nodeinfo",
2287+
help="Request NodeInfo (name, hardware model, etc) from a node. "
2288+
"You need to pass the destination ID as an argument with '--dest'. "
2289+
"For repeaters, the nodeNum is required.",
2290+
action="store_true",
2291+
)
2292+
22702293
group.add_argument(
22712294
"--request-position",
22722295
help="Request the position from a node. "

‎meshtastic/mesh_interface.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,35 @@ def sendData(
582582
p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit, pkiEncrypted=pkiEncrypted, publicKey=publicKey)
583583
return p
584584

585+
def requestNodeInfo(
586+
self,
587+
destinationId: Union[int, str] = BROADCAST_ADDR,
588+
wantResponse: bool = False,
589+
channelIndex: int = 0,
590+
hopLimit: Optional[int] = None,
591+
):
592+
"""Send a NodeInfo request and optionally ask for a response"""
593+
u = mesh_pb2.User()
594+
595+
if wantResponse:
596+
onResponse = self.onResponseNodeInfo
597+
else:
598+
onResponse = None
599+
600+
d = self.sendData(
601+
u,
602+
destinationId=destinationId,
603+
portNum=portnums_pb2.PortNum.NODEINFO_APP,
604+
wantResponse=wantResponse,
605+
onResponse=onResponse,
606+
channelIndex=channelIndex,
607+
hopLimit=hopLimit,
608+
)
609+
610+
if wantResponse:
611+
self.waitForNodeInfo()
612+
return d
613+
585614
def sendPosition(
586615
self,
587616
latitude: float = 0.0,
@@ -634,6 +663,13 @@ def sendPosition(
634663
self.waitForPosition()
635664
return d
636665

666+
def onResponseNodeInfo(self, p: dict):
667+
"""on response for NodeInfo"""
668+
if p["decoded"]["portnum"] == "NODEINFO_APP":
669+
self._acknowledgment.receivedNodeInfo = True
670+
if "user" in p["decoded"]:
671+
print(f"NodeInfo received:\n{p['decoded']['user']}")
672+
637673
def onResponsePosition(self, p):
638674
"""on response for position"""
639675
if p["decoded"]["portnum"] == "POSITION_APP":
@@ -1067,6 +1103,12 @@ def waitForTelemetry(self):
10671103
if not success:
10681104
raise MeshInterface.MeshInterfaceError("Timed out waiting for telemetry")
10691105

1106+
def waitForNodeInfo(self):
1107+
"""Wait for NodeInfo"""
1108+
success = self._timeout.waitForNodeInfo(self._acknowledgment)
1109+
if not success:
1110+
raise MeshInterface.MeshInterfaceError("Timed out waiting for NodeInfo")
1111+
10701112
def waitForPosition(self):
10711113
"""Wait for position"""
10721114
success = self._timeout.waitForPosition(self._acknowledgment)

‎meshtastic/util.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,16 @@ def waitForWaypoint(self, acknowledgment) -> bool:
266266
time.sleep(self.sleepInterval)
267267
return False
268268

269+
def waitForNodeInfo(self, acknowledgment) -> bool:
270+
"""Block until NodeInfo response is received. Returns True if NodeInfo response has been received."""
271+
self.reset()
272+
while time.time() < self.expireTime:
273+
if getattr(acknowledgment, "receivedNodeInfo", None):
274+
acknowledgment.reset()
275+
return True
276+
time.sleep(self.sleepInterval)
277+
return False
278+
269279
class Acknowledgment:
270280
"A class that records which type of acknowledgment was just received, if any."
271281

@@ -278,6 +288,7 @@ def __init__(self) -> None:
278288
self.receivedTelemetry = False
279289
self.receivedPosition = False
280290
self.receivedWaypoint = False
291+
self.receivedNodeInfo = False
281292

282293
def reset(self) -> None:
283294
"""reset"""
@@ -288,6 +299,7 @@ def reset(self) -> None:
288299
self.receivedTelemetry = False
289300
self.receivedPosition = False
290301
self.receivedWaypoint = False
302+
self.receivedNodeInfo = False
291303

292304

293305
class DeferredExecution:

0 commit comments

Comments
 (0)