Skip to content

Commit 951edfe

Browse files
authored
Merge pull request #665 from lysol/remote-admin-retry
Retry admin channel setting retrieval and add configurable timeout
2 parents 584a14f + 2026212 commit 951edfe

File tree

7 files changed

+146
-57
lines changed

7 files changed

+146
-57
lines changed

meshtastic/__main__.py

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,12 @@ def onConnected(interface):
273273
try:
274274
args = mt_config.args
275275

276+
# convenient place to store any keyword args we pass to getNode
277+
getNode_kwargs = {
278+
"requestChannelAttempts": args.channel_fetch_attempts,
279+
"timeout": args.timeout
280+
}
281+
276282
# do not print this line if we are exporting the config
277283
if not args.export_config:
278284
print("Connected to radio")
@@ -324,14 +330,14 @@ def onConnected(interface):
324330
print(f"Setting device owner to {args.set_owner}")
325331
else: # short name only
326332
print(f"Setting device owner short to {args.set_owner_short}")
327-
interface.getNode(args.dest, False).setOwner(long_name=args.set_owner, short_name=args.set_owner_short)
333+
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(long_name=args.set_owner, short_name=args.set_owner_short)
328334

329335
# TODO: add to export-config and configure
330336
if args.set_canned_message:
331337
closeNow = True
332338
waitForAckNak = True
333339
print(f"Setting canned plugin message to {args.set_canned_message}")
334-
interface.getNode(args.dest, False).set_canned_message(
340+
interface.getNode(args.dest, False, **getNode_kwargs).set_canned_message(
335341
args.set_canned_message
336342
)
337343

@@ -340,12 +346,12 @@ def onConnected(interface):
340346
closeNow = True
341347
waitForAckNak = True
342348
print(f"Setting ringtone to {args.set_ringtone}")
343-
interface.getNode(args.dest, False).set_ringtone(args.set_ringtone)
349+
interface.getNode(args.dest, False, **getNode_kwargs).set_ringtone(args.set_ringtone)
344350

345351
if args.pos_fields:
346352
# If --pos-fields invoked with args, set position fields
347353
closeNow = True
348-
positionConfig = interface.getNode(args.dest).localConfig.position
354+
positionConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig.position
349355
allFields = 0
350356

351357
try:
@@ -364,12 +370,12 @@ def onConnected(interface):
364370
print(f"Setting position fields to {allFields}")
365371
setPref(positionConfig, "position_flags", f"{allFields:d}")
366372
print("Writing modified preferences to device")
367-
interface.getNode(args.dest).writeConfig("position")
373+
interface.getNode(args.dest, **getNode_kwargs).writeConfig("position")
368374

369375
elif args.pos_fields is not None:
370376
# If --pos-fields invoked without args, read and display current value
371377
closeNow = True
372-
positionConfig = interface.getNode(args.dest).localConfig.position
378+
positionConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig.position
373379

374380
fieldNames = []
375381
for bit in positionConfig.PositionFlags.values():
@@ -380,57 +386,58 @@ def onConnected(interface):
380386
if args.set_ham:
381387
closeNow = True
382388
print(f"Setting Ham ID to {args.set_ham} and turning off encryption")
383-
interface.getNode(args.dest).setOwner(args.set_ham, is_licensed=True)
389+
interface.getNode(args.dest, **getNode_kwargs).setOwner(args.set_ham, is_licensed=True)
384390
# Must turn off encryption on primary channel
385-
interface.getNode(args.dest).turnOffEncryptionOnPrimaryChannel()
391+
interface.getNode(args.dest, **getNode_kwargs).turnOffEncryptionOnPrimaryChannel()
386392

387393
if args.reboot:
388394
closeNow = True
389395
waitForAckNak = True
390-
interface.getNode(args.dest, False).reboot()
396+
interface.getNode(args.dest, False, **getNode_kwargs).reboot()
391397

392398
if args.reboot_ota:
393399
closeNow = True
394400
waitForAckNak = True
395-
interface.getNode(args.dest, False).rebootOTA()
401+
interface.getNode(args.dest, False, **getNode_kwargs).rebootOTA()
396402

397403
if args.enter_dfu:
398404
closeNow = True
399405
waitForAckNak = True
400-
interface.getNode(args.dest, False).enterDFUMode()
406+
interface.getNode(args.dest, False, **getNode_kwargs).enterDFUMode()
401407

402408
if args.shutdown:
403409
closeNow = True
404410
waitForAckNak = True
405-
interface.getNode(args.dest, False).shutdown()
411+
interface.getNode(args.dest, False, **getNode_kwargs).shutdown()
406412

407413
if args.device_metadata:
408414
closeNow = True
409-
interface.getNode(args.dest, False).getMetadata()
415+
interface.getNode(args.dest, False, **getNode_kwargs).getMetadata()
410416

411417
if args.begin_edit:
412418
closeNow = True
413-
interface.getNode(args.dest, False).beginSettingsTransaction()
419+
interface.getNode(args.dest, False, **getNode_kwargs).beginSettingsTransaction()
414420

415421
if args.commit_edit:
416422
closeNow = True
417-
interface.getNode(args.dest, False).commitSettingsTransaction()
423+
interface.getNode(args.dest, False, **getNode_kwargs).commitSettingsTransaction()
418424

419425
if args.factory_reset or args.factory_reset_device:
420426
closeNow = True
421427
waitForAckNak = True
428+
422429
full = bool(args.factory_reset_device)
423-
interface.getNode(args.dest, False).factoryReset(full=full)
430+
interface.getNode(args.dest, False, **getNode_kwargs).factoryReset(full=full)
424431

425432
if args.remove_node:
426433
closeNow = True
427434
waitForAckNak = True
428-
interface.getNode(args.dest, False).removeNode(args.remove_node)
435+
interface.getNode(args.dest, False, **getNode_kwargs).removeNode(args.remove_node)
429436

430437
if args.reset_nodedb:
431438
closeNow = True
432439
waitForAckNak = True
433-
interface.getNode(args.dest, False).resetNodeDb()
440+
interface.getNode(args.dest, False, **getNode_kwargs).resetNodeDb()
434441

435442
if args.sendtext:
436443
closeNow = True
@@ -444,7 +451,7 @@ def onConnected(interface):
444451
args.dest,
445452
wantAck=True,
446453
channelIndex=channelIndex,
447-
onResponse=interface.getNode(args.dest, False).onAckNak,
454+
onResponse=interface.getNode(args.dest, False, **getNode_kwargs).onAckNak,
448455
)
449456
else:
450457
meshtastic.util.our_exit(
@@ -535,7 +542,7 @@ def onConnected(interface):
535542
if args.set:
536543
closeNow = True
537544
waitForAckNak = True
538-
node = interface.getNode(args.dest, False)
545+
node = interface.getNode(args.dest, False, **getNode_kwargs)
539546

540547
# Handle the int/float/bool arguments
541548
pref = None
@@ -574,19 +581,19 @@ def onConnected(interface):
574581
configuration = yaml.safe_load(file)
575582
closeNow = True
576583

577-
interface.getNode(args.dest, False).beginSettingsTransaction()
584+
interface.getNode(args.dest, False, **getNode_kwargs).beginSettingsTransaction()
578585

579586
if "owner" in configuration:
580587
print(f"Setting device owner to {configuration['owner']}")
581588
waitForAckNak = True
582-
interface.getNode(args.dest, False).setOwner(configuration["owner"])
589+
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(configuration["owner"])
583590

584591
if "owner_short" in configuration:
585592
print(
586593
f"Setting device owner short to {configuration['owner_short']}"
587594
)
588595
waitForAckNak = True
589-
interface.getNode(args.dest, False).setOwner(
596+
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
590597
long_name=None, short_name=configuration["owner_short"]
591598
)
592599

@@ -595,17 +602,17 @@ def onConnected(interface):
595602
f"Setting device owner short to {configuration['ownerShort']}"
596603
)
597604
waitForAckNak = True
598-
interface.getNode(args.dest, False).setOwner(
605+
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
599606
long_name=None, short_name=configuration["ownerShort"]
600607
)
601608

