Compare commits

..

30 Commits
2.0.2 ... 2.0.6

Author SHA1 Message Date
Thomas Göttgens
57719ddd5e Merge pull request #407 from GUVWAF/traceRoute
Traceroute option
2022-12-06 15:36:49 +01:00
GUVWAF
d0b8b9ff1b Forgot to convert into TRACEROUTE_APP 2022-12-06 14:18:30 +01:00
github-actions
c5c9723208 Update protobuf submodule 2022-12-06 13:16:28 +00:00
Ben Meadors
9bceaafd9c Merge pull request #406 from GUVWAF/hopLimit
Set hopLimit to the config of the device
2022-12-05 20:10:27 -06:00
GUVWAF
7d3a9178ea Add traceroute option 2022-12-05 20:36:34 +01:00
GUVWAF
2c76c0cafa Remove setting hopLimit to default
Instead set is to config of device
2022-12-05 20:04:38 +01:00
GUVWAF
82977e9ef2 Fix --ch-set help message 2022-12-05 20:02:53 +01:00
Sacha Weatherstone
7a9c25da8e Merge pull request #404 from ahmedkadd/fix-script-typo
Rename protobufs shell script to fix typo
2022-12-02 14:39:18 +11:00
Ahmed Kaddoura
342c48fb16 Rename protobufs shell script to fix typo 2022-11-30 07:58:56 -08:00
github-actions
6bc955a403 bump version 2022-11-27 12:10:59 +00:00
Ben Meadors
741ba378ab Merge pull request #402 from GUVWAF/wantResponse
Only set wantResponse for admin packets
2022-11-27 06:10:01 -06:00
GUVWAF
c1054caf4a Only set wantResponse for admin packets 2022-11-27 12:44:58 +01:00
Ben Meadors
24b97d9277 Merge pull request #401 from GUVWAF/setowner
Remove automatic short owner naming and increase to 4 characters
2022-11-26 07:56:34 -06:00
GUVWAF
868fb64857 Only set is_licensed if long_name is set 2022-11-26 11:08:00 +01:00
GUVWAF
8729e97e1b Remove automatic short owner naming and increase to 4 characters 2022-11-26 10:52:20 +01:00
github-actions
aaed54393e bump version 2022-11-22 17:03:56 +00:00
Ben Meadors
d12776bb5f Merge pull request #400 from meshtastic/transaction-for-config
Transactions for editing settings
2022-11-21 15:27:31 -06:00
Ben Meadors
7829f6afca Args 2022-11-20 20:01:50 -06:00
Ben Meadors
4bd10bc102 Add begin / edit transactions for setting updates 2022-11-20 19:46:43 -06:00
Ben Meadors
3821e02f09 Update protos 2022-11-20 19:11:38 -06:00
Ben Meadors
97689da0b4 Merge pull request #395 from GUVWAF/master
Change how admin packets to remote nodes are handled
2022-11-19 15:36:33 -06:00
GUVWAF
5c75e74bf9 Don't request config and channels if not needed.
Instead, wait for an (implicit) ACK or NAK.
Applies to admin packets set-owner, reboot,
shutdown, factory-reset and reset-nodedb.
2022-11-19 21:19:06 +01:00
GUVWAF
388a46abf4 Merge branch 'meshtastic:master' into master 2022-11-15 13:26:30 +01:00
github-actions
6b89fc81a1 bump version 2022-11-10 03:23:52 +00:00
Ben Meadors
c9b5d5d697 Update protos and add audio 2022-11-09 21:20:38 -06:00
Ben Meadors
f16dd0e737 Update url 2022-11-08 18:25:49 -06:00
Ben Meadors
5ed19eff73 Create cleanup_artifacts.yml 2022-11-05 06:52:28 -05:00
github-actions
0b3605141d bump version 2022-11-03 13:02:34 +00:00
GUVWAF
f1df14ca92 lastTried channel should be its index 2022-11-03 09:28:15 +01:00
GUVWAF
7aff5e9ee5 Unset wantAck if you set wantResponse for Admin 2022-11-02 09:49:20 +01:00
17 changed files with 389 additions and 170 deletions

20
.github/workflows/cleanup_artifacts.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Remove old artifacts
on:
schedule:
# Every day at 1am
- cron: '0 1 * * *'
workflow_dispatch:
jobs:
remove-old-artifacts:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Remove old artifacts
uses: c-hive/gha-remove-artifacts@v1
with:
age: '1 month'
skip-tags: true

View File

@@ -183,5 +183,6 @@ protocols = {
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing), portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol("telemetry", telemetry_pb2.Telemetry), portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol("telemetry", telemetry_pb2.Telemetry),
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol("remotehw", remote_hardware_pb2.HardwareMessage), portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol("remotehw", remote_hardware_pb2.HardwareMessage),
portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed) portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed),
portnums_pb2.PortNum.TRACEROUTE_APP: KnownProtocol("traceroute", mesh_pb2.RouteDiscovery)
} }

View File

