Compare commits

..

27 Commits

Author SHA1 Message Date
Ben Meadors
92c7b2db69 Merge pull request #348 from meshtastic/moduleconfg
100ms delays to prevent overloading the serial
2022-07-02 14:43:24 -05:00
Ben Meadors
ff94ad968c 100ms delays to prevent overloading the serial 2022-07-02 14:41:09 -05:00
github-actions
c6071c57ec bump version 2022-06-30 01:17:37 +00:00
Ben Meadors
e3e3562c2c Merge pull request #347 from meshtastic/moduleconfg
Module config progress
2022-06-29 20:16:37 -05:00
Ben Meadors
06b5b8fa83 It works, I think 2022-06-29 20:01:48 -05:00
Ben Meadors
4b95b0ff30 Module config progress 2022-06-29 18:35:06 -05:00
Thomas Göttgens
42f2ed571d Merge pull request #346 from meshtastic/patch-1
small issues corrected
2022-06-21 19:38:01 +02:00
Thomas Göttgens
f3791c5c6d - make segemented config print entire name on screen
- rename wifi password to psk
2022-06-21 19:29:54 +02:00
github-actions
4cff344971 bump version 2022-06-21 16:18:33 +00:00
Ben Meadors
594b307e94 Merge pull request #344 from meshtastic/patch-1
Get and Set general attributes working.
2022-06-21 08:23:58 -05:00
Ben Meadors
8a5fd16469 Don't blow away config please 2022-06-21 08:05:15 -05:00
Ben Meadors
4fa93989fa Don't need to pass interface down 2022-06-19 15:41:21 -05:00
Ben Meadors
032072d2f3 Started on set 2022-06-19 09:55:09 -05:00
Ben Meadors
ce7b1d9916 Get preferences now working 2022-06-19 08:17:28 -05:00
Thomas Göttgens
d015da3ca1 Get and Set general attributes almost working. 2022-06-18 14:53:04 +02:00
Thomas Göttgens
a956c8068c Merge pull request #343 from meshtastic/patch-1
Untangle Modem Presets from Channels
2022-06-18 13:41:38 +02:00
Thomas Göttgens
2124e292f1 Untangle Modem Presets from Channels 2022-06-18 13:38:08 +02:00
Ben Meadors
115739a9bb Merge pull request #342 from meshtastic/nanopb-upgrade
Github action protos version
2022-06-17 08:39:00 -05:00
Ben Meadors
cc2c16b957 Github action protos version 2022-06-17 08:33:13 -05:00
Thomas Göttgens
b9245c6c1f Merge pull request #341 from meshtastic/patch-1
almost working
2022-06-17 12:40:06 +02:00
Thomas Göttgens
d21f7811fa where did that came from? 2022-06-17 12:37:50 +02:00
Thomas Göttgens
8dbd6431f7 almost working 2022-06-17 12:31:18 +02:00
Thomas Göttgens
c7b2bbf700 Merge pull request #340 from meshtastic/patch-1
remove "Request Settings" during init, team settings and deprecated settings.
2022-06-17 10:59:54 +02:00
Thomas Göttgens
682fdb7ef4 Remove test for deprecated option 2022-06-17 10:57:39 +02:00
Thomas Göttgens
9c79f9d80e Wait again, since we don't request config manually 2022-06-17 10:54:31 +02:00
Thomas Göttgens
c55b1188e8 remove "Request Settings" during init, team settings and deprecated settings. 2022-06-17 10:40:40 +02:00
github-actions
d5e4eaf2d8 bump version 2022-06-17 05:53:24 +00:00
15 changed files with 407 additions and 468 deletions

View File

@@ -18,9 +18,9 @@ jobs:
- name: Download nanopb
run: |
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.4-linux-x86.tar.gz
tar xvzf nanopb-0.4.4-linux-x86.tar.gz
mv nanopb-0.4.4-linux-x86 nanopb-0.4.4
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.6-linux-x86.tar.gz
tar xvzf nanopb-0.4.6-linux-x86.tar.gz
mv nanopb-0.4.6-linux-x86 nanopb-0.4.6
- name: Re-generate protocol buffers
run: |

68
.vscode/launch.json vendored
View File