602609
if "channel_url" in configuration:
603610
print("Setting channel url to", configuration["channel_url"])
604-
interface.getNode(args.dest).setURL(configuration["channel_url"])
611+
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channel_url"])
605612

606613
if "channelUrl" in configuration:
607614
print("Setting channel url to", configuration["channelUrl"])
608-
interface.getNode(args.dest).setURL(configuration["channelUrl"])
615+
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channelUrl"])
609616

610617
if "location" in configuration:
611618
alt = 0
@@ -630,28 +637,28 @@ def onConnected(interface):
630637
interface.localNode.writeConfig("position")
631638

632639
if "config" in configuration:
633-
localConfig = interface.getNode(args.dest).localConfig
640+
localConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig
634641
for section in configuration["config"]:
635642
traverseConfig(
636643
section, configuration["config"][section], localConfig
637644
)
638-
interface.getNode(args.dest).writeConfig(
645+
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
639646
meshtastic.util.camel_to_snake(section)
640647
)
641648

642649
if "module_config" in configuration:
643-
moduleConfig = interface.getNode(args.dest).moduleConfig
650+
moduleConfig = interface.getNode(args.dest, **getNode_kwargs).moduleConfig
644651
for section in configuration["module_config"]:
645652
traverseConfig(
646653
section,
647654
configuration["module_config"][section],
648655
moduleConfig,
649656
)
650-
interface.getNode(args.dest).writeConfig(
657+
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
651658
meshtastic.util.camel_to_snake(section)
652659
)
653660