@@ -177,6 +177,7 @@ def setPref(config, comp_name, valStr):
def onConnected(interface): def onConnected(interface):
"""Callback invoked when we connect to a radio""" """Callback invoked when we connect to a radio"""
closeNow = False # Should we drop the connection after we finish? closeNow = False # Should we drop the connection after we finish?
waitForAckNak = False # Should we wait for an acknowledgment if we send to a remote node?
try: try:
our_globals = Globals.getInstance() our_globals = Globals.getInstance()
args = our_globals.get_args() args = our_globals.get_args()
@@ -191,6 +192,7 @@ def onConnected(interface):
alt = 0 alt = 0
lat = 0.0 lat = 0.0
lon = 0.0 lon = 0.0
# TODO: use getNode(args.dest) to be able to set it for a remote node
localConfig = interface.localNode.localConfig localConfig = interface.localNode.localConfig
if args.setalt: if args.setalt:
alt = int(args.setalt) alt = int(args.setalt)
@@ -215,19 +217,22 @@ def onConnected(interface):
if args.set_owner: if args.set_owner:
closeNow = True closeNow = True
waitForAckNak = True
print(f"Setting device owner to {args.set_owner}") print(f"Setting device owner to {args.set_owner}")
interface.getNode(args.dest).setOwner(args.set_owner) interface.getNode(args.dest, False).setOwner(args.set_owner)
if args.set_owner_short: if args.set_owner_short:
closeNow = True closeNow = True
waitForAckNak = True
print(f"Setting device owner short to {args.set_owner_short}") print(f"Setting device owner short to {args.set_owner_short}")
interface.getNode(args.dest).setOwner(long_name=None, short_name=args.set_owner_short) interface.getNode(args.dest, False).setOwner(long_name=None, short_name=args.set_owner_short)
# TODO: add to export-config and configure # TODO: add to export-config and configure
if args.set_canned_message: if args.set_canned_message:
closeNow = True closeNow = True
waitForAckNak = True
print(f"Setting canned plugin message to {args.set_canned_message}") print(f"Setting canned plugin message to {args.set_canned_message}")
interface.getNode(args.dest).set_canned_message(args.set_canned_message) interface.getNode(args.dest, False).set_canned_message(args.set_canned_message)
if args.pos_fields: if args.pos_fields:
# If --pos-fields invoked with args, set position fields # If --pos-fields invoked with args, set position fields
@@ -271,27 +276,40 @@ def onConnected(interface):
if args.reboot: if args.reboot:
closeNow = True closeNow = True
interface.getNode(args.dest).reboot() waitForAckNak = True
interface.getNode(args.dest, False).reboot()
if args.reboot_ota: if args.reboot_ota:
closeNow = True closeNow = True
interface.getNode(args.dest).rebootOTA(); waitForAckNak = True
interface.getNode(args.dest, False).rebootOTA();
if args.shutdown: if args.shutdown:
closeNow = True closeNow = True
interface.getNode(args.dest).shutdown() waitForAckNak = True
interface.getNode(args.dest, False).shutdown()
if args.device_metadata: if args.device_metadata:
closeNow = True closeNow = True
interface.getNode(args.dest).getMetadata() interface.getNode(args.dest).getMetadata()
if args.begin_edit:
closeNow = True
interface.getNode(args.dest, False).beginSettingsTransaction()
if args.commit_edit:
closeNow = True
interface.getNode(args.dest, False).commitSettingsTransaction()
if args.factory_reset: if args.factory_reset:
closeNow = True closeNow = True
interface.getNode(args.dest).factoryReset() waitForAckNak = True
interface.getNode(args.dest, False).factoryReset()
if args.reset_nodedb: if args.reset_nodedb:
closeNow = True closeNow = True
interface.getNode(args.dest).resetNodeDb() waitForAckNak = True
interface.getNode(args.dest, False).resetNodeDb()
if args.sendtext: if args.sendtext:
closeNow = True closeNow = True
@@ -312,6 +330,13 @@ def onConnected(interface):
interface.sendData(payload, args.dest, portNum=portnums_pb2.PortNum.REPLY_APP, interface.sendData(payload, args.dest, portNum=portnums_pb2.PortNum.REPLY_APP,
wantAck=True, wantResponse=True) wantAck=True, wantResponse=True)
if args.traceroute:
loraConfig = getattr(interface.localNode.localConfig, 'lora')
hopLimit = getattr(loraConfig, 'hop_limit')
dest = str(args.traceroute)
print(f"Sending traceroute request to {dest} (this could take a while)")
interface.sendTraceRoute(dest, hopLimit)
if args.gpio_wrb or args.gpio_rd or args.gpio_watch: if args.gpio_wrb or args.gpio_rd or args.gpio_watch:
if args.dest == BROADCAST_ADDR: if args.dest == BROADCAST_ADDR:
meshtastic.util.our_exit("Warning: Must use a destination node ID.") meshtastic.util.our_exit("Warning: Must use a destination node ID.")
@@ -376,17 +401,22 @@ def onConnected(interface):
configuration = yaml.safe_load(file) configuration = yaml.safe_load(file)
closeNow = True closeNow = True
interface.getNode(args.dest, False).beginSettingsTransaction()
if 'owner' in configuration: if 'owner' in configuration:
print(f"Setting device owner to {configuration['owner']}") print(f"Setting device owner to {configuration['owner']}")
interface.getNode(args.dest).setOwner(configuration['owner']) waitForAckNak = True
interface.getNode(args.dest, False).setOwner(configuration['owner'])
if 'owner_short' in configuration: if 'owner_short' in configuration:
print(f"Setting device owner short to {configuration['owner_short']}") print(f"Setting device owner short to {configuration['owner_short']}")
interface.getNode(args.dest).setOwner(long_name=None, short_name=configuration['owner_short']) waitForAckNak = True
interface.getNode(args.dest, False).setOwner(long_name=None, short_name=configuration['owner_short'])
if 'ownerShort' in configuration: if 'ownerShort' in configuration:
print(f"Setting device owner short to {configuration['ownerShort']}") print(f"Setting device owner short to {configuration['ownerShort']}")
interface.getNode(args.dest).setOwner(long_name=None, short_name=configuration['ownerShort']) waitForAckNak = True
interface.getNode(args.dest, False).setOwner(long_name=None, short_name=configuration['ownerShort'])
if 'channel_url' in configuration: if 'channel_url' in configuration:
print("Setting channel url to", configuration['channel_url']) print("Setting channel url to", configuration['channel_url'])
@@ -432,6 +462,7 @@ def onConnected(interface):
setPref(moduleConfig, f"{section}.{pref}", str(configuration['module_config'][section][pref])) setPref(moduleConfig, f"{section}.{pref}", str(configuration['module_config'][section][pref]))
interface.getNode(args.dest).writeConfig(section) interface.getNode(args.dest).writeConfig(section)
interface.getNode(args.dest, False).commitSettingsTransaction()
print("Writing modified configuration to device") print("Writing modified configuration to device")
if args.export_config: if args.export_config:
@@ -604,6 +635,10 @@ def onConnected(interface):
else: else:
tunnel.Tunnel(interface, subnet=args.tunnel_net) tunnel.Tunnel(interface, subnet=args.tunnel_net)
if args.dest != BROADCAST_ADDR and waitForAckNak:
print(f"Waiting for an acknowledgment from remote node (this could take a while)")
interface.getNode(args.dest, False).iface.waitForAckNak()
# if the user didn't ask for serial debugging output, we might want to exit after we've done our operation # if the user didn't ask for serial debugging output, we might want to exit after we've done our operation
if (not args.seriallog) and closeNow: if (not args.seriallog) and closeNow:
interface.close() # after running command then exit interface.close() # after running command then exit
@@ -861,7 +896,7 @@ def initParser():
"--ch-set", help=("Set a channel parameter. To see channel settings available:'--ch-set all all --ch-index 0'. " "--ch-set", help=("Set a channel parameter. To see channel settings available:'--ch-set all all --ch-index 0'. "
"Can set the 'psk' using this command. To disable encryption on primary channel:'--ch-set psk none --ch-index 0'. " "Can set the 'psk' using this command. To disable encryption on primary channel:'--ch-set psk none --ch-index 0'. "
"To set encryption with a new random key on second channel:'--ch-set psk random --ch-index 1'. " "To set encryption with a new random key on second channel:'--ch-set psk random --ch-index 1'. "
"To set encryption back to the default:'--ch-set default --ch-index 0'. To set encryption with your " "To set encryption back to the default:'--ch-set psk default --ch-index 0'. To set encryption with your "
"own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."), "own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."),
nargs=2, action='append') nargs=2, action='append')
@@ -907,6 +942,12 @@ def initParser():
parser.add_argument( parser.add_argument(
"--sendping", help="Send a ping message (which requests a reply)", action="store_true") "--sendping", help="Send a ping message (which requests a reply)", action="store_true")
parser.add_argument(
"--traceroute", help="Traceroute from connected node to a destination. " \
"You need pass the destination ID as argument, like " \
"this: '--traceroute !ba4bf9d0' " \
"Only nodes that have the encryption key can be traced.")
parser.add_argument( parser.add_argument(
"--reboot", help="Tell the destination node to reboot", action="store_true") "--reboot", help="Tell the destination node to reboot", action="store_true")
@@ -919,6 +960,12 @@ def initParser():
parser.add_argument( parser.add_argument(
"--device-metadata", help="Get the device metadata from the node", action="store_true") "--device-metadata", help="Get the device metadata from the node", action="store_true")
parser.add_argument(
"--begin-edit", help="Tell the node to open a transaction to edit settings", action="store_true")
parser.add_argument(
"--commit-edit", help="Tell the node to commit open settings transaction", action="store_true")
parser.add_argument( parser.add_argument(
"--factory-reset", help="Tell the destination node to install the default config", action="store_true") "--factory-reset", help="Tell the destination node to install the default config", action="store_true")

