Skip to content

Commit

Permalink
Merge pull request #665 from lysol/remote-admin-retry
Browse files Browse the repository at this point in the history
Retry admin channel setting retrieval and add configurable timeout
  • Loading branch information
ianmcorvidae authored Sep 18, 2024
2 parents 584a14f + 2026212 commit 951edfe
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 57 deletions.
105 changes: 63 additions & 42 deletions meshtastic/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ def onConnected(interface):
try:
args = mt_config.args

# convenient place to store any keyword args we pass to getNode
getNode_kwargs = {
"requestChannelAttempts": args.channel_fetch_attempts,
"timeout": args.timeout
}

# do not print this line if we are exporting the config
if not args.export_config:
print("Connected to radio")
Expand Down Expand Up @@ -324,14 +330,14 @@ def onConnected(interface):
print(f"Setting device owner to {args.set_owner}")
else: # short name only
print(f"Setting device owner short to {args.set_owner_short}")
interface.getNode(args.dest, False).setOwner(long_name=args.set_owner, short_name=args.set_owner_short)
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(long_name=args.set_owner, short_name=args.set_owner_short)

# TODO: add to export-config and configure
if args.set_canned_message:
closeNow = True
waitForAckNak = True
print(f"Setting canned plugin message to {args.set_canned_message}")
interface.getNode(args.dest, False).set_canned_message(
interface.getNode(args.dest, False, **getNode_kwargs).set_canned_message(
args.set_canned_message
)

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

if args.pos_fields:
# If --pos-fields invoked with args, set position fields
closeNow = True
positionConfig = interface.getNode(args.dest).localConfig.position
positionConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig.position
allFields = 0

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

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

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

if args.reboot:
closeNow = True
waitForAckNak = True
interface.getNode(args.dest, False).reboot()
interface.getNode(args.dest, False, **getNode_kwargs).reboot()

if args.reboot_ota:
closeNow = True
waitForAckNak = True
interface.getNode(args.dest, False).rebootOTA()
interface.getNode(args.dest, False, **getNode_kwargs).rebootOTA()

if args.enter_dfu:
closeNow = True
waitForAckNak = True
interface.getNode(args.dest, False).enterDFUMode()
interface.getNode(args.dest, False, **getNode_kwargs).enterDFUMode()

if args.shutdown:
closeNow = True
waitForAckNak = True
interface.getNode(args.dest, False).shutdown()
interface.getNode(args.dest, False, **getNode_kwargs).shutdown()

if args.device_metadata:
closeNow = True
interface.getNode(args.dest, False).getMetadata()
interface.getNode(args.dest, False, **getNode_kwargs).getMetadata()

if args.begin_edit:
closeNow = True
interface.getNode(args.dest, False).beginSettingsTransaction()
interface.getNode(args.dest, False, **getNode_kwargs).beginSettingsTransaction()

if args.commit_edit:
closeNow = True
interface.getNode(args.dest, False).commitSettingsTransaction()
interface.getNode(args.dest, False, **getNode_kwargs).commitSettingsTransaction()

if args.factory_reset or args.factory_reset_device:
closeNow = True
waitForAckNak = True

full = bool(args.factory_reset_device)
interface.getNode(args.dest, False).factoryReset(full=full)
interface.getNode(args.dest, False, **getNode_kwargs).factoryReset(full=full)

if args.remove_node:
closeNow = True
waitForAckNak = True
interface.getNode(args.dest, False).removeNode(args.remove_node)
interface.getNode(args.dest, False, **getNode_kwargs).removeNode(args.remove_node)

if args.reset_nodedb:
closeNow = True
waitForAckNak = True
interface.getNode(args.dest, False).resetNodeDb()
interface.getNode(args.dest, False, **getNode_kwargs).resetNodeDb()

if args.sendtext:
closeNow = True
Expand All @@ -444,7 +451,7 @@ def onConnected(interface):
args.dest,
wantAck=True,
channelIndex=channelIndex,
onResponse=interface.getNode(args.dest, False).onAckNak,
onResponse=interface.getNode(args.dest, False, **getNode_kwargs).onAckNak,
)
else:
meshtastic.util.our_exit(
Expand Down Expand Up @@ -535,7 +542,7 @@ def onConnected(interface):
if args.set:
closeNow = True
waitForAckNak = True
node = interface.getNode(args.dest, False)
node = interface.getNode(args.dest, False, **getNode_kwargs)

# Handle the int/float/bool arguments
pref = None
Expand Down Expand Up @@ -574,19 +581,19 @@ def onConnected(interface):
configuration = yaml.safe_load(file)
closeNow = True

interface.getNode(args.dest, False).beginSettingsTransaction()
interface.getNode(args.dest, False, **getNode_kwargs).beginSettingsTransaction()

if "owner" in configuration:
print(f"Setting device owner to {configuration['owner']}")
waitForAckNak = True
interface.getNode(args.dest, False).setOwner(configuration["owner"])
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(configuration["owner"])

if "owner_short" in configuration:
print(
f"Setting device owner short to {configuration['owner_short']}"
)
waitForAckNak = True
interface.getNode(args.dest, False).setOwner(
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
long_name=None, short_name=configuration["owner_short"]
)

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

if "channel_url" in configuration:
print("Setting channel url to", configuration["channel_url"])
interface.getNode(args.dest).setURL(configuration["channel_url"])
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channel_url"])

if "channelUrl" in configuration:
print("Setting channel url to", configuration["channelUrl"])
interface.getNode(args.dest).setURL(configuration["channelUrl"])
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channelUrl"])

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

if "config" in configuration:
localConfig = interface.getNode(args.dest).localConfig
localConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig
for section in configuration["config"]:
traverseConfig(
section, configuration["config"][section], localConfig
)
interface.getNode(args.dest).writeConfig(
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
meshtastic.util.camel_to_snake(section)
)

if "module_config" in configuration:
moduleConfig = interface.getNode(args.dest).moduleConfig
moduleConfig = interface.getNode(args.dest, **getNode_kwargs).moduleConfig
for section in configuration["module_config"]:
traverseConfig(
section,
configuration["module_config"][section],
moduleConfig,
)
interface.getNode(args.dest).writeConfig(
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
meshtastic.util.camel_to_snake(section)
)

interface.getNode(args.dest, False).commitSettingsTransaction()
interface.getNode(args.dest, False, **getNode_kwargs).commitSettingsTransaction()
print("Writing modified configuration to device")

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

if args.seturl:
closeNow = True
interface.getNode(args.dest).setURL(args.seturl)
interface.getNode(args.dest, **getNode_kwargs).setURL(args.seturl)

# handle changing channels

Expand All @@ -680,7 +687,7 @@ def onConnected(interface):
meshtastic.util.our_exit(
"Warning: Channel name must be shorter. Channel not added."
)
n = interface.getNode(args.dest)
n = interface.getNode(args.dest, **getNode_kwargs)
ch = n.getChannelByName(args.ch_add)
if ch:
meshtastic.util.our_exit(
Expand Down Expand Up @@ -719,7 +726,7 @@ def onConnected(interface):
)
else:
print(f"Deleting channel {channelIndex}")
ch = interface.getNode(args.dest).deleteChannel(channelIndex)
ch = interface.getNode(args.dest, **getNode_kwargs).deleteChannel(channelIndex)

def setSimpleConfig(modem_preset):
"""Set one of the simple modem_config"""
Expand All @@ -729,7 +736,7 @@ def setSimpleConfig(modem_preset):
"Warning: Cannot set modem preset for non-primary channel", 1
)
# Overwrite modem_preset
node = interface.getNode(args.dest, False)
node = interface.getNode(args.dest, False, **getNode_kwargs)
if len(node.localConfig.ListFields()) == 0:
node.requestConfig(node.localConfig.DESCRIPTOR.fields_by_name.get("lora"))
node.localConfig.lora.modem_preset = modem_preset
Expand Down Expand Up @@ -763,7 +770,7 @@ def setSimpleConfig(modem_preset):
channelIndex = mt_config.channel_index
if channelIndex is None:
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
node = interface.getNode(args.dest)
node = interface.getNode(args.dest, **getNode_kwargs)
ch = node.channels[channelIndex]