654-
interface.getNode(args.dest, False).commitSettingsTransaction()
661+
interface.getNode(args.dest, False, **getNode_kwargs).commitSettingsTransaction()
655662
print("Writing modified configuration to device")
656663

657664
if args.export_config:
@@ -664,7 +671,7 @@ def onConnected(interface):
664671

665672
if args.seturl:
666673
closeNow = True
667-
interface.getNode(args.dest).setURL(args.seturl)
674+
interface.getNode(args.dest, **getNode_kwargs).setURL(args.seturl)
668675

669676
# handle changing channels
670677

@@ -680,7 +687,7 @@ def onConnected(interface):
680687
meshtastic.util.our_exit(
681688
"Warning: Channel name must be shorter. Channel not added."
682689
)
683-
n = interface.getNode(args.dest)
690+
n = interface.getNode(args.dest, **getNode_kwargs)
684691
ch = n.getChannelByName(args.ch_add)
685692
if ch:
686693
meshtastic.util.our_exit(
@@ -719,7 +726,7 @@ def onConnected(interface):
719726
)
720727
else:
721728
print(f"Deleting channel {channelIndex}")
722-
ch = interface.getNode(args.dest).deleteChannel(channelIndex)
729+
ch = interface.getNode(args.dest, **getNode_kwargs).deleteChannel(channelIndex)
723730

724731
def setSimpleConfig(modem_preset):
725732
"""Set one of the simple modem_config"""
@@ -729,7 +736,7 @@ def setSimpleConfig(modem_preset):
729736
"Warning: Cannot set modem preset for non-primary channel", 1
730737
)
731738
# Overwrite modem_preset
732-
node = interface.getNode(args.dest, False)
739+
node = interface.getNode(args.dest, False, **getNode_kwargs)
733740
if len(node.localConfig.ListFields()) == 0:
734741
node.requestConfig(node.localConfig.DESCRIPTOR.fields_by_name.get("lora"))
735742
node.localConfig.lora.modem_preset = modem_preset
@@ -763,7 +770,7 @@ def setSimpleConfig(modem_preset):
763770
channelIndex = mt_config.channel_index
764771
if channelIndex is None:
765772
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
766-
node = interface.getNode(args.dest)
773+
node = interface.getNode(args.dest, **getNode_kwargs)
767774
ch = node.channels[channelIndex]
768775

769776
if args.ch_enable or args.ch_disable:
@@ -827,20 +834,20 @@ def setSimpleConfig(modem_preset):
827834
if args.get_canned_message:
828835
closeNow = True
829836
print("")
830-
interface.getNode(args.dest).get_canned_message()
837+
interface.getNode(args.dest, **getNode_kwargs).get_canned_message()
831838

832839
if args.get_ringtone:
833840
closeNow = True
834841
print("")
835-
interface.getNode(args.dest).get_ringtone()
842+
interface.getNode(args.dest, **getNode_kwargs).get_ringtone()
836843