View File

@@ -19,7 +19,7 @@ from . import mesh_pb2 as mesh__pb2
from . import module_config_pb2 as module__config__pb2 from . import module_config_pb2 as module__config__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61\x64min.proto\x1a\rchannel.proto\x1a\x0c\x63onfig.proto\x1a\x15\x64\x65vice_metadata.proto\x1a\nmesh.proto\x1a\x13module_config.proto\"\xe8\n\n\x0c\x41\x64minMessage\x12\x1d\n\x13get_channel_request\x18\x01 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x02 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x03 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\x04 \x01(\x0b\x32\x05.UserH\x00\x12\x36\n\x12get_config_request\x18\x05 \x01(\x0e\x32\x18.AdminMessage.ConfigTypeH\x00\x12&\n\x13get_config_response\x18\x06 \x01(\x0b\x32\x07.ConfigH\x00\x12\x43\n\x19get_module_config_request\x18\x07 \x01(\x0e\x32\x1e.AdminMessage.ModuleConfigTypeH\x00\x12\x33\n\x1aget_module_config_response\x18\x08 \x01(\x0b\x32\r.ModuleConfigH\x00\x12\x34\n*get_canned_message_module_messages_request\x18\n \x01(\x08H\x00\x12\x35\n+get_canned_message_module_messages_response\x18\x0b \x01(\tH\x00\x12%\n\x1bget_device_metadata_request\x18\x0c \x01(\x08H\x00\x12\x37\n\x1cget_device_metadata_response\x18\r \x01(\x0b\x32\x0f.DeviceMetadataH\x00\x12\x1a\n\tset_owner\x18 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18! \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\nset_config\x18\" \x01(\x0b\x32\x07.ConfigH\x00\x12*\n\x11set_module_config\x18# \x01(\x0b\x32\r.ModuleConfigH\x00\x12,\n\"set_canned_message_module_messages\x18$ \x01(\tH\x00\x12\x1c\n\x12\x63onfirm_set_config\x18@ \x01(\x08H\x00\x12#\n\x19\x63onfirm_set_module_config\x18\x41 \x01(\x08H\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18\x42 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18\x43 \x01(\x08H\x00\x12\x1c\n\x12reboot_ota_seconds\x18_ \x01(\x05H\x00\x12\x18\n\x0e\x65xit_simulator\x18` \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18\x61 \x01(\x05H\x00\x12\x1a\n\x10shutdown_seconds\x18\x62 \x01(\x05H\x00\x12\x17\n\rfactory_reset\x18\x63 \x01(\x05H\x00\x12\x16\n\x0cnodedb_reset\x18\x64 \x01(\x05H\x00\"\x95\x01\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x12\n\x0eNETWORK_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\x12\x14\n\x10\x42LUETOOTH_CONFIG\x10\x06\"\xa6\x01\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x42\x11\n\x0fpayload_variantBH\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61\x64min.proto\x1a\rchannel.proto\x1a\x0c\x63onfig.proto\x1a\x15\x64\x65vice_metadata.proto\x1a\nmesh.proto\x1a\x13module_config.proto\"\xf6\n\n\x0c\x41\x64minMessage\x12\x1d\n\x13get_channel_request\x18\x01 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x02 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x03 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\x04 \x01(\x0b\x32\x05.UserH\x00\x12\x36\n\x12get_config_request\x18\x05 \x01(\x0e\x32\x18.AdminMessage.ConfigTypeH\x00\x12&\n\x13get_config_response\x18\x06 \x01(\x0b\x32\x07.ConfigH\x00\x12\x43\n\x19get_module_config_request\x18\x07 \x01(\x0e\x32\x1e.AdminMessage.ModuleConfigTypeH\x00\x12\x33\n\x1aget_module_config_response\x18\x08 \x01(\x0b\x32\r.ModuleConfigH\x00\x12\x34\n*get_canned_message_module_messages_request\x18\n \x01(\x08H\x00\x12\x35\n+get_canned_message_module_messages_response\x18\x0b \x01(\tH\x00\x12%\n\x1bget_device_metadata_request\x18\x0c \x01(\x08H\x00\x12\x37\n\x1cget_device_metadata_response\x18\r \x01(\x0b\x32\x0f.DeviceMetadataH\x00\x12\x1a\n\tset_owner\x18 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18! \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\nset_config\x18\" \x01(\x0b\x32\x07.ConfigH\x00\x12*\n\x11set_module_config\x18# \x01(\x0b\x32\r.ModuleConfigH\x00\x12,\n\"set_canned_message_module_messages\x18$ \x01(\tH\x00\x12\x1d\n\x13\x62\x65gin_edit_settings\x18@ \x01(\x08H\x00\x12\x1e\n\x14\x63ommit_edit_settings\x18\x41 \x01(\x08H\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18\x42 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18\x43 \x01(\x08H\x00\x12\x1c\n\x12reboot_ota_seconds\x18_ \x01(\x05H\x00\x12\x18\n\x0e\x65xit_simulator\x18` \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18\x61 \x01(\x05H\x00\x12\x1a\n\x10shutdown_seconds\x18\x62 \x01(\x05H\x00\x12\x17\n\rfactory_reset\x18\x63 \x01(\x05H\x00\x12\x16\n\x0cnodedb_reset\x18\x64 \x01(\x05H\x00\"\x95\x01\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x12\n\x0eNETWORK_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\x12\x14\n\x10\x42LUETOOTH_CONFIG\x10\x06\"\xb8\x01\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x12\x10\n\x0c\x41UDIO_CONFIG\x10\x07\x42\x11\n\x0fpayload_variantBH\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3')
@@ -38,9 +38,9 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosH\003Z\"github.com/meshtastic/go/generated' DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosH\003Z\"github.com/meshtastic/go/generated'
_ADMINMESSAGE._serialized_start=101 _ADMINMESSAGE._serialized_start=101
_ADMINMESSAGE._serialized_end=1485 _ADMINMESSAGE._serialized_end=1499
_ADMINMESSAGE_CONFIGTYPE._serialized_start=1148 _ADMINMESSAGE_CONFIGTYPE._serialized_start=1144
_ADMINMESSAGE_CONFIGTYPE._serialized_end=1297 _ADMINMESSAGE_CONFIGTYPE._serialized_end=1293
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_start=1300 _ADMINMESSAGE_MODULECONFIGTYPE._serialized_start=1296
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_end=1466 _ADMINMESSAGE_MODULECONFIGTYPE._serialized_end=1480
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