if args.ch_enable or args.ch_disable:
Expand Down Expand Up @@ -827,20 +834,20 @@ def setSimpleConfig(modem_preset):
if args.get_canned_message:
closeNow = True
print("")
interface.getNode(args.dest).get_canned_message()
interface.getNode(args.dest, **getNode_kwargs).get_canned_message()

if args.get_ringtone:
closeNow = True
print("")
interface.getNode(args.dest).get_ringtone()
interface.getNode(args.dest, **getNode_kwargs).get_ringtone()

if args.info:
print("")
# If we aren't trying to talk to our local node, don't show it
if args.dest == BROADCAST_ADDR:
interface.showInfo()
print("")
interface.getNode(args.dest).showInfo()
interface.getNode(args.dest, **getNode_kwargs).showInfo()
closeNow = True
print("")
pypi_version = meshtastic.util.check_if_newer_version()
Expand All @@ -857,7 +864,7 @@ def setSimpleConfig(modem_preset):

if args.get:
closeNow = True
node = interface.getNode(args.dest, False)
node = interface.getNode(args.dest, False, **getNode_kwargs)
for pref in args.get:
found = getPref(node, pref[0])

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

if args.qr or args.qr_all:
closeNow = True
url = interface.getNode(args.dest, True).getURL(includeAll=args.qr_all)
url = interface.getNode(args.dest, True, **getNode_kwargs).getURL(includeAll=args.qr_all)
if args.qr_all:
urldesc = "Complete URL (includes all channels)"
else:
Expand Down Expand Up @@ -928,7 +935,7 @@ def setSimpleConfig(modem_preset):
print(
f"Waiting for an acknowledgment from remote node (this could take a while)"
)
interface.getNode(args.dest, False).iface.waitForAckNak()
interface.getNode(args.dest, False, **getNode_kwargs).iface.waitForAckNak()