@@ -42,7 +42,71 @@
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug" ]
"args": ["--debug"]
},
{
"name": "meshtastic debug getPref",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--get", "power.is_power_saving"]
},
{
"name": "meshtastic debug getPref telemetry",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--get", "telemetry.environment_update_interval"]
},
{
"name": "meshtastic debug info",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--info"]
},
{
"name": "meshtastic debug set region",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--set", "lora.region", "1"]
},
{
"name": "meshtastic debug setPref",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--set", "power.is_power_saving", "1"]
},
{
"name": "meshtastic debug setPref telemetry.environment_measurement_enabled",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--set", "telemetry.environment_measurement_enabled", "1"]
},
{
"name": "meshtastic debug setPref telemetry.environment_screen_enabled",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--set", "telemetry.environment_screen_enabled", "1"]
},
{
"name": "meshtastic debug setPref telemetry",
"type": "python",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--set", "telemetry.environment_measurement_enabled", "1"]
},
{
"name": "meshtastic setpref",
@@ -92,7 +156,7 @@
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--sendtext", "pytest"]
}
},
{
"name": "meshtastic showNodes",
"type": "python",

View File

@@ -34,7 +34,7 @@ Basic functionality is complete now.
- DONE add fromId and toId to received messages dictionaries
- make command line options for displaying/changing config
- update nodedb as nodes change
- radioConfig - getter/setter syntax: https://www.python-course.eu/python3_properties.php
- localConfig - getter/setter syntax: https://www.python-course.eu/python3_properties.php
- let user change radio params via commandline options
- keep nodedb up-to-date based on received MeshPackets
- handle radio reboots and redownload db when that happens. Look for a special FromRadio.rebooted packet

View File

@@ -7,7 +7,7 @@ Source code on [github](https://github.com/meshtastic/Meshtastic-python)
properties of SerialInterface:
- radioConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
- localConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
the device.
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
node in the mesh. This is a read-only datastructure.

View File

@@ -52,78 +52,72 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=W0613
print(f"Connection changed: {topic.getName()}")
def getPref(attributes, name):
def getPref(config, comp_name):
"""Get a channel or preferences value"""
camel_name = meshtastic.util.snake_to_camel(name)
name = splitCompoundName(comp_name)
camel_name = meshtastic.util.snake_to_camel(name[1])
# Note: protobufs has the keys in snake_case, so snake internally
snake_name = meshtastic.util.camel_to_snake(name)
snake_name = meshtastic.util.camel_to_snake(name[1])
logging.debug(f'snake_name:{snake_name} camel_name:{camel_name}')
logging.debug(f'use camel:{Globals.getInstance().get_camel_case()}')
objDesc = attributes.DESCRIPTOR
field = objDesc.fields_by_name.get(snake_name)
if not field:
if Globals.getInstance().get_camel_case():
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not get it.")
else:
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not get it.")
print(f"Choices in sorted order are:")
names = []
for f in objDesc.fields:
tmp_name = f'{f.name}'
if Globals.getInstance().get_camel_case():
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
names.append(tmp_name)
for temp_name in sorted(names):
print(f" {temp_name}")
return
objDesc = config.DESCRIPTOR
config_type = objDesc.fields_by_name.get(name[0])
pref = False
if config_type:
pref = config_type.message_type.fields_by_name.get(snake_name)
if (not pref) or (not config_type):
return False
# read the value
val = getattr(attributes, snake_name)
config_values = getattr(config, config_type.name)
pref_value = getattr(config_values, pref.name)
if Globals.getInstance().get_camel_case():
print(f"{camel_name}: {str(val)}")
logging.debug(f"{camel_name}: {str(val)}")
print(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}")
logging.debug(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}")
else:
print(f"{snake_name}: {str(val)}")
logging.debug(f"{snake_name}: {str(val)}")
print(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}")
logging.debug(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}")
return True
def splitCompoundName(comp_name):
name = comp_name.split(".",1)
if len(name) != 2:
name[0]=comp_name
name.append(comp_name)
return name
def setPref(attributes, name, valStr):
def setPref(config, comp_name, valStr):
"""Set a channel or preferences value"""
snake_name = meshtastic.util.camel_to_snake(name)
camel_name = meshtastic.util.snake_to_camel(name)
name = splitCompoundName(comp_name)
snake_name = meshtastic.util.camel_to_snake(name[1])
camel_name = meshtastic.util.snake_to_camel(name[1])
logging.debug(f'snake_name:{snake_name}')
logging.debug(f'camel_name:{camel_name}')
objDesc = attributes.DESCRIPTOR
field = objDesc.fields_by_name.get(snake_name)
if not field:
if Globals.getInstance().get_camel_case():
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not set it.")
else:
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not set it.")
print(f"Choices in sorted order are:")
names = []
for f in objDesc.fields:
tmp_name = f'{f.name}'
if Globals.getInstance().get_camel_case():
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
names.append(tmp_name)
for temp_name in sorted(names):
print(f" {temp_name}")
return
objDesc = config.DESCRIPTOR
config_type = objDesc.fields_by_name.get(name[0])
pref = False
if config_type:
pref = config_type.message_type.fields_by_name.get(snake_name)
if (not pref) or (not config_type):
return False
val = meshtastic.util.fromStr(valStr)
logging.debug(f'valStr:{valStr} val:{val}')
if snake_name == 'wifi_password' and len(valStr) < 8:
print(f"Warning: wifi_password must be 8 or more characters.")
return
if snake_name == 'psk' and len(valStr) < 8:
print(f"Warning: wifi.psk must be 8 or more characters.")
return
enumType = field.enum_type
enumType = pref.enum_type
# pylint: disable=C0123
if enumType and type(val) == str:
# We've failed so far to convert this string into an enum, try to find it by reflection
@@ -132,9 +126,9 @@ def setPref(attributes, name, valStr):
val = e.number
else:
if Globals.getInstance().get_camel_case():
print(f"{camel_name} does not have an enum called {val}, so you can not set it.")
print(f"{name[0]}.{camel_name} does not have an enum called {val}, so you can not set it.")
else:
print(f"{snake_name} does not have an enum called {val}, so you can not set it.")
print(f"{name[0]}.{snake_name} does not have an enum called {val}, so you can not set it.")
print(f"Choices in sorted order are:")
names = []
for f in enumType.values:
@@ -147,23 +141,27 @@ def setPref(attributes, name, valStr):
# note: 'ignore_incoming' is a repeating field
if snake_name != 'ignore_incoming':
try:
setattr(attributes, snake_name, val)
config_values = getattr(config, config_type.name)
setattr(config_values, pref.name, val)
except TypeError:
# The setter didn't like our arg type guess try again as a string
setattr(attributes, snake_name, valStr)
config_values = getattr(config, config_type.name)
setattr(config_values, pref.name, valStr)
else:
if val == 0:
# clear values
print("Clearing ignore_incoming list")
del attributes.ignore_incoming[:]
del config_type.message_type.ignore_incoming[:]
else:
print(f"Adding '{val}' to the ignore_incoming list")
attributes.ignore_incoming.extend([val])
config_type.message_type.ignore_incoming.extend([val])
if Globals.getInstance().get_camel_case():
print(f"Set {camel_name} to {valStr}")
print(f"Set {name[0]}.{camel_name} to {valStr}")
else:
print(f"Set {snake_name} to {valStr}")
print(f"Set {name[0]}.{snake_name} to {valStr}")
return True
def onConnected(interface):
@@ -183,18 +181,18 @@ def onConnected(interface):
alt = 0
lat = 0.0
lon = 0.0
prefs = interface.localNode.radioConfig.preferences
localConfig = interface.localNode.localConfig
if args.setalt:
alt = int(args.setalt)
prefs.fixed_position = True
localConfig.fixed_position = True
print(f"Fixing altitude at {alt} meters")
if args.setlat:
lat = float(args.setlat)
prefs.fixed_position = True
localConfig.fixed_position = True
print(f"Fixing latitude at {lat} degrees")
if args.setlon:
lon = float(args.setlon)
prefs.fixed_position = True
localConfig.fixed_position = True
print(f"Fixing longitude at {lon} degrees")
print("Setting device position")
@@ -224,7 +222,7 @@ def onConnected(interface):
if args.pos_fields:
# If --pos-fields invoked with args, set position fields
closeNow = True
prefs = interface.getNode(args.dest).radioConfig.preferences
localConfig = interface.getNode(args.dest).localConfig
allFields = 0
try:
@@ -239,34 +237,21 @@ def onConnected(interface):
else:
print(f"Setting position fields to {allFields}")
setPref(prefs, 'position_flags', f'{allFields:d}')
setPref(localConfig, 'position_flags', f'{allFields:d}')
print("Writing modified preferences to device")
interface.getNode(args.dest).writeConfig()
elif args.pos_fields is not None:
# If --pos-fields invoked without args, read and display current value
closeNow = True
prefs = interface.getNode(args.dest).radioConfig.preferences
localConfig = interface.getNode(args.dest).localConfig
fieldNames = []
for bit in config_pb2.PositionFlags.values():
if prefs.position_flags & bit:
if localConfig.position_flags & bit:
fieldNames.append(config_pb2.PositionFlags.Name(bit))
print(' '.join(fieldNames))
if args.set_team:
closeNow = True
try:
v_team = meshtastic.mesh_pb2.Team.Value(args.set_team.upper())
except ValueError:
v_team = 0
print(f"ERROR: Team \'{args.set_team}\' not found.")
print("Try a team name from the sorted list below, or use 'CLEAR' for unaffiliated:")
print(sorted(meshtastic.mesh_pb2.Team.keys()))
else:
print(f"Setting team to {meshtastic.mesh_pb2.Team.Name(v_team)}")
interface.getNode(args.dest).setOwner(team=v_team)
if args.set_ham:
closeNow = True
print(f"Setting Ham ID to {args.set_ham} and turning off encryption")
@@ -339,14 +324,22 @@ def onConnected(interface):
# handle settings
if args.set:
closeNow = True
prefs = interface.getNode(args.dest).radioConfig.preferences
node = interface.getNode(args.dest)
# Handle the int/float/bool arguments
for pref in args.set:
setPref(prefs, pref[0], pref[1])
found = setPref(node.localConfig, pref[0], pref[1])
if not found:
found = setPref(node.moduleConfig, pref[0], pref[1])
print("Writing modified preferences to device")
interface.getNode(args.dest).writeConfig()
if found:
print("Writing modified preferences to device")
interface.getNode(args.dest).writeConfig()
else:
if Globals.getInstance().get_camel_case():
print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have an attribute {pref[0]}.")
else:
print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have attribute {pref[0]}.")
if args.configure:
with open(args.configure[0], encoding='utf8') as file:
@@ -377,35 +370,35 @@ def onConnected(interface):
alt = 0
lat = 0.0
lon = 0.0
prefs = interface.localNode.radioConfig.preferences
localConfig = interface.localNode.localConfig
if 'alt' in configuration['location']:
alt = int(configuration['location']['alt'])
prefs.fixed_position = True
localConfig.fixed_position = True
print(f"Fixing altitude at {alt} meters")
if 'lat' in configuration['location']:
lat = float(configuration['location']['lat'])
prefs.fixed_position = True
localConfig.fixed_position = True
print(f"Fixing latitude at {lat} degrees")
if 'lon' in configuration['location']:
lon = float(configuration['location']['lon'])
prefs.fixed_position = True
localConfig.fixed_position = True
print(f"Fixing longitude at {lon} degrees")
print("Setting device position")
interface.sendPosition(lat, lon, alt)
interface.localNode.writeConfig()
if 'user_prefs' in configuration:
prefs = interface.getNode(args.dest).radioConfig.preferences
localConfig = interface.getNode(args.dest).localConfig
for pref in configuration['user_prefs']:
setPref(prefs, pref, str(configuration['user_prefs'][pref]))
setPref(localConfig, pref, str(configuration['user_prefs'][pref]))
print("Writing modified preferences to device")
interface.getNode(args.dest).writeConfig()
if 'userPrefs' in configuration:
prefs = interface.getNode(args.dest).radioConfig.preferences
localConfig = interface.getNode(args.dest).localConfig
for pref in configuration['userPrefs']:
setPref(prefs, pref, str(configuration['userPrefs'][pref]))
setPref(localConfig, pref, str(configuration['userPrefs'][pref]))
print("Writing modified preferences to device")
interface.getNode(args.dest).writeConfig()
@@ -454,69 +447,56 @@ def onConnected(interface):
print(f"Deleting channel {channelIndex}")
ch = interface.getNode(args.dest).deleteChannel(channelIndex)
ch_changes = [args.ch_vlongslow, args.ch_longslow, args.ch_longfast,
args.ch_midslow, args.ch_midfast,
args.ch_shortslow, args.ch_shortfast]
any_primary_channel_changes = any(x for x in ch_changes)
if args.ch_set or any_primary_channel_changes or args.ch_enable or args.ch_disable:
def setSimpleConfig(modem_preset):
"""Set one of the simple modem_config"""
# Completely new radio settings
chs = config_pb2.Config.LoRaConfig()
chs.modem_preset = modem_preset
prefs = interface.getNode(args.dest).localConfig
prefs.lora.CopyFrom(chs)
interface.getNode(args.dest).writeConfig()
# handle the simple radio set commands
if args.ch_vlongslow:
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.VLongSlow)
if args.ch_longslow:
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.LongSlow)
if args.ch_longfast:
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.LongFast)
if args.ch_midslow:
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.MidSlow)
if args.ch_midfast:
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.MidFast)
if args.ch_shortslow:
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.ShortSlow)
if args.ch_shortfast:
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.ShortFast)
if args.ch_set or args.ch_enable or args.ch_disable:
closeNow = True
channelIndex = our_globals.get_channel_index()
if channelIndex is None:
if any_primary_channel_changes:
# we assume that they want the primary channel if they're setting range values
channelIndex = 0
else:
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
ch = interface.getNode(args.dest).channels[channelIndex]
if any_primary_channel_changes or args.ch_enable or args.ch_disable:
if args.ch_enable or args.ch_disable:
if channelIndex == 0 and not any_primary_channel_changes:
if channelIndex == 0:
meshtastic.util.our_exit("Warning: Cannot enable/disable PRIMARY channel.")
if channelIndex != 0:
if any_primary_channel_changes:
meshtastic.util.our_exit("Warning: Standard channel settings can only be applied to the PRIMARY channel")
enable = True # default to enable
if args.ch_enable:
enable = True
if args.ch_disable:
enable = False
def setSimpleChannel(modem_config):
"""Set one of the simple modem_config only based channels"""
# Completely new channel settings
chs = channel_pb2.ChannelSettings()
chs.modem_config = modem_config
chs.psk = bytes([1]) # Use default channel psk 1
ch.settings.CopyFrom(chs)
# handle the simple channel set commands
if args.ch_vlongslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.VLongSlow)
if args.ch_longslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.LongSlow)
if args.ch_longfast:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.LongFast)
if args.ch_midslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.MidSlow)
if args.ch_midfast:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.MidFast)
if args.ch_shortslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.ShortSlow)
if args.ch_shortfast:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.ShortFast)
# Handle the channel settings
for pref in (args.ch_set or []):
if pref[0] == "psk":
@@ -552,12 +532,21 @@ def onConnected(interface):
if args.get:
closeNow = True
prefs = interface.getNode(args.dest).radioConfig.preferences
localConfig = interface.getNode(args.dest).localConfig
moduleConfig = interface.getNode(args.dest).moduleConfig
# Handle the int/float/bool arguments
for pref in args.get:
getPref(prefs, pref[0])
found = getPref(localConfig, pref[0])
if not found:
found = getPref(moduleConfig, pref[0])
if not found:
if Globals.getInstance().get_camel_case():
print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have an attribute {pref[0]}.")
else:
print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have attribute {pref[0]}.")
print("Completed getting preferences")
if args.nodes:
@@ -641,7 +630,7 @@ def export_config(interface):
if alt:
config += f" alt: {alt}\n"
config += "\n"
preferences = f'{interface.localNode.radioConfig.preferences}'
preferences = f'{interface.localNode.localConfig}'
prefs = preferences.splitlines()
if prefs:
if Globals.getInstance().get_camel_case():
@@ -849,9 +838,6 @@ def initParser():
parser.add_argument(
"--set-owner-short", help="Set device owner short name", action="store")
parser.add_argument(
"--set-team", help="Set team affiliation (an invalid team will list valid values)", action="store")
parser.add_argument(
"--set-ham", help="Set licensed Ham ID and turn off encryption", action="store")
@@ -913,13 +899,6 @@ def initParser():
parser.add_argument("--noproto", help="Don't start the API, just function as a dumb serial terminal.",
action="store_true")
parser.add_argument('--setchan', dest='deprecated', nargs=2, action='append',
help='Deprecated, use "--ch-set param value" instead')
parser.add_argument('--set-router', dest='deprecated',
action='store_true', help='Deprecated, use "--set is_router true" instead')
parser.add_argument('--unset-router', dest='deprecated',
action='store_false', help='Deprecated, use "--set is_router false" instead')
have_tunnel = platform.system() == 'Linux'
if have_tunnel:
parser.add_argument('--tunnel', action='store_true',

View File

@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x63onfig.proto\"\xf1\x11\n\x06\x43onfig\x12&\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x14.Config.DeviceConfigH\x00\x12*\n\x08position\x18\x02 \x01(\x0b\x32\x16.Config.PositionConfigH\x00\x12$\n\x05power\x18\x03 \x01(\x0b\x32\x13.Config.PowerConfigH\x00\x12\"\n\x04wifi\x18\x04 \x01(\x0b\x32\x12.Config.WiFiConfigH\x00\x12(\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfigH\x00\x12\"\n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfigH\x00\x1a\xd8\x01\n\x0c\x44\x65viceConfig\x12\'\n\x04role\x18\x01 \x01(\x0e\x32\x19.Config.DeviceConfig.Role\x12\x17\n\x0fserial_disabled\x18\x02 \x01(\x08\x12\x15\n\rfactory_reset\x18\x03 \x01(\x08\x12\x19\n\x11\x64\x65\x62ug_log_enabled\x18\x04 \x01(\x08\x12\x12\n\nntp_server\x18\x05 \x01(\t\"@\n\x04Role\x12\n\n\x06\x43lient\x10\x00\x12\x0e\n\nClientMute\x10\x01\x12\n\n\x06Router\x10\x02\x12\x10\n\x0cRouterClient\x10\x03\x1a\x86\x03\n\x0ePositionConfig\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12)\n!position_broadcast_smart_disabled\x18\x02 \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\x03 \x01(\x08\x12\x14\n\x0cgps_disabled\x18\x05 \x01(\x08\x12\x1b\n\x13gps_update_interval\x18\x06 \x01(\r\x12\x18\n\x10gps_attempt_time\x18\x07 \x01(\r\x12\x16\n\x0eposition_flags\x18\n \x01(\r\"\xaa\x01\n\rPositionFlags\x12\x11\n\rPOS_UNDEFINED\x10\x00\x12\x10\n\x0cPOS_ALTITUDE\x10\x01\x12\x0f\n\x0bPOS_ALT_MSL\x10\x02\x12\x0f\n\x0bPOS_GEO_SEP\x10\x04\x12\x0b\n\x07POS_DOP\x10\x08\x12\r\n\tPOS_HVDOP\x10\x10\x12\x11\n\rPOS_SATINVIEW\x10 \x12\x0f\n\x0bPOS_SEQ_NOS\x10@\x12\x12\n\rPOS_TIMESTAMP\x10\x80\x01\x1a\x8f\x04\n\x0bPowerConfig\x12\x39\n\x0e\x63harge_current\x18\x01 \x01(\x0e\x32!.Config.PowerConfig.ChargeCurrent\x12\x17\n\x0fis_power_saving\x18\x02 \x01(\x08\x12\x19\n\x11is_always_powered\x18\x03 \x01(\x08\x12&\n\x1eon_battery_shutdown_after_secs\x18\x04 \x01(\r\x12\x1f\n\x17\x61\x64\x63_multiplier_override\x18\x06 \x01(\x02\x12\x1b\n\x13wait_bluetooth_secs\x18\x07 \x01(\r\x12\x1d\n\x15mesh_sds_timeout_secs\x18\t \x01(\r\x12\x10\n\x08sds_secs\x18\n \x01(\r\x12\x0f\n\x07ls_secs\x18\x0b \x01(\r\x12\x15\n\rmin_wake_secs\x18\x0c \x01(\r\"\xd1\x01\n\rChargeCurrent\x12\x0b\n\x07MAUnset\x10\x00\x12\t\n\x05MA100\x10\x01\x12\t\n\x05MA190\x10\x02\x12\t\n\x05MA280\x10\x03\x12\t\n\x05MA360\x10\x04\x12\t\n\x05MA450\x10\x05\x12\t\n\x05MA550\x10\x06\x12\t\n\x05MA630\x10\x07\x12\t\n\x05MA700\x10\x08\x12\t\n\x05MA780\x10\t\x12\t\n\x05MA880\x10\n\x12\t\n\x05MA960\x10\x0b\x12\n\n\x06MA1000\x10\x0c\x12\n\n\x06MA1080\x10\r\x12\n\n\x06MA1160\x10\x0e\x12\n\n\x06MA1240\x10\x0f\x12\n\n\x06MA1320\x10\x10\x1aK\n\nWiFiConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03psk\x18\x02 \x01(\t\x12\x0f\n\x07\x61p_mode\x18\x03 \x01(\x08\x12\x11\n\tap_hidden\x18\x04 \x01(\x08\x1a\x8f\x02\n\rDisplayConfig\x12\x16\n\x0escreen_on_secs\x18\x01 \x01(\r\x12=\n\ngps_format\x18\x02 \x01(\x0e\x32).Config.DisplayConfig.GpsCoordinateFormat\x12!\n\x19\x61uto_screen_carousel_secs\x18\x03 \x01(\r\"\x83\x01\n\x13GpsCoordinateFormat\x12\x10\n\x0cGpsFormatDec\x10\x00\x12\x10\n\x0cGpsFormatDMS\x10\x01\x12\x10\n\x0cGpsFormatUTM\x10\x02\x12\x11\n\rGpsFormatMGRS\x10\x03\x12\x10\n\x0cGpsFormatOLC\x10\x04\x12\x11\n\rGpsFormatOSGR\x10\x05\x1a\x93\x04\n\nLoRaConfig\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x34\n\x0cmodem_preset\x18\x02 \x01(\x0e\x32\x1e.Config.LoRaConfig.ModemPreset\x12\x11\n\tbandwidth\x18\x03 \x01(\r\x12\x15\n\rspread_factor\x18\x04 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x05 \x01(\r\x12\x18\n\x10\x66requency_offset\x18\x06 \x01(\x02\x12-\n\x06region\x18\x07 \x01(\x0e\x32\x1d.Config.LoRaConfig.RegionCode\x12\x11\n\thop_limit\x18\x08 \x01(\r\x12\x13\n\x0btx_disabled\x18\t \x01(\x08\x12\x17\n\x0fignore_incoming\x18g \x03(\r\"\x81\x01\n\nRegionCode\x12\t\n\x05Unset\x10\x00\x12\x06\n\x02US\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x45U868\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08\x12\x06\n\x02RU\x10\t\x12\x06\n\x02IN\x10\n\x12\t\n\x05NZ865\x10\x0b\x12\x06\n\x02TH\x10\x0c\"p\n\x0bModemPreset\x12\x0c\n\x08LongFast\x10\x00\x12\x0c\n\x08LongSlow\x10\x01\x12\r\n\tVLongSlow\x10\x02\x12\x0b\n\x07MidSlow\x10\x03\x12\x0b\n\x07MidFast\x10\x04\x12\r\n\tShortSlow\x10\x05\x12\r\n\tShortFast\x10\x06\x42\x10\n\x0epayloadVariantBH\n\x13\x63om.geeksville.meshB\x0c\x43onfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x63onfig.proto\"\xf8\x11\n\x06\x43onfig\x12&\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x14.Config.DeviceConfigH\x00\x12*\n\x08position\x18\x02 \x01(\x0b\x32\x16.Config.PositionConfigH\x00\x12$\n\x05power\x18\x03 \x01(\x0b\x32\x13.Config.PowerConfigH\x00\x12\"\n\x04wifi\x18\x04 \x01(\x0b\x32\x12.Config.WiFiConfigH\x00\x12(\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfigH\x00\x12\"\n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfigH\x00\x1a\xd8\x01\n\x0c\x44\x65viceConfig\x12\'\n\x04role\x18\x01 \x01(\x0e\x32\x19.Config.DeviceConfig.Role\x12\x17\n\x0fserial_disabled\x18\x02 \x01(\x08\x12\x15\n\rfactory_reset\x18\x03 \x01(\x08\x12\x19\n\x11\x64\x65\x62ug_log_enabled\x18\x04 \x01(\x08\x12\x12\n\nntp_server\x18\x05 \x01(\t\"@\n\x04Role\x12\n\n\x06\x43lient\x10\x00\x12\x0e\n\nClientMute\x10\x01\x12\n\n\x06Router\x10\x02\x12\x10\n\x0cRouterClient\x10\x03\x1a\xa8\x03\n\x0ePositionConfig\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12)\n!position_broadcast_smart_disabled\x18\x02 \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\x03 \x01(\x08\x12\x14\n\x0cgps_disabled\x18\x05 \x01(\x08\x12\x1b\n\x13gps_update_interval\x18\x06 \x01(\r\x12\x18\n\x10gps_attempt_time\x18\x07 \x01(\r\x12\x16\n\x0eposition_flags\x18\n \x01(\r\"\xcc\x01\n\rPositionFlags\x12\x11\n\rPOS_UNDEFINED\x10\x00\x12\x10\n\x0cPOS_ALTITUDE\x10\x01\x12\x0f\n\x0bPOS_ALT_MSL\x10\x02\x12\x0f\n\x0bPOS_GEO_SEP\x10\x04\x12\x0b\n\x07POS_DOP\x10\x08\x12\r\n\tPOS_HVDOP\x10\x10\x12\x11\n\rPOS_SATINVIEW\x10 \x12\x0f\n\x0bPOS_SEQ_NOS\x10@\x12\x12\n\rPOS_TIMESTAMP\x10\x80\x01\x12\x10\n\x0bPOS_HEADING\x10\x80\x02\x12\x0e\n\tPOS_SPEED\x10\x80\x04\x1a\xf4\x03\n\x0bPowerConfig\x12\x39\n\x0e\x63harge_current\x18\x01 \x01(\x0e\x32!.Config.PowerConfig.ChargeCurrent\x12\x17\n\x0fis_power_saving\x18\x02 \x01(\x08\x12&\n\x1eon_battery_shutdown_after_secs\x18\x04 \x01(\r\x12\x1f\n\x17\x61\x64\x63_multiplier_override\x18\x06 \x01(\x02\x12\x1b\n\x13wait_bluetooth_secs\x18\x07 \x01(\r\x12\x1d\n\x15mesh_sds_timeout_secs\x18\t \x01(\r\x12\x10\n\x08sds_secs\x18\n \x01(\r\x12\x0f\n\x07ls_secs\x18\x0b \x01(\r\x12\x15\n\rmin_wake_secs\x18\x0c \x01(\r\"\xd1\x01\n\rChargeCurrent\x12\x0b\n\x07MAUnset\x10\x00\x12\t\n\x05MA100\x10\x01\x12\t\n\x05MA190\x10\x02\x12\t\n\x05MA280\x10\x03\x12\t\n\x05MA360\x10\x04\x12\t\n\x05MA450\x10\x05\x12\t\n\x05MA550\x10\x06\x12\t\n\x05MA630\x10\x07\x12\t\n\x05MA700\x10\x08\x12\t\n\x05MA780\x10\t\x12\t\n\x05MA880\x10\n\x12\t\n\x05MA960\x10\x0b\x12\n\n\x06MA1000\x10\x0c\x12\n\n\x06MA1080\x10\r\x12\n\n\x06MA1160\x10\x0e\x12\n\n\x06MA1240\x10\x0f\x12\n\n\x06MA1320\x10\x10\x1aK\n\nWiFiConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03psk\x18\x02 \x01(\t\x12\x0f\n\x07\x61p_mode\x18\x03 \x01(\x08\x12\x11\n\tap_hidden\x18\x04 \x01(\x08\x1a\x8f\x02\n\rDisplayConfig\x12\x16\n\x0escreen_on_secs\x18\x01 \x01(\r\x12=\n\ngps_format\x18\x02 \x01(\x0e\x32).Config.DisplayConfig.GpsCoordinateFormat\x12!\n\x19\x61uto_screen_carousel_secs\x18\x03 \x01(\r\"\x83\x01\n\x13GpsCoordinateFormat\x12\x10\n\x0cGpsFormatDec\x10\x00\x12\x10\n\x0cGpsFormatDMS\x10\x01\x12\x10\n\x0cGpsFormatUTM\x10\x02\x12\x11\n\rGpsFormatMGRS\x10\x03\x12\x10\n\x0cGpsFormatOLC\x10\x04\x12\x11\n\rGpsFormatOSGR\x10\x05\x1a\x93\x04\n\nLoRaConfig\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x34\n\x0cmodem_preset\x18\x02 \x01(\x0e\x32\x1e.Config.LoRaConfig.ModemPreset\x12\x11\n\tbandwidth\x18\x03 \x01(\r\x12\x15\n\rspread_factor\x18\x04 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x05 \x01(\r\x12\x18\n\x10\x66requency_offset\x18\x06 \x01(\x02\x12-\n\x06region\x18\x07 \x01(\x0e\x32\x1d.Config.LoRaConfig.RegionCode\x12\x11\n\thop_limit\x18\x08 \x01(\r\x12\x13\n\x0btx_disabled\x18\t \x01(\x08\x12\x17\n\x0fignore_incoming\x18g \x03(\r\"\x81\x01\n\nRegionCode\x12\t\n\x05Unset\x10\x00\x12\x06\n\x02US\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x45U868\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08\x12\x06\n\x02RU\x10\t\x12\x06\n\x02IN\x10\n\x12\t\n\x05NZ865\x10\x0b\x12\x06\n\x02TH\x10\x0c\"p\n\x0bModemPreset\x12\x0c\n\x08LongFast\x10\x00\x12\x0c\n\x08LongSlow\x10\x01\x12\r\n\tVLongSlow\x10\x02\x12\x0b\n\x07MedSlow\x10\x03\x12\x0b\n\x07MedFast\x10\x04\x12\r\n\tShortSlow\x10\x05\x12\r\n\tShortFast\x10\x06\x42\x10\n\x0epayloadVariantBH\n\x13\x63om.geeksville.meshB\x0c\x43onfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
@@ -91,29 +91,29 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014ConfigProtosH\003Z!github.com/meshtastic/gomeshproto'
_CONFIG._serialized_start=17
_CONFIG._serialized_end=2306
_CONFIG._serialized_end=2313
_CONFIG_DEVICECONFIG._serialized_start=264
_CONFIG_DEVICECONFIG._serialized_end=480
_CONFIG_DEVICECONFIG_ROLE._serialized_start=416
_CONFIG_DEVICECONFIG_ROLE._serialized_end=480
_CONFIG_POSITIONCONFIG._serialized_start=483
_CONFIG_POSITIONCONFIG._serialized_end=873
_CONFIG_POSITIONCONFIG._serialized_end=907
_CONFIG_POSITIONCONFIG_POSITIONFLAGS._serialized_start=703
_CONFIG_POSITIONCONFIG_POSITIONFLAGS._serialized_end=873
_CONFIG_POWERCONFIG._serialized_start=876
_CONFIG_POWERCONFIG._serialized_end=1403
_CONFIG_POWERCONFIG_CHARGECURRENT._serialized_start=1194
_CONFIG_POWERCONFIG_CHARGECURRENT._serialized_end=1403
_CONFIG_WIFICONFIG._serialized_start=1405
_CONFIG_WIFICONFIG._serialized_end=1480
_CONFIG_DISPLAYCONFIG._serialized_start=1483
_CONFIG_DISPLAYCONFIG._serialized_end=1754
_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_start=1623
_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_end=1754
_CONFIG_LORACONFIG._serialized_start=1757
_CONFIG_LORACONFIG._serialized_end=2288
_CONFIG_LORACONFIG_REGIONCODE._serialized_start=2045
_CONFIG_LORACONFIG_REGIONCODE._serialized_end=2174
_CONFIG_LORACONFIG_MODEMPRESET._serialized_start=2176
_CONFIG_LORACONFIG_MODEMPRESET._serialized_end=2288
_CONFIG_POSITIONCONFIG_POSITIONFLAGS._serialized_end=907
_CONFIG_POWERCONFIG._serialized_start=910
_CONFIG_POWERCONFIG._serialized_end=1410
_CONFIG_POWERCONFIG_CHARGECURRENT._serialized_start=1201
_CONFIG_POWERCONFIG_CHARGECURRENT._serialized_end=1410
_CONFIG_WIFICONFIG._serialized_start=1412
_CONFIG_WIFICONFIG._serialized_end=1487
_CONFIG_DISPLAYCONFIG._serialized_start=1490
_CONFIG_DISPLAYCONFIG._serialized_end=1761
_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_start=1630
_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_end=1761
_CONFIG_LORACONFIG._serialized_start=1764
_CONFIG_LORACONFIG._serialized_end=2295
_CONFIG_LORACONFIG_REGIONCODE._serialized_start=2052
_CONFIG_LORACONFIG_REGIONCODE._serialized_end=2181
_CONFIG_LORACONFIG_MODEMPRESET._serialized_start=2183
_CONFIG_LORACONFIG_MODEMPRESET._serialized_end=2295
# @@protoc_insertion_point(module_scope)

View File

@@ -362,11 +362,9 @@ class MeshInterface:
def waitForConfig(self):
"""Block until radio config is received. Returns True if config has been received."""
# TODO
return True
#success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
#if not success:
#raise Exception("Timed out waiting for interface config")
success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
if not success:
raise Exception("Timed out waiting for interface config")
def getMyNodeInfo(self):
"""Get info about my node."""
@@ -424,10 +422,8 @@ class MeshInterface:
"""We need to send a heartbeat message to the device every X seconds"""
def callback():
self.heartbeatTimer = None
# TODO
# prefs = self.localNode.radioConfig.preferences
#i = prefs.phone_timeout_secs / 2
i = 0
prefs = self.localNode.localConfig
i = prefs.power.ls_secs / 2
logging.debug(f"Sending heartbeat, interval {i}")
if i != 0:
self.heartbeatTimer = threading.Timer(i, callback)
@@ -536,29 +532,44 @@ class MeshInterface:
# stream API fromRadio.config_complete_id
logging.debug(f"Config complete ID {self.configId}")
self._handleConfigComplete()
elif fromRadio.HasField("packet"):
self._handlePacketFromRadio(fromRadio.packet)
elif fromRadio.rebooted:
# Tell clients the device went away. Careful not to call the overridden
# subclass version that closes the serial port
MeshInterface._disconnected(self)
self._startConfig() # redownload the node db etc...
elif fromRadio.config:
logging.debug("Hey! We got some configs")
elif fromRadio.config or fromRadio.moduleConfig:
if fromRadio.config.HasField("device"):
logging.debug("device!")
# TODO: do something with this config
self.localNode.localConfig.device.CopyFrom(fromRadio.config.device)
elif fromRadio.config.HasField("position"):
logging.debug("position!")
self.localNode.localConfig.position.CopyFrom(fromRadio.config.position)
elif fromRadio.config.HasField("power"):
logging.debug("power!")
self.localNode.localConfig.power.CopyFrom(fromRadio.config.power)
elif fromRadio.config.HasField("wifi"):
logging.debug("wifi!")
self.localNode.localConfig.wifi.CopyFrom(fromRadio.config.wifi)
elif fromRadio.config.HasField("display"):
logging.debug("display!")
self.localNode.localConfig.display.CopyFrom(fromRadio.config.display)
elif fromRadio.config.HasField("lora"):
logging.debug("lora!")
self.localNode.localConfig.lora.CopyFrom(fromRadio.config.lora)
elif fromRadio.moduleConfig.HasField("mqtt"):
self.localNode.moduleConfig.mqtt.CopyFrom(fromRadio.moduleConfig.mqtt)
elif fromRadio.moduleConfig.HasField("serial"):
self.localNode.moduleConfig.serial.CopyFrom(fromRadio.moduleConfig.serial)
elif fromRadio.moduleConfig.HasField("external_notification"):
self.localNode.moduleConfig.external_notification.CopyFrom(fromRadio.moduleConfig.external_notification)
elif fromRadio.moduleConfig.HasField("range_test"):
self.localNode.moduleConfig.range_test.CopyFrom(fromRadio.moduleConfig.range_test)
elif fromRadio.moduleConfig.HasField("telemetry"):
self.localNode.moduleConfig.telemetry.CopyFrom(fromRadio.moduleConfig.telemetry)
elif fromRadio.moduleConfig.HasField("canned_message"):
self.localNode.moduleConfig.canned_message.CopyFrom(fromRadio.moduleConfig.canned_message)
else:
logging.debug("Unexpected FromRadio payload")

View File

File diff suppressed because one or more lines are too long

View File

@@ -5,22 +5,22 @@ import logging
import base64
import time
from google.protobuf.json_format import MessageToJson
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2, localonly_pb2
from meshtastic.util import pskToString, stripnl, Timeout, our_exit, fromPSK
class Node:
"""A model of a (local or remote) node in the mesh
Includes methods for radioConfig and channels
Includes methods for localConfig, moduleConfig and channels
"""
def __init__(self, iface, nodeNum, noProto=False):
"""Constructor"""
self.iface = iface
self.nodeNum = nodeNum
self.radioConfig = None
self.partialConfig = None
self.localConfig = localonly_pb2.LocalConfig()
self.moduleConfig = localonly_pb2.LocalModuleConfig()
self.channels = None
self._timeout = Timeout(maxSecs=300)
self.partialChannels = None
@@ -55,28 +55,22 @@ class Node:
def showInfo(self):
"""Show human readable description of our node"""
prefs = ""
if self.radioConfig and self.radioConfig.preferences:
prefs = stripnl(MessageToJson(self.radioConfig.preferences))
if self.localConfig:
prefs = stripnl(MessageToJson(self.localConfig))
print(f"Preferences: {prefs}\n")
prefs = ""
if self.moduleConfig:
prefs = stripnl(MessageToJson(self.moduleConfig))
print(f"Module preferences: {prefs}\n")
self.showChannels()
def requestConfig(self):
"""Send regular MeshPackets to ask for settings and channels."""
logging.debug(f"requestConfig for nodeNum:{self.nodeNum}")
self.radioConfig = None
self.partialConfig = []
self.channels = None
self.partialChannels = [] # We keep our channels in a temp array until finished
# Note: We do not get the canned plugin message, unless get_canned_message() is called
self.cannedPluginMessage = None
self.cannedPluginMessagePart1 = None
self.cannedPluginMessagePart2 = None
self.cannedPluginMessagePart3 = None
self.cannedPluginMessagePart4 = None
self._requestSettings()
self._requestChannel(0)
def turnOffEncryptionOnPrimaryChannel(self):
"""Turn off encryption on primary channel."""
@@ -86,25 +80,109 @@ class Node:
def waitForConfig(self, attribute='channels'):
"""Block until radio config is received. Returns True if config has been received."""
return self._timeout.waitForSet(self, attrs=('config', attribute))
return self._timeout.waitForSet(self, attrs=('localConfig', attribute))
def writeConfig(self):
"""Write the current (edited) radioConfig to the device"""
if self.radioConfig is None:
our_exit("Error: No RadioConfig has been read")
"""Write the current (edited) localConfig to the device"""
if self.localConfig is None:
our_exit("Error: No localConfig has been read")
p = admin_pb2.AdminMessage()
p.set_radio.CopyFrom(self.radioConfig)
if self.localConfig.device:
p = admin_pb2.AdminMessage()
p.set_config.device.CopyFrom(self.localConfig.device)
self._sendAdmin(p)
logging.debug("Wrote device")
time.sleep(0.1)
self._sendAdmin(p)
logging.debug("Wrote config")
if self.localConfig.position:
p = admin_pb2.AdminMessage()
p.set_config.position.CopyFrom(self.localConfig.position)
self._sendAdmin(p)
logging.debug("Wrote position")
time.sleep(0.1)
if self.localConfig.power:
p = admin_pb2.AdminMessage()
p.set_config.power.CopyFrom(self.localConfig.power)
self._sendAdmin(p)
logging.debug("Wrote power")
time.sleep(0.1)
if self.localConfig.wifi:
p = admin_pb2.AdminMessage()
p.set_config.wifi.CopyFrom(self.localConfig.wifi)
self._sendAdmin(p)
logging.debug("Wrote wifi")
time.sleep(0.1)
if self.localConfig.display:
p = admin_pb2.AdminMessage()
p.set_config.display.CopyFrom(self.localConfig.display)
self._sendAdmin(p)
logging.debug("Wrote display")
time.sleep(0.1)
if self.localConfig.lora:
p = admin_pb2.AdminMessage()
p.set_config.lora.CopyFrom(self.localConfig.lora)
self._sendAdmin(p)
logging.debug("Wrote lora")
time.sleep(0.1)
if self.moduleConfig.mqtt:
p = admin_pb2.AdminMessage()
p.set_module_config.mqtt.CopyFrom(self.moduleConfig.mqtt)
self._sendAdmin(p)
logging.debug("Wrote module: mqtt")
time.sleep(0.1)
if self.moduleConfig.serial:
p = admin_pb2.AdminMessage()
p.set_module_config.serial.CopyFrom(self.moduleConfig.serial)
self._sendAdmin(p)
logging.debug("Wrote module: serial")
time.sleep(0.1)
if self.moduleConfig.external_notification:
p = admin_pb2.AdminMessage()
p.set_module_config.external_notification.CopyFrom(self.moduleConfig.external_notification)
self._sendAdmin(p)
logging.debug("Wrote module: external_notification")
time.sleep(0.1)
if self.moduleConfig.store_forward:
p = admin_pb2.AdminMessage()
p.set_module_config.store_forward.CopyFrom(self.moduleConfig.store_forward)
self._sendAdmin(p)
logging.debug("Wrote module: store_forward")
time.sleep(0.1)
if self.moduleConfig.range_test:
p = admin_pb2.AdminMessage()
p.set_module_config.range_test.CopyFrom(self.moduleConfig.range_test)
self._sendAdmin(p)
logging.debug("Wrote module: range_test")
time.sleep(0.1)
if self.moduleConfig.telemetry:
p = admin_pb2.AdminMessage()
p.set_module_config.telemetry.CopyFrom(self.moduleConfig.telemetry)
self._sendAdmin(p)
logging.debug("Wrote module: telemetry")
time.sleep(0.1)
if self.moduleConfig.canned_message:
p = admin_pb2.AdminMessage()
p.set_module_config.canned_message.CopyFrom(self.moduleConfig.canned_message)
self._sendAdmin(p)
logging.debug("Wrote module: canned_message")
time.sleep(0.1)
def writeChannel(self, channelIndex, adminIndex=0):
"""Write the current (edited) channel to the device"""
p = admin_pb2.AdminMessage()
p.set_channel.CopyFrom(self.channels[channelIndex])
self._sendAdmin(p, adminIndex=adminIndex)
logging.debug(f"Wrote channel {channelIndex}")
@@ -165,7 +243,7 @@ class Node:
else:
return 0
def setOwner(self, long_name=None, short_name=None, is_licensed=False, team=None):
def setOwner(self, long_name=None, short_name=None, is_licensed=False):
"""Set device owner name"""
logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
nChars = 3
@@ -194,14 +272,11 @@ class Node:
short_name = short_name[:nChars]
p.set_owner.short_name = short_name
p.set_owner.is_licensed = is_licensed
if team is not None:
p.set_owner.team = team
# 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.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.team:{p.set_owner.team}')
return self._sendAdmin(p)
def getURL(self, includeAll: bool = True):
@@ -218,8 +293,8 @@ class Node:
def setURL(self, url):
"""Set mesh network URL"""
if self.radioConfig is None:
our_exit("Warning: No RadioConfig has been read")
if self.localConfig is None:
our_exit("Warning: No Config has been read")
# URLs are of the form https://www.meshtastic.org/d/#{base64_channel_set}
# Split on '/#' to find the base64 encoded channel settings
@@ -252,66 +327,6 @@ class Node:
self.writeChannel(ch.index)
i = i + 1
def onResponseRequestSettings(self, p):
"""Handle the response packets for requesting settings _requestSettings()"""
logging.debug(f'onResponseRequestSetting() p:{p}')
errorFound = False
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
errorFound = True
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
# TODO
#if errorFound is False:
#self.partialConfig[p["decoded"]["admin"]["payloadVariant"]] = p["decoded"]["admin"]["raw"].get_config_response
#logging.debug(f'self.partialConfig:{self.partialConfig}')
#self._timeout.reset() # We made foreward progress
#self.gotResponse = True
def _requestSettings(self):
"""Done with initial config messages, now send regular
MeshPackets to ask for settings."""
# TODO: should we check that localNode has an 'admin' channel?
# Show progress message for super slow operations
if self != self.iface.localNode:
print("Requesting preferences from remote node.")
print("Be sure:")
print(" 1. There is a SECONDARY channel named 'admin'.")
print(" 2. The '--seturl' was used to configure.")
print(" 3. All devices have the same modem config. (i.e., '--ch-longfast')")
print(" 4. All devices have been rebooted after all of the above. (optional, but recommended)")
print("Note: This could take a while (it requests remote channel configs, then writes config)")
p1 = admin_pb2.AdminMessage()
p1.get_config_request = admin_pb2.AdminMessage.ConfigType.DEVICE_CONFIG
self._sendAdmin(p1, wantResponse=True, onResponse=self.onResponseRequestSettings)
p2 = admin_pb2.AdminMessage()
p2.get_config_request = admin_pb2.AdminMessage.ConfigType.POSITION_CONFIG
self._sendAdmin(p2, wantResponse=True, onResponse=self.onResponseRequestSettings)
p3 = admin_pb2.AdminMessage()
p3.get_config_request = admin_pb2.AdminMessage.ConfigType.POWER_CONFIG
self._sendAdmin(p3, wantResponse=True, onResponse=self.onResponseRequestSettings)
p4 = admin_pb2.AdminMessage()
p4.get_config_request = admin_pb2.AdminMessage.ConfigType.WIFI_CONFIG
self._sendAdmin(p4, wantResponse=True, onResponse=self.onResponseRequestSettings)
p5 = admin_pb2.AdminMessage()
p5.get_config_request = admin_pb2.AdminMessage.ConfigType.DISPLAY_CONFIG
self._sendAdmin(p5, wantResponse=True, onResponse=self.onResponseRequestSettings)
p6 = admin_pb2.AdminMessage()
p6.get_config_request = admin_pb2.AdminMessage.ConfigType.LORA_CONFIG
self._sendAdmin(p6, wantResponse=True, onResponse=self.onResponseRequestSettings)
# TODO Assemble radioConfig
logging.debug("Received config, now fetching channels...")
self._requestChannel(0) # now start fetching channels
def onResponseRequestCannedMessagePluginMessagePart1(self, p):
"""Handle the response packet for requesting canned message plugin message part 1"""
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart1() p:{p}')

View File

@@ -789,63 +789,6 @@ def test_main_setalt(capsys):
assert err == ''
mo.assert_called()
# TODO
#@pytest.mark.unit
#@pytest.mark.usefixtures("reset_globals")
#def test_main_set_team_valid(capsys):
# """Test --set-team"""
# sys.argv = ['', '--set-team', 'CYAN']
# Globals.getInstance().set_args(sys.argv)
#
# mocked_node = MagicMock(autospec=Node)
# def mock_setOwner(team):
# print('inside mocked setOwner')
# print(f'{team}')
# mocked_node.setOwner.side_effect = mock_setOwner
#
# iface = MagicMock(autospec=SerialInterface)
# iface.localNode.return_value = mocked_node
#
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.mesh_pb2.Team') as mm:
# mm.Name.return_value = 'FAKENAME'
# mm.Value.return_value = 'FAKEVAL'
# main()
# out, err = capsys.readouterr()
# assert re.search(r'Connected to radio', out, re.MULTILINE)
# assert re.search(r'Setting team to', out, re.MULTILINE)
# assert err == ''
# mo.assert_called()
# mm.Name.assert_called()
# mm.Value.assert_called()
#
# TODO
#@pytest.mark.unit
#@pytest.mark.usefixtures("reset_globals")
#def test_main_set_team_invalid(capsys):
# """Test --set-team using an invalid team name"""
# sys.argv = ['', '--set-team', 'NOTCYAN']
# Globals.getInstance().set_args(sys.argv)
#
# iface = MagicMock(autospec=SerialInterface)
#
# def throw_an_exception(exc):
# raise ValueError("Fake exception.")
#
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.mesh_pb2.Team') as mm:
# mm.Value.side_effect = throw_an_exception
# main()
# out, err = capsys.readouterr()
# assert re.search(r'Connected to radio', out, re.MULTILINE)
# assert re.search(r'ERROR: Team', out, re.MULTILINE)
# assert err == ''
# mo.assert_called()
# mm.Value.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_globals")
def test_main_seturl(capsys):
@@ -1541,24 +1484,6 @@ def test_main_get_with_invalid(capsys):
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_globals")
def test_main_setchan(capsys):
"""Test --setchan (deprecated)"""
sys.argv = ['', '--setchan', 'a', 'b']
Globals.getInstance().set_args(sys.argv)
iface = MagicMock(autospec=SerialInterface)
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
with pytest.raises(SystemExit) as pytest_wrapped_e:
main()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
_, err = capsys.readouterr()
assert re.search(r'usage:', err, re.MULTILINE)
@pytest.mark.unit
@pytest.mark.usefixtures("reset_globals")
def test_main_onReceive_empty(caplog, capsys):

View File

@@ -164,19 +164,6 @@ from ..channel_pb2 import Channel
# assert err == ''
# TODO
#@pytest.mark.unit
#def test_setOwner_and_team(caplog):
# """Test setOwner"""
# anode = Node('foo', 'bar', noProto=True)
# with caplog.at_level(logging.DEBUG):
# anode.setOwner(long_name ='Test123', short_name='123', team=1)
# assert re.search(r'p.set_owner.long_name:Test123:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.short_name:123:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.team:1', caplog.text, re.MULTILINE)
# TODO
#@pytest.mark.unit
#def test_setOwnerShort(caplog):
@@ -197,7 +184,6 @@ from ..channel_pb2 import Channel
# assert re.search(r'p.set_owner.long_name:Test123:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.short_name:Tst:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
# TODO
@@ -210,7 +196,6 @@ from ..channel_pb2 import Channel
# assert re.search(r'p.set_owner.long_name:Tnt:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.short_name:Tnt:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
# TODO
@@ -223,7 +208,6 @@ from ..channel_pb2 import Channel
# assert re.search(r'p.set_owner.long_name:A B C:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.short_name:ABC:', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.is_licensed:True', caplog.text, re.MULTILINE)
# assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
# TODO

View File

@@ -214,26 +214,6 @@ def test_smoke1_set_owner():
assert return_value == 0
@pytest.mark.smoke1
def test_smoke1_set_team():
"""Test --set-team """
# unset the team
return_value, out = subprocess.getstatusoutput('meshtastic --set-team CLEAR')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --set-team CYAN')
assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --info')
assert re.search(r'CYAN', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smoke1
def test_smoke1_ch_set_modem_config():
"""Test --ch-set modem_config"""

View File

@@ -219,26 +219,6 @@ def test_smokevirt_set_owner():
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_set_team():
"""Test --set-team """
# unset the team
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-team CLEAR')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-team CYAN')
assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'CYAN', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_ch_values():
"""Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,

2
proto

Submodule proto updated: 94a4dbb842...c63a16c32f

View File

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