View File

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x64\x65vice_metadata.proto\"H\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\rBQ\n\x13\x63om.geeksville.meshB\x14\x44\x65viceMetadataProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x64\x65vice_metadata.proto\"\x99\x01\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\r\x12\x13\n\x0b\x63\x61nShutdown\x18\x03 \x01(\x08\x12\x0f\n\x07hasWifi\x18\x04 \x01(\x08\x12\x14\n\x0chasBluetooth\x18\x05 \x01(\x08\x12\x13\n\x0bhasEthernet\x18\x06 \x01(\x08\x42Q\n\x13\x63om.geeksville.meshB\x14\x44\x65viceMetadataProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3')
@@ -30,6 +30,6 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\024DeviceMetadataProtosH\003Z\"github.com/meshtastic/go/generated' DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\024DeviceMetadataProtosH\003Z\"github.com/meshtastic/go/generated'
_DEVICEMETADATA._serialized_start=25 _DEVICEMETADATA._serialized_start=26
_DEVICEMETADATA._serialized_end=97 _DEVICEMETADATA._serialized_end=179
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

View File

@@ -16,7 +16,7 @@ from . import config_pb2 as config__pb2
from . import module_config_pb2 as module__config__pb2 from . import module_config_pb2 as module__config__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0flocalonly.proto\x1a\x0c\x63onfig.proto\x1a\x13module_config.proto\"\xb0\x02\n\x0bLocalConfig\x12$\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x14.Config.DeviceConfig\x12(\n\x08position\x18\x02 \x01(\x0b\x32\x16.Config.PositionConfig\x12\"\n\x05power\x18\x03 \x01(\x0b\x32\x13.Config.PowerConfig\x12&\n\x07network\x18\x04 \x01(\x0b\x32\x15.Config.NetworkConfig\x12&\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfig\x12 \n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfig\x12*\n\tbluetooth\x18\x07 \x01(\x0b\x32\x17.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\"\x9a\x03\n\x11LocalModuleConfig\x12&\n\x04mqtt\x18\x01 \x01(\x0b\x32\x18.ModuleConfig.MQTTConfig\x12*\n\x06serial\x18\x02 \x01(\x0b\x32\x1a.ModuleConfig.SerialConfig\x12G\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32(.ModuleConfig.ExternalNotificationConfig\x12\x37\n\rstore_forward\x18\x04 \x01(\x0b\x32 .ModuleConfig.StoreForwardConfig\x12\x31\n\nrange_test\x18\x05 \x01(\x0b\x32\x1d.ModuleConfig.RangeTestConfig\x12\x30\n\ttelemetry\x18\x06 \x01(\x0b\x32\x1d.ModuleConfig.TelemetryConfig\x12\x39\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32!.ModuleConfig.CannedMessageConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBL\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0flocalonly.proto\x1a\x0c\x63onfig.proto\x1a\x13module_config.proto\"\xb0\x02\n\x0bLocalConfig\x12$\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x14.Config.DeviceConfig\x12(\n\x08position\x18\x02 \x01(\x0b\x32\x16.Config.PositionConfig\x12\"\n\x05power\x18\x03 \x01(\x0b\x32\x13.Config.PowerConfig\x12&\n\x07network\x18\x04 \x01(\x0b\x32\x15.Config.NetworkConfig\x12&\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfig\x12 \n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfig\x12*\n\tbluetooth\x18\x07 \x01(\x0b\x32\x17.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\"\xc4\x03\n\x11LocalModuleConfig\x12&\n\x04mqtt\x18\x01 \x01(\x0b\x32\x18.ModuleConfig.MQTTConfig\x12*\n\x06serial\x18\x02 \x01(\x0b\x32\x1a.ModuleConfig.SerialConfig\x12G\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32(.ModuleConfig.ExternalNotificationConfig\x12\x37\n\rstore_forward\x18\x04 \x01(\x0b\x32 .ModuleConfig.StoreForwardConfig\x12\x31\n\nrange_test\x18\x05 \x01(\x0b\x32\x1d.ModuleConfig.RangeTestConfig\x12\x30\n\ttelemetry\x18\x06 \x01(\x0b\x32\x1d.ModuleConfig.TelemetryConfig\x12\x39\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32!.ModuleConfig.CannedMessageConfig\x12(\n\x05\x61udio\x18\t \x01(\x0b\x32\x19.ModuleConfig.AudioConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBL\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3')
@@ -43,5 +43,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
_LOCALCONFIG._serialized_start=55 _LOCALCONFIG._serialized_start=55
_LOCALCONFIG._serialized_end=359 _LOCALCONFIG._serialized_end=359
_LOCALMODULECONFIG._serialized_start=362 _LOCALMODULECONFIG._serialized_start=362
_LOCALMODULECONFIG._serialized_end=772 _LOCALMODULECONFIG._serialized_end=814
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