if args.wait_to_disconnect:
print(f"Waiting {args.wait_to_disconnect} seconds before disconnecting")
Expand Down Expand Up @@ -1408,6 +1415,20 @@ def initParser():
action="append",
)

group.add_argument(
"--channel-fetch-attempts",
help=("Attempt to retrieve channel settings for --ch-set this many times before giving up."),
default=3,
type=int,
)

group.add_argument(
"--timeout",
help="How long to wait for replies",
default=300,
type=int,
)

group.add_argument(
"--ch-vlongslow",
help="Change to the very long-range and slow channel",
Expand Down
22 changes: 18 additions & 4 deletions meshtastic/mesh_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,19 +315,33 @@ def getTimeAgo(ts) -> Optional[str]:
return table

def getNode(
self, nodeId: str, requestChannels: bool = True
self, nodeId: str, requestChannels: bool = True, requestChannelAttempts: int = 3, timeout: int = 300
) -> meshtastic.node.Node:
"""Return a node object which contains device settings and channel info"""
if nodeId in (LOCAL_ADDR, BROADCAST_ADDR):
return self.localNode
else:
n = meshtastic.node.Node(self, nodeId)
n = meshtastic.node.Node(self, nodeId, timeout=timeout)
# Only request device settings and channel info when necessary
if requestChannels:
logging.debug("About to requestChannels")
n.requestChannels()
if not n.waitForConfig():
our_exit("Error: Timed out waiting for channels")
retries_left = requestChannelAttempts
last_index: int = 0
while retries_left > 0:
retries_left -= 1
if not n.waitForConfig():
new_index: int = len(n.partialChannels) if n.partialChannels else 0
# each time we get a new channel, reset the counter
if new_index != last_index:
retries_left = requestChannelAttempts - 1
if retries_left <= 0:
our_exit(f"Error: Timed out waiting for channels, giving up")
print("Timed out trying to retrieve channel info, retrying")
n.requestChannels(startingIndex=new_index)
last_index = new_index
else:
break
return n

def sendText(
Expand Down
Loading

0 comments on commit 951edfe

Please sign in to comment.