837844
if args.info:
838845
print("")
839846
# If we aren't trying to talk to our local node, don't show it
840847
if args.dest == BROADCAST_ADDR:
841848
interface.showInfo()
842849
print("")
843-
interface.getNode(args.dest).showInfo()
850+
interface.getNode(args.dest, **getNode_kwargs).showInfo()
844851
closeNow = True
845852
print("")
846853
pypi_version = meshtastic.util.check_if_newer_version()
@@ -857,7 +864,7 @@ def setSimpleConfig(modem_preset):
857864

858865
if args.get:
859866
closeNow = True
860-
node = interface.getNode(args.dest, False)
867+
node = interface.getNode(args.dest, False, **getNode_kwargs)
861868
for pref in args.get:
862869
found = getPref(node, pref[0])
863870

@@ -873,7 +880,7 @@ def setSimpleConfig(modem_preset):
873880

874881
if args.qr or args.qr_all:
875882
closeNow = True
876-
url = interface.getNode(args.dest, True).getURL(includeAll=args.qr_all)
883+
url = interface.getNode(args.dest, True, **getNode_kwargs).getURL(includeAll=args.qr_all)
877884
if args.qr_all:
878885
urldesc = "Complete URL (includes all channels)"
879886
else:
@@ -928,7 +935,7 @@ def setSimpleConfig(modem_preset):
928935
print(
929936
f"Waiting for an acknowledgment from remote node (this could take a while)"
930937
)
931-
interface.getNode(args.dest, False).iface.waitForAckNak()
938+
interface.getNode(args.dest, False, **getNode_kwargs).iface.waitForAckNak()
932939

933940
if args.wait_to_disconnect:
934941
print(f"Waiting {args.wait_to_disconnect} seconds before disconnecting")
@@ -1408,6 +1415,20 @@ def initParser():
14081415
action="append",
14091416
)
14101417

1418+
group.add_argument(
1419+
"--channel-fetch-attempts",
1420+
help=("Attempt to retrieve channel settings for --ch-set this many times before giving up."),
1421+
default=3,
1422+
type=int,
1423+
)
1424+
1425+
group.add_argument(
1426+
"--timeout",
1427+
help="How long to wait for replies",
1428+
default=300,
1429+
type=int,
1430+
)
1431+
14111432
group.add_argument(
14121433
"--ch-vlongslow",
14131434
help="Change to the very long-range and slow channel",

meshtastic/mesh_interface.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,19 +315,33 @@ def getTimeAgo(ts) -> Optional[str]:
315315
return table
316316

317317
def getNode(
318-
self, nodeId: str, requestChannels: bool = True
318+
self, nodeId: str, requestChannels: bool = True, requestChannelAttempts: int = 3, timeout: int = 300
319319
) -> meshtastic.node.Node:
320320
"""Return a node object which contains device settings and channel info"""
321321
if nodeId in (LOCAL_ADDR, BROADCAST_ADDR):
322322
return self.localNode
323323
else:
324-
n = meshtastic.node.Node(self, nodeId)
324+
n = meshtastic.node.Node(self, nodeId, timeout=timeout)
325325
# Only request device settings and channel info when necessary
326326
if requestChannels:
327327
logging.debug("About to requestChannels")
328328
n.requestChannels()
329-
if not n.waitForConfig():
330-
our_exit("Error: Timed out waiting for channels")
329+
retries_left = requestChannelAttempts
330+
last_index: int = 0
331+
while retries_left > 0:
332+
retries_left -= 1
333+
if not n.waitForConfig():
334+
new_index: int = len(n.partialChannels) if n.partialChannels else 0
335+
# each time we get a new channel, reset the counter
336+
if new_index != last_index:
337+
retries_left = requestChannelAttempts - 1
338+
if retries_left <= 0:
339+
our_exit(f"Error: Timed out waiting for channels, giving up")
340+
print("Timed out trying to retrieve channel info, retrying")
341+
n.requestChannels(startingIndex=new_index)
342+
last_index = new_index
343+
else:
344+
break
331345
return n
332346

333347
def sendText(

0 commit comments

Comments
 (0)