View File

@@ -18,7 +18,7 @@ from google.protobuf.json_format import MessageToJson
import meshtastic.node import meshtastic.node
from meshtastic import portnums_pb2, mesh_pb2 from meshtastic import portnums_pb2, mesh_pb2
from meshtastic.util import stripnl, Timeout, our_exit, remove_keys_from_dict, convert_mac_addr from meshtastic.util import stripnl, Timeout, Acknowledgment, our_exit, remove_keys_from_dict, convert_mac_addr
from meshtastic.__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols from meshtastic.__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols
class MeshInterface: class MeshInterface:
@@ -47,12 +47,12 @@ class MeshInterface:
self.responseHandlers = {} # A map from request ID to the handler self.responseHandlers = {} # A map from request ID to the handler
self.failure = None # If we've encountered a fatal exception it will be kept here self.failure = None # If we've encountered a fatal exception it will be kept here
self._timeout = Timeout() self._timeout = Timeout()
self._acknowledgment = Acknowledgment()
self.heartbeatTimer = None self.heartbeatTimer = None
random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it
self.currentPacketId = random.randint(0, 0xffffffff) self.currentPacketId = random.randint(0, 0xffffffff)
self.nodesByNum = None self.nodesByNum = None
self.configId = None self.configId = None
self.defaultHopLimit = 3
self.gotResponse = False # used in gpio read self.gotResponse = False # used in gpio read
self.mask = None # used in gpio read and gpio watch self.mask = None # used in gpio read and gpio watch
@@ -157,23 +157,24 @@ class MeshInterface:
return table return table
def getNode(self, nodeId): def getNode(self, nodeId, requestConfig=True):
"""Return a node object which contains device settings and channel info""" """Return a node object which contains device settings and channel info"""
if nodeId in (LOCAL_ADDR, BROADCAST_ADDR): if nodeId in (LOCAL_ADDR, BROADCAST_ADDR):
return self.localNode return self.localNode
else: else:
n = meshtastic.node.Node(self, nodeId) n = meshtastic.node.Node(self, nodeId)
logging.debug("About to requestConfig") # Only request device settings and channel info when necessary
n.requestConfig() if requestConfig:
if not n.waitForConfig(): logging.debug("About to requestConfig")
our_exit("Error: Timed out waiting for node config") n.requestConfig()
if not n.waitForConfig():
our_exit("Error: Timed out waiting for node config")
return n return n
def sendText(self, text: AnyStr, def sendText(self, text: AnyStr,
destinationId=BROADCAST_ADDR, destinationId=BROADCAST_ADDR,
wantAck=False, wantAck=False,
wantResponse=False, wantResponse=False,
hopLimit=None,
onResponse=None, onResponse=None,
channelIndex=0): channelIndex=0):
"""Send a utf8 string to some other node, if the node has a display it """Send a utf8 string to some other node, if the node has a display it
@@ -195,21 +196,17 @@ class MeshInterface:
Returns the sent packet. The id field will be populated in this packet Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks. and can be used to track future message acks/naks.
""" """
if hopLimit is None:
hopLimit = self.defaultHopLimit
return self.sendData(text.encode("utf-8"), destinationId, return self.sendData(text.encode("utf-8"), destinationId,
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
wantAck=wantAck, wantAck=wantAck,
wantResponse=wantResponse, wantResponse=wantResponse,
hopLimit=hopLimit,
onResponse=onResponse, onResponse=onResponse,
channelIndex=channelIndex) channelIndex=channelIndex)
def sendData(self, data, destinationId=BROADCAST_ADDR, def sendData(self, data, destinationId=BROADCAST_ADDR,
portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False,
wantResponse=False, wantResponse=False,
hopLimit=None,
onResponse=None, onResponse=None,
channelIndex=0): channelIndex=0):
"""Send a data packet to some other node """Send a data packet to some other node
@@ -234,8 +231,6 @@ class MeshInterface:
Returns the sent packet. The id field will be populated in this packet Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks. and can be used to track future message acks/naks.
""" """
if hopLimit is None:
hopLimit = self.defaultHopLimit
if getattr(data, "SerializeToString", None): if getattr(data, "SerializeToString", None):
logging.debug(f"Serializing protobuf as data: {stripnl(data)}") logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
@@ -258,8 +253,7 @@ class MeshInterface:
if onResponse is not None: if onResponse is not None:
self._addResponseHandler(meshPacket.id, onResponse) self._addResponseHandler(meshPacket.id, onResponse)
p = self._sendPacket(meshPacket, destinationId, p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck)
wantAck=wantAck, hopLimit=hopLimit)
return p return p
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
@@ -298,20 +292,41 @@ class MeshInterface:
wantAck=wantAck, wantAck=wantAck,
wantResponse=wantResponse) wantResponse=wantResponse)
def sendTraceRoute(self, dest, hopLimit):
r = mesh_pb2.RouteDiscovery()
self.sendData(r, destinationId=dest, portNum=portnums_pb2.PortNum.TRACEROUTE_APP,
wantResponse=True, onResponse=self.onResponseTraceRoute)
# extend timeout based on number of nodes, limit by configured hopLimit
waitFactor = min(len(self.nodes)-1, hopLimit)
self.waitForTraceRoute(waitFactor)
def onResponseTraceRoute(self, p):
routeDiscovery = mesh_pb2.RouteDiscovery()
routeDiscovery.ParseFromString(p["decoded"]["payload"])
asDict = google.protobuf.json_format.MessageToDict(routeDiscovery)
print("Route traced:")
routeStr = self._nodeNumToId(p["to"])
if "route" in asDict:
for nodeNum in asDict["route"]:
routeStr += " --> " + self._nodeNumToId(nodeNum)
routeStr += " --> " + self._nodeNumToId(p["from"])
print(routeStr)
self._acknowledgment.receivedTraceRoute = True
def _addResponseHandler(self, requestId, callback): def _addResponseHandler(self, requestId, callback):
self.responseHandlers[requestId] = ResponseHandler(callback) self.responseHandlers[requestId] = ResponseHandler(callback)
def _sendPacket(self, meshPacket, def _sendPacket(self, meshPacket,
destinationId=BROADCAST_ADDR, destinationId=BROADCAST_ADDR,
wantAck=False, hopLimit=None): wantAck=False):
"""Send a MeshPacket to the specified node (or if unspecified, broadcast). """Send a MeshPacket to the specified node (or if unspecified, broadcast).
You probably don't want this - use sendData instead. You probably don't want this - use sendData instead.
Returns the sent packet. The id field will be populated in this packet and Returns the sent packet. The id field will be populated in this packet and
can be used to track future message acks/naks. can be used to track future message acks/naks.
""" """
if hopLimit is None:
hopLimit = self.defaultHopLimit
# We allow users to talk to the local node before we've completed the full connection flow... # We allow users to talk to the local node before we've completed the full connection flow...
if(self.myInfo is not None and destinationId != self.myInfo.my_node_num): if(self.myInfo is not None and destinationId != self.myInfo.my_node_num):
@@ -345,6 +360,8 @@ class MeshInterface:
meshPacket.to = nodeNum meshPacket.to = nodeNum
meshPacket.want_ack = wantAck meshPacket.want_ack = wantAck
loraConfig = getattr(self.localNode.localConfig, 'lora')
hopLimit = getattr(loraConfig, 'hop_limit')
meshPacket.hop_limit = hopLimit meshPacket.hop_limit = hopLimit
# if the user hasn't set an ID for this packet (likely and recommended), # if the user hasn't set an ID for this packet (likely and recommended),
@@ -366,6 +383,16 @@ class MeshInterface:
if not success: if not success:
raise Exception("Timed out waiting for interface config") raise Exception("Timed out waiting for interface config")
def waitForAckNak(self):
success = self._timeout.waitForAckNak(self._acknowledgment)
if not success:
raise Exception("Timed out waiting for an acknowledgment")
def waitForTraceRoute(self, waitFactor):
success = self._timeout.waitForTraceRoute(waitFactor, self._acknowledgment)
if not success:
raise Exception("Timed out waiting for traceroute")
def getMyNodeInfo(self): def getMyNodeInfo(self):
"""Get info about my node.""" """Get info about my node."""
if self.myInfo is None: if self.myInfo is None:
@@ -717,7 +744,8 @@ class MeshInterface:
# we keep the responseHandler in dict until we get a non ack # we keep the responseHandler in dict until we get a non ack
handler = self.responseHandlers.pop(requestId, None) handler = self.responseHandlers.pop(requestId, None)
if handler is not None: if handler is not None:
handler.callback(asDict) if not isAck or (isAck and handler.__name__ == "onAckNak"):
handler.callback(asDict)
logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ") logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ")
publishingThread.queueWork(lambda: pub.sendMessage( publishingThread.queueWork(lambda: pub.sendMessage(

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -181,6 +181,13 @@ class Node:
logging.debug("Wrote module: canned_message") logging.debug("Wrote module: canned_message")
time.sleep(0.3) time.sleep(0.3)
if self.moduleConfig.audio:
p = admin_pb2.AdminMessage()
p.set_module_config.audio.CopyFrom(self.moduleConfig.audio)
self._sendAdmin(p)
logging.debug("Wrote module: audo")
time.sleep(0.3)
def writeConfig(self, config_name): def writeConfig(self, config_name):
"""Write the current (edited) localConfig to the device""" """Write the current (edited) localConfig to the device"""
if self.localConfig is None: if self.localConfig is None:
@@ -216,6 +223,8 @@ class Node:
p.set_module_config.telemetry.CopyFrom(self.moduleConfig.telemetry) p.set_module_config.telemetry.CopyFrom(self.moduleConfig.telemetry)
elif config_name == 'canned_message': elif config_name == 'canned_message':
p.set_module_config.canned_message.CopyFrom(self.moduleConfig.canned_message) p.set_module_config.canned_message.CopyFrom(self.moduleConfig.canned_message)
elif config_name == 'audio':
p.set_module_config.audio.CopyFrom(self.moduleConfig.audio)
else: else:
our_exit(f"Error: No valid config with name {config_name}") our_exit(f"Error: No valid config with name {config_name}")
@@ -290,38 +299,30 @@ class Node:
def setOwner(self, long_name=None, short_name=None, is_licensed=False): def setOwner(self, long_name=None, short_name=None, is_licensed=False):
"""Set device owner name""" """Set device owner name"""
logging.debug(f"in setOwner nodeNum:{self.nodeNum}") logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
nChars = 3
minChars = 2
if long_name is not None:
long_name = long_name.strip()
if short_name is None:
words = long_name.split()
if len(long_name) <= nChars:
short_name = long_name
elif len(words) >= minChars:
short_name = ''.join(map(lambda word: word[0], words))
else:
trans = str.maketrans(dict.fromkeys('aeiouAEIOU'))
short_name = long_name[0] + long_name[1:].translate(trans)
if len(short_name) < nChars:
short_name = long_name[:nChars]
p = admin_pb2.AdminMessage() p = admin_pb2.AdminMessage()
nChars = 4
if long_name is not None: if long_name is not None:
long_name = long_name.strip()
p.set_owner.long_name = long_name p.set_owner.long_name = long_name
p.set_owner.is_licensed = is_licensed
if short_name is not None: if short_name is not None:
short_name = short_name.strip() short_name = short_name.strip()
if len(short_name) > nChars: if len(short_name) > nChars:
short_name = short_name[:nChars] short_name = short_name[:nChars]
print(f"Maximum is 4 characters, truncated to {short_name}")
p.set_owner.short_name = short_name p.set_owner.short_name = short_name
p.set_owner.is_licensed = is_licensed
# Note: These debug lines are used in unit tests # Note: These debug lines are used in unit tests
logging.debug(f'p.set_owner.long_name:{p.set_owner.long_name}:') logging.debug(f'p.set_owner.long_name:{p.set_owner.long_name}:')
logging.debug(f'p.set_owner.short_name:{p.set_owner.short_name}:') logging.debug(f'p.set_owner.short_name:{p.set_owner.short_name}:')
logging.debug(f'p.set_owner.is_licensed:{p.set_owner.is_licensed}') logging.debug(f'p.set_owner.is_licensed:{p.set_owner.is_licensed}')
return self._sendAdmin(p) # If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def getURL(self, includeAll: bool = True): def getURL(self, includeAll: bool = True):
"""The sharable URL that describes the current channel""" """The sharable URL that describes the current channel"""
@@ -439,7 +440,12 @@ class Node:
p.set_canned_message_module_messages = chunk p.set_canned_message_module_messages = chunk
logging.debug(f"Setting canned message '{chunk}' part {i+1}") logging.debug(f"Setting canned message '{chunk}' part {i+1}")
self._sendAdmin(p) # If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def exitSimulator(self): def exitSimulator(self):
"""Tell a simulator node to exit (this message """Tell a simulator node to exit (this message
@@ -456,7 +462,38 @@ class Node:
p.reboot_seconds = secs p.reboot_seconds = secs
logging.info(f"Telling node to reboot in {secs} seconds") logging.info(f"Telling node to reboot in {secs} seconds")
return self._sendAdmin(p) # If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def beginSettingsTransaction(self):
"""Tell the node to open a transaction to edit settings."""
p = admin_pb2.AdminMessage()
p.begin_edit_settings = True
logging.info(f"Telling open a transaction to edit settings")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def commitSettingsTransaction(self):
"""Tell the node to commit the open transaction for editing settings."""
p = admin_pb2.AdminMessage()
p.commit_edit_settings = True
logging.info(f"Telling node to commit open transaction for editing settings")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def rebootOTA(self, secs: int = 10): def rebootOTA(self, secs: int = 10):
"""Tell the node to reboot into factory firmware.""" """Tell the node to reboot into factory firmware."""
@@ -464,7 +501,12 @@ class Node:
p.reboot_ota_seconds = secs p.reboot_ota_seconds = secs
logging.info(f"Telling node to reboot to OTA in {secs} seconds") logging.info(f"Telling node to reboot to OTA in {secs} seconds")
return self._sendAdmin(p) # If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def shutdown(self, secs: int = 10): def shutdown(self, secs: int = 10):
"""Tell the node to shutdown.""" """Tell the node to shutdown."""
@@ -472,7 +514,12 @@ class Node:
p.shutdown_seconds = secs p.shutdown_seconds = secs
logging.info(f"Telling node to shutdown in {secs} seconds") logging.info(f"Telling node to shutdown in {secs} seconds")
return self._sendAdmin(p) # If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def getMetadata(self, secs: int = 10): def getMetadata(self, secs: int = 10):
"""Tell the node to shutdown.""" """Tell the node to shutdown."""
@@ -488,7 +535,12 @@ class Node:
p.factory_reset = True p.factory_reset = True
logging.info(f"Telling node to factory reset") logging.info(f"Telling node to factory reset")
return self._sendAdmin(p) # If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def resetNodeDb(self): def resetNodeDb(self):
"""Tell the node to reset its list of nodes.""" """Tell the node to reset its list of nodes."""
@@ -496,7 +548,12 @@ class Node:
p.nodedb_reset = True p.nodedb_reset = True
logging.info(f"Telling node to reset the NodeDB") logging.info(f"Telling node to reset the NodeDB")
return self._sendAdmin(p) # If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def _fixupChannels(self): def _fixupChannels(self):
"""Fixup indexes and add disabled channels as needed""" """Fixup indexes and add disabled channels as needed"""
@@ -551,7 +608,7 @@ class Node:
return # Don't try to parse this routing message return # Don't try to parse this routing message
lastTried = 0 lastTried = 0
if len(self.partialChannels) > 0: if len(self.partialChannels) > 0:
lastTried = self.partialChannels[-1] lastTried = self.partialChannels[-1].index
logging.debug(f"Retrying previous channel request.") logging.debug(f"Retrying previous channel request.")
self._requestChannel(lastTried) self._requestChannel(lastTried)
return return
@@ -580,6 +637,18 @@ class Node:
else: else:
self._requestChannel(index + 1) self._requestChannel(index + 1)
def onAckNak(self, p):
if p["decoded"]["routing"]["errorReason"] != "NONE":
print(f'Received a NAK, error reason: {p["decoded"]["routing"]["errorReason"]}')
self.iface._acknowledgment.receivedNak = True
else:
if int(p["from"]) == self.iface.localNode.nodeNum:
print(f'Received an implicit ACK. Packet will likely arrive, but cannot be guaranteed.')
self.iface._acknowledgment.receivedImplAck = True
else:
print(f'Received an ACK.')
self.iface._acknowledgment.receivedAck = True
def _requestChannel(self, channelNum: int): def _requestChannel(self, channelNum: int):
"""Done with initial config messages, now send regular """Done with initial config messages, now send regular
MeshPackets to ask for settings""" MeshPackets to ask for settings"""
@@ -597,7 +666,7 @@ class Node:
# pylint: disable=R1710 # pylint: disable=R1710
def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=False, def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=True,
onResponse=None, adminIndex=0): onResponse=None, adminIndex=0):
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)""" """Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
@@ -610,7 +679,7 @@ class Node:
return self.iface.sendData(p, self.nodeNum, return self.iface.sendData(p, self.nodeNum,
portNum=portnums_pb2.PortNum.ADMIN_APP, portNum=portnums_pb2.PortNum.ADMIN_APP,
wantAck=True, wantAck=False,
wantResponse=wantResponse, wantResponse=wantResponse,
onResponse=onResponse, onResponse=onResponse,
channelIndex=adminIndex) channelIndex=adminIndex)

View File

@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eportnums.proto*\x81\x03\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42\x45\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eportnums.proto*\xa4\x03\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tAUDIO_APP\x10\t\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x12\n\x0eTRACEROUTE_APP\x10\x46\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42\x45\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3')
_PORTNUM = DESCRIPTOR.enum_types_by_name['PortNum'] _PORTNUM = DESCRIPTOR.enum_types_by_name['PortNum']
PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM) PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM)
@@ -28,6 +28,7 @@ ROUTING_APP = 5
ADMIN_APP = 6 ADMIN_APP = 6
TEXT_MESSAGE_COMPRESSED_APP = 7 TEXT_MESSAGE_COMPRESSED_APP = 7
WAYPOINT_APP = 8 WAYPOINT_APP = 8
AUDIO_APP = 9
REPLY_APP = 32 REPLY_APP = 32
IP_TUNNEL_APP = 33 IP_TUNNEL_APP = 33
SERIAL_APP = 64 SERIAL_APP = 64
@@ -36,6 +37,7 @@ RANGE_TEST_APP = 66
TELEMETRY_APP = 67 TELEMETRY_APP = 67
ZPS_APP = 68 ZPS_APP = 68
SIMULATOR_APP = 69 SIMULATOR_APP = 69
TRACEROUTE_APP = 70
PRIVATE_APP = 256 PRIVATE_APP = 256
ATAK_FORWARDER = 257 ATAK_FORWARDER = 257
MAX = 511 MAX = 511
@@ -46,5 +48,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsH\003Z\"github.com/meshtastic/go/generated' DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsH\003Z\"github.com/meshtastic/go/generated'
_PORTNUM._serialized_start=19 _PORTNUM._serialized_start=19
_PORTNUM._serialized_end=404 _PORTNUM._serialized_end=439
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

View File

@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ftelemetry.proto\"i\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\"\x9b\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\"\x82\x01\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12(\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x0e.DeviceMetricsH\x00\x12\x32\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x13.EnvironmentMetricsH\x00\x42\t\n\x07variant*\xa0\x01\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\nBL\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ftelemetry.proto\"i\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\"\x9b\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\"\x82\x01\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12(\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x0e.DeviceMetricsH\x00\x12\x32\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x13.EnvironmentMetricsH\x00\x42\t\n\x07variant*\xae\x01\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\n\x12\x0c\n\x08QMC5883L\x10\x0b\x42L\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosH\x03Z\"github.com/meshtastic/go/generatedb\x06proto3')
_TELEMETRYSENSORTYPE = DESCRIPTOR.enum_types_by_name['TelemetrySensorType'] _TELEMETRYSENSORTYPE = DESCRIPTOR.enum_types_by_name['TelemetrySensorType']
TelemetrySensorType = enum_type_wrapper.EnumTypeWrapper(_TELEMETRYSENSORTYPE) TelemetrySensorType = enum_type_wrapper.EnumTypeWrapper(_TELEMETRYSENSORTYPE)
@@ -30,6 +30,7 @@ SHTC3 = 7
LPS22 = 8 LPS22 = 8
QMC6310 = 9 QMC6310 = 9
QMI8658 = 10 QMI8658 = 10
QMC5883L = 11
_DEVICEMETRICS = DESCRIPTOR.message_types_by_name['DeviceMetrics'] _DEVICEMETRICS = DESCRIPTOR.message_types_by_name['DeviceMetrics']
@@ -61,7 +62,7 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosH\003Z\"github.com/meshtastic/go/generated' DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosH\003Z\"github.com/meshtastic/go/generated'
_TELEMETRYSENSORTYPE._serialized_start=418 _TELEMETRYSENSORTYPE._serialized_start=418
_TELEMETRYSENSORTYPE._serialized_end=578 _TELEMETRYSENSORTYPE._serialized_end=592
_DEVICEMETRICS._serialized_start=19 _DEVICEMETRICS._serialized_start=19
_DEVICEMETRICS._serialized_end=124 _DEVICEMETRICS._serialized_end=124
_ENVIRONMENTMETRICS._serialized_start=127 _ENVIRONMENTMETRICS._serialized_start=127

View File

@@ -159,6 +159,40 @@ class Timeout:
time.sleep(self.sleepInterval) time.sleep(self.sleepInterval)
return False return False
def waitForAckNak(self, acknowledgment, attrs=('receivedAck', 'receivedNak', 'receivedImplAck')):
"""Block until an ACK or NAK has been received. Returns True if ACK or NAK has been received."""
self.reset()
while time.time() < self.expireTime:
if any(map(lambda a: getattr(acknowledgment, a, None), attrs)):
acknowledgment.reset()
return True
time.sleep(self.sleepInterval)
return False
def waitForTraceRoute(self, waitFactor, acknowledgment, attr='receivedTraceRoute'):
"""Block until traceroute response is received. Returns True if traceroute response has been received."""
self.expireTimeout *= waitFactor
self.reset()
while time.time() < self.expireTime:
if getattr(acknowledgment, attr, None):
acknowledgment.reset()
return True
time.sleep(self.sleepInterval)
return False
class Acknowledgment:
"A class that records which type of acknowledgment was just received, if any."
def __init__(self):
self.receivedAck = False
self.receivedNak = False
self.receivedImplAck = False
self.receivedTraceRoute = False
def reset(self):
self.receivedAck = False
self.receivedNak = False
self.receivedImplAck = False
self.receivedTraceRoute = False
class DeferredExecution(): class DeferredExecution():
"""A thread that accepts closures to run, and runs them as they are received""" """A thread that accepts closures to run, and runs them as they are received"""

View File

@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
# This call to setup() does all the work # This call to setup() does all the work
setup( setup(
name="meshtastic", name="meshtastic",
version="2.0.1", version="2.0.5",
description="Python API & client shell for talking to Meshtastic devices", description="Python API & client shell for talking to Meshtastic devices",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",