mirror of
https://github.com/meshtastic/python.git
synced 2026-01-05 06:17:55 -05:00
Remove the -E files
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,675 +0,0 @@
|
||||
#!python3
|
||||
|
||||
import argparse
|
||||
import platform
|
||||
import logging
|
||||
import sys
|
||||
import codecs
|
||||
import time
|
||||
import base64
|
||||
import os
|
||||
from . import SerialInterface, TCPInterface, BLEInterface, test, remote_hardware
|
||||
from pubsub import pub
|
||||
from . import mesh_pb2, portnums_pb2, channel_pb2
|
||||
from .util import stripnl
|
||||
import google.protobuf.json_format
|
||||
import pyqrcode
|
||||
import traceback
|
||||
import pkg_resources
|
||||
|
||||
"""We only import the tunnel code if we are on a platform that can run it"""
|
||||
have_tunnel = platform.system() == 'Linux'
|
||||
|
||||
"""The command line arguments"""
|
||||
args = None
|
||||
|
||||
"""The parser for arguments"""
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
channelIndex = 0
|
||||
|
||||
|
||||
def onReceive(packet, interface):
|
||||
"""Callback invoked when a packet arrives"""
|
||||
try:
|
||||
d = packet.get('decoded')
|
||||
|
||||
# Exit once we receive a reply
|
||||
if args.sendtext and packet["to"] == interface.myInfo.my_node_num and d["portnum"] == portnums_pb2.PortNum.TEXT_MESSAGE_APP:
|
||||
interface.close() # after running command then exit
|
||||
|
||||
# Reply to every received message with some stats
|
||||
if args.reply:
|
||||
msg = d.get('text')
|
||||
if msg:
|
||||
#shortName = packet['decoded']['shortName']
|
||||
rxSnr = packet['rxSnr']
|
||||
hopLimit = packet['hopLimit']
|
||||
print(f"message: {msg}")
|
||||
reply = "got msg \'{}\' with rxSnr: {} and hopLimit: {}".format(
|
||||
msg, rxSnr, hopLimit)
|
||||
print("Sending reply: ", reply)
|
||||
interface.sendText(reply)
|
||||
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
|
||||
|
||||
def onConnection(interface, topic=pub.AUTO_TOPIC):
|
||||
"""Callback invoked when we connect/disconnect from a radio"""
|
||||
print(f"Connection changed: {topic.getName()}")
|
||||
|
||||
|
||||
trueTerms = {"t", "true", "yes"}
|
||||
falseTerms = {"f", "false", "no"}
|
||||
|
||||
|
||||
def genPSK256():
|
||||
return os.urandom(32)
|
||||
|
||||
|
||||
def fromPSK(valstr):
|
||||
"""A special version of fromStr that assumes the user is trying to set a PSK.
|
||||
In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN
|
||||
"""
|
||||
if valstr == "random":
|
||||
return genPSK256()
|
||||
elif valstr == "none":
|
||||
return bytes([0]) # Use the 'no encryption' PSK
|
||||
elif valstr == "default":
|
||||
return bytes([1]) # Use default channel psk
|
||||
elif valstr.startswith("simple"):
|
||||
# Use one of the single byte encodings
|
||||
return bytes([int(valstr[6:]) + 1])
|
||||
else:
|
||||
return fromStr(valstr)
|
||||
|
||||
|
||||
def fromStr(valstr):
|
||||
"""try to parse as int, float or bool (and fallback to a string as last resort)
|
||||
|
||||
Returns: an int, bool, float, str or byte array (for strings of hex digits)
|
||||
|
||||
Args:
|
||||
valstr (string): A user provided string
|
||||
"""
|
||||
if(len(valstr) == 0): # Treat an emptystring as an empty bytes
|
||||
val = bytes()
|
||||
elif(valstr.startswith('0x')):
|
||||
# if needed convert to string with asBytes.decode('utf-8')
|
||||
val = bytes.fromhex(valstr[2:])
|
||||
elif valstr in trueTerms:
|
||||
val = True
|
||||
elif valstr in falseTerms:
|
||||
val = False
|
||||
else:
|
||||
try:
|
||||
val = int(valstr)
|
||||
except ValueError:
|
||||
try:
|
||||
val = float(valstr)
|
||||
except ValueError:
|
||||
val = valstr # Not a float or an int, assume string
|
||||
|
||||
return val
|
||||
|
||||
|
||||
never = 0xffffffff
|
||||
oneday = 24 * 60 * 60
|
||||
|
||||
|
||||
def getPref(attributes, name):
|
||||
"""Get a channel or preferences value"""
|
||||
|
||||
objDesc = attributes.DESCRIPTOR
|
||||
field = objDesc.fields_by_name.get(name)
|
||||
if not field:
|
||||
print(f"{attributes.__class__.__name__} doesn't have an attribute called {name}, so you can not get it.")
|
||||
print(f"Choices are:")
|
||||
for f in objDesc.fields:
|
||||
print(f" {f.name}")
|
||||
return
|
||||
|
||||
# okay - try to read the value
|
||||
try:
|
||||
try:
|
||||
val = getattr(attributes, name)
|
||||
except TypeError as ex:
|
||||
# The getter didn't like our arg type guess try again as a string
|
||||
val = getattr(attributes, name)
|
||||
|
||||
# succeeded!
|
||||
print(f"{name}: {str(val)}")
|
||||
except Exception as ex:
|
||||
print(f"Can't get {name} due to {ex}")
|
||||
|
||||
|
||||
def setPref(attributes, name, valStr):
|
||||
"""Set a channel or preferences value"""
|
||||
|
||||
objDesc = attributes.DESCRIPTOR
|
||||
field = objDesc.fields_by_name.get(name)
|
||||
if not field:
|
||||
print(f"{attributes.__class__.__name__} doesn't have an attribute called {name}, so you can not set it.")
|
||||
print(f"Choices are:")
|
||||
for f in objDesc.fields:
|
||||
print(f" {f.name}")
|
||||
return
|
||||
|
||||
val = fromStr(valStr)
|
||||
|
||||
enumType = field.enum_type
|
||||
if enumType and type(val) == str:
|
||||
# We've failed so far to convert this string into an enum, try to find it by reflection
|
||||
e = enumType.values_by_name.get(val)
|
||||
if e:
|
||||
val = e.number
|
||||
else:
|
||||
print(f"{name} doesn't have an enum called {val}, so you can not set it.")
|
||||
print(f"Choices are:")
|
||||
for f in enumType.values:
|
||||
print(f" {f.name}")
|
||||
return
|
||||
|
||||
# okay - try to read the value
|
||||
try:
|
||||
try:
|
||||
setattr(attributes, name, val)
|
||||
except TypeError as ex:
|
||||
# The setter didn't like our arg type guess try again as a string
|
||||
setattr(attributes, name, valStr)
|
||||
|
||||
# succeeded!
|
||||
print(f"Set {name} to {valStr}")
|
||||
except Exception as ex:
|
||||
print(f"Can't set {name} due to {ex}")
|
||||
|
||||
|
||||
targetNode = None
|
||||
|
||||
|
||||
def onConnected(interface):
|
||||
"""Callback invoked when we connect to a radio"""
|
||||
closeNow = False # Should we drop the connection after we finish?
|
||||
try:
|
||||
global args
|
||||
print("Connected to radio")
|
||||
|
||||
def getNode():
|
||||
"""This operation could be expensive, so we try to cache the results"""
|
||||
global targetNode
|
||||
if not targetNode:
|
||||
targetNode = interface.getNode(args.destOrLocal)
|
||||
return targetNode
|
||||
|
||||
if args.setlat or args.setlon or args.setalt:
|
||||
closeNow = True
|
||||
|
||||
alt = 0
|
||||
lat = 0.0
|
||||
lon = 0.0
|
||||
time = 0 # always set time, but based on the local clock
|
||||
prefs = interface.localNode.radioConfig.preferences
|
||||
if args.setalt:
|
||||
alt = int(args.setalt)
|
||||
prefs.fixed_position = True
|
||||
print(f"Fixing altitude at {alt} meters")
|
||||
if args.setlat:
|
||||
lat = float(args.setlat)
|
||||
prefs.fixed_position = True
|
||||
print(f"Fixing latitude at {lat} degrees")
|
||||
if args.setlon:
|
||||
lon = float(args.setlon)
|
||||
prefs.fixed_position = True
|
||||
print(f"Fixing longitude at {lon} degrees")
|
||||
|
||||
print("Setting device position")
|
||||
# can include lat/long/alt etc: latitude = 37.5, longitude = -122.1
|
||||
interface.sendPosition(lat, lon, alt, time)
|
||||
interface.localNode.writeConfig()
|
||||
elif not args.no_time:
|
||||
# We normally provide a current time to the mesh when we connect
|
||||
interface.sendPosition()
|
||||
|
||||
if args.set_owner:
|
||||
closeNow = True
|
||||
print(f"Setting device owner to {args.set_owner}")
|
||||
getNode().setOwner(args.set_owner)
|
||||
|
||||
if args.set_ham:
|
||||
closeNow = True
|
||||
print(
|
||||
f"Setting HAM ID to {args.set_ham} and turning off encryption")
|
||||
getNode().setOwner(args.set_ham, is_licensed=True)
|
||||
# Must turn off crypt on primary channel
|
||||
ch = getNode().channels[0]
|
||||
ch.settings.psk = fromPSK("none")
|
||||
print(f"Writing modified channels to device")
|
||||
getNode().writeChannel(0)
|
||||
|
||||
if args.reboot:
|
||||
closeNow = True
|
||||
getNode().reboot()
|
||||
|
||||
if args.sendtext:
|
||||
closeNow = True
|
||||
print(f"Sending text message {args.sendtext} to {args.destOrAll}")
|
||||
interface.sendText(args.sendtext, args.destOrAll,
|
||||
wantAck=True)
|
||||
|
||||
if args.sendping:
|
||||
print(f"Sending ping message {args.sendtext} to {args.destOrAll}")
|
||||
payload = str.encode("test string")
|
||||
interface.sendData(payload, args.destOrAll, portNum=portnums_pb2.PortNum.REPLY_APP,
|
||||
wantAck=True, wantResponse=True)
|
||||
|
||||
if args.gpio_wrb or args.gpio_rd or args.gpio_watch:
|
||||
rhc = remote_hardware.RemoteHardwareClient(interface)
|
||||
|
||||
if args.gpio_wrb:
|
||||
bitmask = 0
|
||||
bitval = 0
|
||||
for wrpair in (args.gpio_wrb or []):
|
||||
bitmask |= 1 << int(wrpair[0])
|
||||
bitval |= int(wrpair[1]) << int(wrpair[0])
|
||||
print(
|
||||
f"Writing GPIO mask 0x{bitmask:x} with value 0x{bitval:x} to {args.dest}")
|
||||
rhc.writeGPIOs(args.dest, bitmask, bitval)
|
||||
closeNow = True
|
||||
|
||||
if args.gpio_rd:
|
||||
bitmask = int(args.gpio_rd, 16)
|
||||
print(f"Reading GPIO mask 0x{bitmask:x} from {args.dest}")
|
||||
|
||||
def onResponse(packet):
|
||||
"""A closure to handle the response packet"""
|
||||
hw = packet["decoded"]["remotehw"]
|
||||
print(f'GPIO read response gpio_value={hw["gpioValue"]}')
|
||||
sys.exit(0) # Just force an exit (FIXME - ugly)
|
||||
|
||||
rhc.readGPIOs(args.dest, bitmask, onResponse)
|
||||
|
||||
if args.gpio_watch:
|
||||
bitmask = int(args.gpio_watch, 16)
|
||||
print(f"Watching GPIO mask 0x{bitmask:x} from {args.dest}")
|
||||
rhc.watchGPIOs(args.dest, bitmask)
|
||||
|
||||
# handle settings
|
||||
if args.set:
|
||||
closeNow = True
|
||||
prefs = getNode().radioConfig.preferences
|
||||
|
||||
# Handle the int/float/bool arguments
|
||||
for pref in args.set:
|
||||
setPref(
|
||||
prefs, pref[0], pref[1])
|
||||
|
||||
print("Writing modified preferences to device")
|
||||
getNode().writeConfig()
|
||||
|
||||
|
||||
if args.seturl:
|
||||
closeNow = True
|
||||
getNode().setURL(args.seturl)
|
||||
|
||||
# handle changing channels
|
||||
|
||||
if args.ch_add:
|
||||
closeNow = True
|
||||
n = getNode()
|
||||
ch = n.getChannelByName(args.ch_add)
|
||||
if ch:
|
||||
logging.error(
|
||||
f"This node already has a '{args.ch_add}' channel - no changes.")
|
||||
else:
|
||||
ch = n.getDisabledChannel()
|
||||
if not ch:
|
||||
raise Exception("No free channels were found")
|
||||
chs = channel_pb2.ChannelSettings()
|
||||
chs.psk = genPSK256()
|
||||
chs.name = args.ch_add
|
||||
ch.settings.CopyFrom(chs)
|
||||
ch.role = channel_pb2.Channel.Role.SECONDARY
|
||||
print(f"Writing modified channels to device")
|
||||
n.writeChannel(ch.index)
|
||||
|
||||
if args.ch_del:
|
||||
closeNow = True
|
||||
|
||||
print(f"Deleting channel {channelIndex}")
|
||||
ch = getNode().deleteChannel(channelIndex)
|
||||
|
||||
if args.ch_set or args.ch_longslow or args.ch_shortfast:
|
||||
closeNow = True
|
||||
|
||||
ch = getNode().channels[channelIndex]
|
||||
|
||||
enable = args.ch_enable # should we enable this channel?
|
||||
|
||||
if args.ch_longslow or args.ch_shortfast:
|
||||
if channelIndex != 0:
|
||||
raise Exception(
|
||||
"standard channel settings can only be applied to the PRIMARY channel")
|
||||
|
||||
enable = True # force enable
|
||||
|
||||
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_longslow:
|
||||
setSimpleChannel(
|
||||
channel_pb2.ChannelSettings.ModemConfig.Bw125Cr48Sf4096)
|
||||
|
||||
if args.ch_shortfast:
|
||||
setSimpleChannel(
|
||||
channel_pb2.ChannelSettings.ModemConfig.Bw500Cr45Sf128)
|
||||
|
||||
# Handle the channel settings
|
||||
for pref in (args.ch_set or []):
|
||||
if pref[0] == "psk":
|
||||
ch.settings.psk = fromPSK(pref[1])
|
||||
else:
|
||||
setPref(ch.settings, pref[0], pref[1])
|
||||
enable = True # If we set any pref, assume the user wants to enable the channel
|
||||
|
||||
if enable:
|
||||
ch.role = channel_pb2.Channel.Role.PRIMARY if (
|
||||
channelIndex == 0) else channel_pb2.Channel.Role.SECONDARY
|
||||
else:
|
||||
ch.role = channel_pb2.Channel.Role.DISABLED
|
||||
|
||||
print(f"Writing modified channels to device")
|
||||
getNode().writeChannel(channelIndex)
|
||||
|
||||
if args.info:
|
||||
print("")
|
||||
if not args.dest: # If we aren't trying to talk to our local node, don't show it
|
||||
interface.showInfo()
|
||||
|
||||
print("")
|
||||
getNode().showInfo()
|
||||
closeNow = True # FIXME, for now we leave the link up while talking to remote nodes
|
||||
print("")
|
||||
|
||||
if args.get:
|
||||
closeNow = True
|
||||
prefs = getNode().radioConfig.preferences
|
||||
|
||||
# Handle the int/float/bool arguments
|
||||
for pref in args.get:
|
||||
getPref(
|
||||
prefs, pref[0])
|
||||
|
||||
print("Completed getting preferences")
|
||||
|
||||
if args.nodes:
|
||||
closeNow = True
|
||||
interface.showNodes()
|
||||
|
||||
if args.qr:
|
||||
closeNow = True
|
||||
url = interface.localNode.getURL(includeAll=False)
|
||||
print(f"Primary channel URL {url}")
|
||||
qr = pyqrcode.create(url)
|
||||
print(qr.terminal())
|
||||
|
||||
if have_tunnel and args.tunnel:
|
||||
from . import tunnel
|
||||
# Even if others said we could close, stay open if the user asked for a tunnel
|
||||
closeNow = False
|
||||
tunnel.Tunnel(interface, subnet=args.tunnel_net)
|
||||
|
||||
# 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:
|
||||
interface.close() # after running command then exit
|
||||
|
||||
except Exception as ex:
|
||||
print(f"Aborting due to: {ex}")
|
||||
interface.close() # close the connection now, so that our app exits
|
||||
|
||||
|
||||
def onNode(node):
|
||||
"""Callback invoked when the node DB changes"""
|
||||
print(f"Node changed: {node}")
|
||||
|
||||
|
||||
def subscribe():
|
||||
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
# pub.subscribe(onConnection, "meshtastic.connection")
|
||||
|
||||
# We now call onConnected from main
|
||||
# pub.subscribe(onConnected, "meshtastic.connection.established")
|
||||
|
||||
# pub.subscribe(onNode, "meshtastic.node")
|
||||
|
||||
|
||||
def common():
|
||||
"""Shared code for all of our command line wrappers"""
|
||||
global args
|
||||
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help(sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
if args.ch_index is not None:
|
||||
global channelIndex
|
||||
channelIndex = int(args.ch_index)
|
||||
|
||||
# Some commands require dest to be set, so we now use destOrAll/destOrLocal for more lenient commands
|
||||
if not args.dest:
|
||||
args.destOrAll = "^all"
|
||||
args.destOrLocal = "^local"
|
||||
else:
|
||||
args.destOrAll = args.dest
|
||||
args.destOrLocal = args.dest # FIXME, temp hack for debugging remove
|
||||
|
||||
if not args.seriallog:
|
||||
if args.noproto:
|
||||
args.seriallog = "stdout"
|
||||
else:
|
||||
args.seriallog = "none" # assume no debug output in this case
|
||||
|
||||
if args.deprecated != None:
|
||||
logging.error(
|
||||
'This option has been deprecated, see help below for the correct replacement...')
|
||||
parser.print_help(sys.stderr)
|
||||
sys.exit(1)
|
||||
elif args.test:
|
||||
test.testAll()
|
||||
else:
|
||||
if args.seriallog == "stdout":
|
||||
logfile = sys.stdout
|
||||
elif args.seriallog == "none":
|
||||
args.seriallog = None
|
||||
logging.debug("Not logging serial output")
|
||||
logfile = None
|
||||
else:
|
||||
logging.info(f"Logging serial output to {args.seriallog}")
|
||||
logfile = open(args.seriallog, 'w+',
|
||||
buffering=1) # line buffering
|
||||
|
||||
subscribe()
|
||||
if args.ble:
|
||||
client = BLEInterface(args.ble, debugOut=logfile)
|
||||
elif args.host:
|
||||
client = TCPInterface(
|
||||
args.host, debugOut=logfile, noProto=args.noproto)
|
||||
else:
|
||||
client = SerialInterface(
|
||||
args.port, debugOut=logfile, noProto=args.noproto)
|
||||
|
||||
# We assume client is fully connected now
|
||||
onConnected(client)
|
||||
|
||||
if args.noproto or (have_tunnel and args.tunnel): # loop until someone presses ctrlc
|
||||
while True:
|
||||
time.sleep(1000)
|
||||
|
||||
# don't call exit, background threads might be running still
|
||||
# sys.exit(0)
|
||||
|
||||
|
||||
def initParser():
|
||||
global parser, args
|
||||
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
help="The port the Meshtastic device is connected to, i.e. /dev/ttyUSB0. If unspecified, we'll try to find it.",
|
||||
default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--host",
|
||||
help="The hostname/ipaddr of the device to connect to (over TCP)",
|
||||
default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--seriallog",
|
||||
help="Log device serial output to either 'stdout', 'none' or a filename to append to.")
|
||||
|
||||
parser.add_argument("--info", help="Read and display the radio config information",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument("--nodes", help="Print Node List in a pretty formatted table",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument("--qr", help="Display the QR code that corresponds to the current channel",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
"--get", help="Get a preferences field", nargs=1, action='append')
|
||||
|
||||
parser.add_argument(
|
||||
"--set", help="Set a preferences field", nargs=2, action='append')
|
||||
|
||||
parser.add_argument(
|
||||
"--seturl", help="Set a channel URL", action="store")
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-index", help="Set the specified channel index", action="store")
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-add", help="Add a secondary channel, you must specify a channel name", default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-del", help="Delete the ch-index channel", action='store_true')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-enable", help="Enable the specified channel", action="store_true", dest="ch_enable")
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-disable", help="Disable the specified channel", action="store_false", dest="ch_enable")
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-set", help="Set a channel parameter", nargs=2, action='append')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-longslow", help="Change to the standard long-range (but slow) channel", action='store_true')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-shortfast", help="Change to the standard fast (but short range) channel", action='store_true')
|
||||
|
||||
parser.add_argument(
|
||||
"--set-owner", help="Set device owner name", action="store")
|
||||
|
||||
parser.add_argument(
|
||||
"--set-ham", help="Set licensed HAM ID and turn off encryption", action="store")
|
||||
|
||||
parser.add_argument(
|
||||
"--dest", help="The destination node id for any sent commands, if not set '^all' or '^local' is assumed as appropriate", default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--sendtext", help="Send a text message")
|
||||
|
||||
parser.add_argument(
|
||||
"--sendping", help="Send a ping message (which requests a reply)", action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
"--reboot", help="Tell the destination node to reboot", action="store_true")
|
||||
|
||||
# parser.add_argument(
|
||||
# "--repeat", help="Normally the send commands send only one message, use this option to request repeated sends")
|
||||
|
||||
parser.add_argument(
|
||||
"--reply", help="Reply to received messages",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
"--gpio-wrb", nargs=2, help="Set a particlar GPIO # to 1 or 0", action='append')
|
||||
|
||||
parser.add_argument(
|
||||
"--gpio-rd", help="Read from a GPIO mask")
|
||||
|
||||
parser.add_argument(
|
||||
"--gpio-watch", help="Start watching a GPIO mask for changes")
|
||||
|
||||
parser.add_argument(
|
||||
"--no-time", help="Suppress sending the current time to the mesh", action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
"--setalt", help="Set device altitude (allows use without GPS)")
|
||||
|
||||
parser.add_argument(
|
||||
"--setlat", help="Set device latitude (allows use without GPS)")
|
||||
|
||||
parser.add_argument(
|
||||
"--setlon", help="Set device longitude (allows use without GPS)")
|
||||
|
||||
parser.add_argument("--debug", help="Show API library debug log messages",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument("--test", help="Run stress test against all connected Meshtastic devices",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument("--ble", help="BLE mac address to connect to (BLE is not yet supported for this tool)",
|
||||
default=None)
|
||||
|
||||
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')
|
||||
|
||||
if have_tunnel:
|
||||
parser.add_argument('--tunnel',
|
||||
action='store_true', help="Create a TUN tunnel device for forwarding IP packets over the mesh")
|
||||
parser.add_argument(
|
||||
"--subnet", dest='tunnel_net', help="Sets the local-end subnet address for the TUN IP bridge", default=None)
|
||||
|
||||
parser.set_defaults(deprecated=None)
|
||||
|
||||
parser.add_argument('--version', action='version',
|
||||
version=f"{pkg_resources.require('meshtastic')[0].version}")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
"""Perform command line meshtastic operations"""
|
||||
initParser()
|
||||
common()
|
||||
|
||||
|
||||
def tunnelMain():
|
||||
"""Run a meshtastic IP tunnel"""
|
||||
global args
|
||||
initParser()
|
||||
args.tunnel = True
|
||||
common()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,184 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: admin.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
import mesh_pb2 as mesh__pb2
|
||||
import radioconfig_pb2 as radioconfig__pb2
|
||||
import channel_pb2 as channel__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='admin.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\013AdminProtosH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\x1a\rchannel.proto\"\xfb\x02\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18! \x01(\x08H\x00\x12\x18\n\x0e\x65xit_simulator\x18\" \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18# \x01(\x05H\x00\x42\t\n\x07variantBG\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
,
|
||||
dependencies=[mesh__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,channel__pb2.DESCRIPTOR,])
|
||||
|
||||
|
||||
|
||||
|
||||
_ADMINMESSAGE = _descriptor.Descriptor(
|
||||
name='AdminMessage',
|
||||
full_name='AdminMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='set_radio', full_name='AdminMessage.set_radio', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='set_owner', full_name='AdminMessage.set_owner', index=1,
|
||||
number=2, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='set_channel', full_name='AdminMessage.set_channel', index=2,
|
||||
number=3, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='get_radio_request', full_name='AdminMessage.get_radio_request', index=3,
|
||||
number=4, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='get_radio_response', full_name='AdminMessage.get_radio_response', index=4,
|
||||
number=5, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='get_channel_request', full_name='AdminMessage.get_channel_request', index=5,
|
||||
number=6, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='get_channel_response', full_name='AdminMessage.get_channel_response', index=6,
|
||||
number=7, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='confirm_set_channel', full_name='AdminMessage.confirm_set_channel', index=7,
|
||||
number=32, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='confirm_set_radio', full_name='AdminMessage.confirm_set_radio', index=8,
|
||||
number=33, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='exit_simulator', full_name='AdminMessage.exit_simulator', index=9,
|
||||
number=34, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='reboot_seconds', full_name='AdminMessage.reboot_seconds', index=10,
|
||||
number=35, type=5, cpp_type=1, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
_descriptor.OneofDescriptor(
|
||||
name='variant', full_name='AdminMessage.variant',
|
||||
index=0, containing_type=None, fields=[]),
|
||||
],
|
||||
serialized_start=62,
|
||||
serialized_end=441,
|
||||
)
|
||||
|
||||
_ADMINMESSAGE.fields_by_name['set_radio'].message_type = radioconfig__pb2._RADIOCONFIG
|
||||
_ADMINMESSAGE.fields_by_name['set_owner'].message_type = mesh__pb2._USER
|
||||
_ADMINMESSAGE.fields_by_name['set_channel'].message_type = channel__pb2._CHANNEL
|
||||
_ADMINMESSAGE.fields_by_name['get_radio_response'].message_type = radioconfig__pb2._RADIOCONFIG
|
||||
_ADMINMESSAGE.fields_by_name['get_channel_response'].message_type = channel__pb2._CHANNEL
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['set_radio'])
|
||||
_ADMINMESSAGE.fields_by_name['set_radio'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['set_owner'])
|
||||
_ADMINMESSAGE.fields_by_name['set_owner'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['set_channel'])
|
||||
_ADMINMESSAGE.fields_by_name['set_channel'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['get_radio_request'])
|
||||
_ADMINMESSAGE.fields_by_name['get_radio_request'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['get_radio_response'])
|
||||
_ADMINMESSAGE.fields_by_name['get_radio_response'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['get_channel_request'])
|
||||
_ADMINMESSAGE.fields_by_name['get_channel_request'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['get_channel_response'])
|
||||
_ADMINMESSAGE.fields_by_name['get_channel_response'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['confirm_set_channel'])
|
||||
_ADMINMESSAGE.fields_by_name['confirm_set_channel'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['confirm_set_radio'])
|
||||
_ADMINMESSAGE.fields_by_name['confirm_set_radio'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['exit_simulator'])
|
||||
_ADMINMESSAGE.fields_by_name['exit_simulator'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
|
||||
_ADMINMESSAGE.fields_by_name['reboot_seconds'])
|
||||
_ADMINMESSAGE.fields_by_name['reboot_seconds'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
|
||||
DESCRIPTOR.message_types_by_name['AdminMessage'] = _ADMINMESSAGE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
AdminMessage = _reflection.GeneratedProtocolMessageType('AdminMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ADMINMESSAGE,
|
||||
'__module__' : 'admin_pb2'
|
||||
# @@protoc_insertion_point(class_scope:AdminMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(AdminMessage)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,72 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: apponly.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
import channel_pb2 as channel__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='apponly.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\rAppOnlyProtosH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\rapponly.proto\x1a\rchannel.proto\"0\n\nChannelSet\x12\"\n\x08settings\x18\x01 \x03(\x0b\x32\x10.ChannelSettingsBI\n\x13\x63om.geeksville.meshB\rAppOnlyProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
,
|
||||
dependencies=[channel__pb2.DESCRIPTOR,])
|
||||
|
||||
|
||||
|
||||
|
||||
_CHANNELSET = _descriptor.Descriptor(
|
||||
name='ChannelSet',
|
||||
full_name='ChannelSet',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='settings', full_name='ChannelSet.settings', index=0,
|
||||
number=1, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=32,
|
||||
serialized_end=80,
|
||||
)
|
||||
|
||||
_CHANNELSET.fields_by_name['settings'].message_type = channel__pb2._CHANNELSETTINGS
|
||||
DESCRIPTOR.message_types_by_name['ChannelSet'] = _CHANNELSET
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
ChannelSet = _reflection.GeneratedProtocolMessageType('ChannelSet', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CHANNELSET,
|
||||
'__module__' : 'apponly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ChannelSet)
|
||||
})
|
||||
_sym_db.RegisterMessage(ChannelSet)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,255 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: channel.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='channel.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\rChannelProtosH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\rchannel.proto\"\xe6\x02\n\x0f\x43hannelSettings\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x32\n\x0cmodem_config\x18\x03 \x01(\x0e\x32\x1c.ChannelSettings.ModemConfig\x12\x11\n\tbandwidth\x18\x06 \x01(\r\x12\x15\n\rspread_factor\x18\x07 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x08 \x01(\r\x12\x13\n\x0b\x63hannel_num\x18\t \x01(\r\x12\x0b\n\x03psk\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\n\n\x02id\x18\n \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x10 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x11 \x01(\x08\"`\n\x0bModemConfig\x12\x12\n\x0e\x42w125Cr45Sf128\x10\x00\x12\x12\n\x0e\x42w500Cr45Sf128\x10\x01\x12\x14\n\x10\x42w31_25Cr48Sf512\x10\x02\x12\x13\n\x0f\x42w125Cr48Sf4096\x10\x03\"\x8b\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\"\n\x08settings\x18\x02 \x01(\x0b\x32\x10.ChannelSettings\x12\x1b\n\x04role\x18\x03 \x01(\x0e\x32\r.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42I\n\x13\x63om.geeksville.meshB\rChannelProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
)
|
||||
|
||||
|
||||
|
||||
_CHANNELSETTINGS_MODEMCONFIG = _descriptor.EnumDescriptor(
|
||||
name='ModemConfig',
|
||||
full_name='ChannelSettings.ModemConfig',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='Bw125Cr45Sf128', index=0, number=0,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='Bw500Cr45Sf128', index=1, number=1,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='Bw31_25Cr48Sf512', index=2, number=2,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='Bw125Cr48Sf4096', index=3, number=3,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=280,
|
||||
serialized_end=376,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_CHANNELSETTINGS_MODEMCONFIG)
|
||||
|
||||
_CHANNEL_ROLE = _descriptor.EnumDescriptor(
|
||||
name='Role',
|
||||
full_name='Channel.Role',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='DISABLED', index=0, number=0,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='PRIMARY', index=1, number=1,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='SECONDARY', index=2, number=2,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=470,
|
||||
serialized_end=518,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_CHANNEL_ROLE)
|
||||
|
||||
|
||||
_CHANNELSETTINGS = _descriptor.Descriptor(
|
||||
name='ChannelSettings',
|
||||
full_name='ChannelSettings',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='tx_power', full_name='ChannelSettings.tx_power', index=0,
|
||||
number=1, type=5, cpp_type=1, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='modem_config', full_name='ChannelSettings.modem_config', index=1,
|
||||
number=3, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='bandwidth', full_name='ChannelSettings.bandwidth', index=2,
|
||||
number=6, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='spread_factor', full_name='ChannelSettings.spread_factor', index=3,
|
||||
number=7, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='coding_rate', full_name='ChannelSettings.coding_rate', index=4,
|
||||
number=8, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='channel_num', full_name='ChannelSettings.channel_num', index=5,
|
||||
number=9, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='psk', full_name='ChannelSettings.psk', index=6,
|
||||
number=4, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='name', full_name='ChannelSettings.name', index=7,
|
||||
number=5, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='id', full_name='ChannelSettings.id', index=8,
|
||||
number=10, type=7, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='uplink_enabled', full_name='ChannelSettings.uplink_enabled', index=9,
|
||||
number=16, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='downlink_enabled', full_name='ChannelSettings.downlink_enabled', index=10,
|
||||
number=17, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
_CHANNELSETTINGS_MODEMCONFIG,
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=18,
|
||||
serialized_end=376,
|
||||
)
|
||||
|
||||
|
||||
_CHANNEL = _descriptor.Descriptor(
|
||||
name='Channel',
|
||||
full_name='Channel',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='index', full_name='Channel.index', index=0,
|
||||
number=1, type=5, cpp_type=1, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='settings', full_name='Channel.settings', index=1,
|
||||
number=2, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='role', full_name='Channel.role', index=2,
|
||||
number=3, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
_CHANNEL_ROLE,
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=379,
|
||||
serialized_end=518,
|
||||
)
|
||||
|
||||
_CHANNELSETTINGS.fields_by_name['modem_config'].enum_type = _CHANNELSETTINGS_MODEMCONFIG
|
||||
_CHANNELSETTINGS_MODEMCONFIG.containing_type = _CHANNELSETTINGS
|
||||
_CHANNEL.fields_by_name['settings'].message_type = _CHANNELSETTINGS
|
||||
_CHANNEL.fields_by_name['role'].enum_type = _CHANNEL_ROLE
|
||||
_CHANNEL_ROLE.containing_type = _CHANNEL
|
||||
DESCRIPTOR.message_types_by_name['ChannelSettings'] = _CHANNELSETTINGS
|
||||
DESCRIPTOR.message_types_by_name['Channel'] = _CHANNEL
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
ChannelSettings = _reflection.GeneratedProtocolMessageType('ChannelSettings', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CHANNELSETTINGS,
|
||||
'__module__' : 'channel_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ChannelSettings)
|
||||
})
|
||||
_sym_db.RegisterMessage(ChannelSettings)
|
||||
|
||||
Channel = _reflection.GeneratedProtocolMessageType('Channel', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CHANNEL,
|
||||
'__module__' : 'channel_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Channel)
|
||||
})
|
||||
_sym_db.RegisterMessage(Channel)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,255 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: deviceonly.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
import mesh_pb2 as mesh__pb2
|
||||
import channel_pb2 as channel__pb2
|
||||
import radioconfig_pb2 as radioconfig__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='deviceonly.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\nDeviceOnlyH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\x10\x64\x65viceonly.proto\x1a\nmesh.proto\x1a\rchannel.proto\x1a\x11radioconfig.proto\"\x80\x01\n\x11LegacyRadioConfig\x12\x39\n\x0bpreferences\x18\x01 \x01(\x0b\x32$.LegacyRadioConfig.LegacyPreferences\x1a\x30\n\x11LegacyPreferences\x12\x1b\n\x06region\x18\x0f \x01(\x0e\x32\x0b.RegionCode\"\x8f\x02\n\x0b\x44\x65viceState\x12\'\n\x0blegacyRadio\x18\x01 \x01(\x0b\x32\x12.LegacyRadioConfig\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08J\x04\x08\x0c\x10\r\")\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.ChannelBF\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
,
|
||||
dependencies=[mesh__pb2.DESCRIPTOR,channel__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,])
|
||||
|
||||
|
||||
|
||||
|
||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES = _descriptor.Descriptor(
|
||||
name='LegacyPreferences',
|
||||
full_name='LegacyRadioConfig.LegacyPreferences',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='region', full_name='LegacyRadioConfig.LegacyPreferences.region', index=0,
|
||||
number=15, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=147,
|
||||
serialized_end=195,
|
||||
)
|
||||
|
||||
_LEGACYRADIOCONFIG = _descriptor.Descriptor(
|
||||
name='LegacyRadioConfig',
|
||||
full_name='LegacyRadioConfig',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='preferences', full_name='LegacyRadioConfig.preferences', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[_LEGACYRADIOCONFIG_LEGACYPREFERENCES, ],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=67,
|
||||
serialized_end=195,
|
||||
)
|
||||
|
||||
|
||||
_DEVICESTATE = _descriptor.Descriptor(
|
||||
name='DeviceState',
|
||||
full_name='DeviceState',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='legacyRadio', full_name='DeviceState.legacyRadio', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='my_node', full_name='DeviceState.my_node', index=1,
|
||||
number=2, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='owner', full_name='DeviceState.owner', index=2,
|
||||
number=3, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='node_db', full_name='DeviceState.node_db', index=3,
|
||||
number=4, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='receive_queue', full_name='DeviceState.receive_queue', index=4,
|
||||
number=5, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='version', full_name='DeviceState.version', index=5,
|
||||
number=8, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='rx_text_message', full_name='DeviceState.rx_text_message', index=6,
|
||||
number=7, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='no_save', full_name='DeviceState.no_save', index=7,
|
||||
number=9, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='did_gps_reset', full_name='DeviceState.did_gps_reset', index=8,
|
||||
number=11, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=198,
|
||||
serialized_end=469,
|
||||
)
|
||||
|
||||
|
||||
_CHANNELFILE = _descriptor.Descriptor(
|
||||
name='ChannelFile',
|
||||
full_name='ChannelFile',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='channels', full_name='ChannelFile.channels', index=0,
|
||||
number=1, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=471,
|
||||
serialized_end=512,
|
||||
)
|
||||
|
||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES.fields_by_name['region'].enum_type = radioconfig__pb2._REGIONCODE
|
||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES.containing_type = _LEGACYRADIOCONFIG
|
||||
_LEGACYRADIOCONFIG.fields_by_name['preferences'].message_type = _LEGACYRADIOCONFIG_LEGACYPREFERENCES
|
||||
_DEVICESTATE.fields_by_name['legacyRadio'].message_type = _LEGACYRADIOCONFIG
|
||||
_DEVICESTATE.fields_by_name['my_node'].message_type = mesh__pb2._MYNODEINFO
|
||||
_DEVICESTATE.fields_by_name['owner'].message_type = mesh__pb2._USER
|
||||
_DEVICESTATE.fields_by_name['node_db'].message_type = mesh__pb2._NODEINFO
|
||||
_DEVICESTATE.fields_by_name['receive_queue'].message_type = mesh__pb2._MESHPACKET
|
||||
_DEVICESTATE.fields_by_name['rx_text_message'].message_type = mesh__pb2._MESHPACKET
|
||||
_CHANNELFILE.fields_by_name['channels'].message_type = channel__pb2._CHANNEL
|
||||
DESCRIPTOR.message_types_by_name['LegacyRadioConfig'] = _LEGACYRADIOCONFIG
|
||||
DESCRIPTOR.message_types_by_name['DeviceState'] = _DEVICESTATE
|
||||
DESCRIPTOR.message_types_by_name['ChannelFile'] = _CHANNELFILE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
LegacyRadioConfig = _reflection.GeneratedProtocolMessageType('LegacyRadioConfig', (_message.Message,), {
|
||||
|
||||
'LegacyPreferences' : _reflection.GeneratedProtocolMessageType('LegacyPreferences', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LEGACYRADIOCONFIG_LEGACYPREFERENCES,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LegacyRadioConfig.LegacyPreferences)
|
||||
})
|
||||
,
|
||||
'DESCRIPTOR' : _LEGACYRADIOCONFIG,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LegacyRadioConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(LegacyRadioConfig)
|
||||
_sym_db.RegisterMessage(LegacyRadioConfig.LegacyPreferences)
|
||||
|
||||
DeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message.Message,), {
|
||||
'DESCRIPTOR' : _DEVICESTATE,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:DeviceState)
|
||||
})
|
||||
_sym_db.RegisterMessage(DeviceState)
|
||||
|
||||
ChannelFile = _reflection.GeneratedProtocolMessageType('ChannelFile', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CHANNELFILE,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ChannelFile)
|
||||
})
|
||||
_sym_db.RegisterMessage(ChannelFile)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,83 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: environmental_measurement.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='environmental_measurement.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\x1f\x65nvironmental_measurement.proto\"g\n\x18\x45nvironmentalMeasurement\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\x42#Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
_ENVIRONMENTALMEASUREMENT = _descriptor.Descriptor(
|
||||
name='EnvironmentalMeasurement',
|
||||
full_name='EnvironmentalMeasurement',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='temperature', full_name='EnvironmentalMeasurement.temperature', index=0,
|
||||
number=1, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=float(0),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='relative_humidity', full_name='EnvironmentalMeasurement.relative_humidity', index=1,
|
||||
number=2, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=float(0),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='barometric_pressure', full_name='EnvironmentalMeasurement.barometric_pressure', index=2,
|
||||
number=3, type=2, cpp_type=6, label=1,
|
||||
has_default_value=False, default_value=float(0),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=35,
|
||||
serialized_end=138,
|
||||
)
|
||||
|
||||
DESCRIPTOR.message_types_by_name['EnvironmentalMeasurement'] = _ENVIRONMENTALMEASUREMENT
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
EnvironmentalMeasurement = _reflection.GeneratedProtocolMessageType('EnvironmentalMeasurement', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ENVIRONMENTALMEASUREMENT,
|
||||
'__module__' : 'environmental_measurement_pb2'
|
||||
# @@protoc_insertion_point(class_scope:EnvironmentalMeasurement)
|
||||
})
|
||||
_sym_db.RegisterMessage(EnvironmentalMeasurement)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
File diff suppressed because one or more lines are too long
@@ -1,86 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: mqtt.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
import mesh_pb2 as mesh__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='mqtt.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\nMQTTProtosH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\nmqtt.proto\x1a\nmesh.proto\"V\n\x0fServiceEnvelope\x12\x1b\n\x06packet\x18\x01 \x01(\x0b\x32\x0b.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\tBF\n\x13\x63om.geeksville.meshB\nMQTTProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
,
|
||||
dependencies=[mesh__pb2.DESCRIPTOR,])
|
||||
|
||||
|
||||
|
||||
|
||||
_SERVICEENVELOPE = _descriptor.Descriptor(
|
||||
name='ServiceEnvelope',
|
||||
full_name='ServiceEnvelope',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='packet', full_name='ServiceEnvelope.packet', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='channel_id', full_name='ServiceEnvelope.channel_id', index=1,
|
||||
number=2, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='gateway_id', full_name='ServiceEnvelope.gateway_id', index=2,
|
||||
number=3, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=26,
|
||||
serialized_end=112,
|
||||
)
|
||||
|
||||
_SERVICEENVELOPE.fields_by_name['packet'].message_type = mesh__pb2._MESHPACKET
|
||||
DESCRIPTOR.message_types_by_name['ServiceEnvelope'] = _SERVICEENVELOPE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
ServiceEnvelope = _reflection.GeneratedProtocolMessageType('ServiceEnvelope', (_message.Message,), {
|
||||
'DESCRIPTOR' : _SERVICEENVELOPE,
|
||||
'__module__' : 'mqtt_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ServiceEnvelope)
|
||||
})
|
||||
_sym_db.RegisterMessage(ServiceEnvelope)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,404 +0,0 @@
|
||||
"""
|
||||
# an API for Meshtastic devices
|
||||
|
||||
Primary class: SerialInterface
|
||||
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
||||
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
|
||||
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.
|
||||
- nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId
|
||||
- myInfo - Contains read-only information about the local radio device (software version, hardware version, etc)
|
||||
|
||||
# Published PubSub topics
|
||||
|
||||
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
|
||||
topics:
|
||||
|
||||
- meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB
|
||||
- meshtastic.connection.lost - published once we've lost our link to the radio
|
||||
- meshtastic.receive.text(packet) - delivers a received packet as a dictionary, if you only care about a particular
|
||||
type of packet, you should subscribe to the full topic name. If you want to see all packets, simply subscribe to "meshtastic.receive".
|
||||
- meshtastic.receive.position(packet)
|
||||
- meshtastic.receive.user(packet)
|
||||
- meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)
|
||||
- meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc...)
|
||||
|
||||
We receive position, user, or data packets from the mesh. You probably only care about meshtastic.receive.data. The first argument for
|
||||
that publish will be the packet. Text or binary data packets (from sendData or sendText) will both arrive this way. If you print packet
|
||||
you'll see the fields in the dictionary. decoded.data.payload will contain the raw bytes that were sent. If the packet was sent with
|
||||
sendText, decoded.data.text will **also** be populated with the decoded string. For ASCII these two strings will be the same, but for
|
||||
unicode scripts they can be different.
|
||||
|
||||
# Example Usage
|
||||
```
|
||||
import meshtastic
|
||||
from pubsub import pub
|
||||
|
||||
def onReceive(packet, interface): # called when a packet arrives
|
||||
print(f"Received: {packet}")
|
||||
|
||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect to the radio
|
||||
# defaults to broadcast, specify a destination ID if you wish
|
||||
interface.sendText("hello mesh")
|
||||
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||
# By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0
|
||||
interface = meshtastic.SerialInterface()
|
||||
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
import pygatt
|
||||
import google.protobuf.json_format
|
||||
import serial
|
||||
import threading
|
||||
import logging
|
||||
import sys
|
||||
import random
|
||||
import traceback
|
||||
import time
|
||||
import base64
|
||||
import platform
|
||||
import socket
|
||||
from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util
|
||||
from .util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
||||
from pubsub import pub
|
||||
from dotmap import DotMap
|
||||
from typing import *
|
||||
from google.protobuf.json_format import MessageToJson
|
||||
|
||||
|
||||
|
||||
def pskToString(psk: bytes):
|
||||
"""Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string"""
|
||||
if len(psk) == 0:
|
||||
return "unencrypted"
|
||||
elif len(psk) == 1:
|
||||
b = psk[0]
|
||||
if b == 0:
|
||||
return "unencrypted"
|
||||
elif b == 1:
|
||||
return "default"
|
||||
else:
|
||||
return f"simple{b - 1}"
|
||||
else:
|
||||
return "secret"
|
||||
|
||||
|
||||
class Node:
|
||||
"""A model of a (local or remote) node in the mesh
|
||||
|
||||
Includes methods for radioConfig and channels
|
||||
"""
|
||||
|
||||
def __init__(self, iface, nodeNum):
|
||||
"""Constructor"""
|
||||
self.iface = iface
|
||||
self.nodeNum = nodeNum
|
||||
self.radioConfig = None
|
||||
self.channels = None
|
||||
self._timeout = Timeout(maxSecs=60)
|
||||
|
||||
def showChannels(self):
|
||||
"""Show human readable description of our channels"""
|
||||
print("Channels:")
|
||||
for c in self.channels:
|
||||
if c.role != channel_pb2.Channel.Role.DISABLED:
|
||||
cStr = stripnl(MessageToJson(c.settings))
|
||||
print(
|
||||
f" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}")
|
||||
publicURL = self.getURL(includeAll=False)
|
||||
adminURL = self.getURL(includeAll=True)
|
||||
print(f"\nPrimary channel URL: {publicURL}")
|
||||
if adminURL != publicURL:
|
||||
print(f"Complete URL (includes all channels): {adminURL}")
|
||||
|
||||
def showInfo(self):
|
||||
"""Show human readable description of our node"""
|
||||
print(
|
||||
f"Preferences: {stripnl(MessageToJson(self.radioConfig.preferences))}\n")
|
||||
self.showChannels()
|
||||
|
||||
def requestConfig(self):
|
||||
"""
|
||||
Send regular MeshPackets to ask for settings and channels
|
||||
"""
|
||||
self.radioConfig = None
|
||||
self.channels = None
|
||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||
|
||||
self._requestSettings()
|
||||
|
||||
def waitForConfig(self):
|
||||
"""Block until radio config is received. Returns True if config has been received."""
|
||||
return self._timeout.waitForSet(self, attrs=('radioConfig', 'channels'))
|
||||
|
||||
def writeConfig(self):
|
||||
"""Write the current (edited) radioConfig to the device"""
|
||||
if self.radioConfig == None:
|
||||
raise Exception("No RadioConfig has been read")
|
||||
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_radio.CopyFrom(self.radioConfig)
|
||||
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote config")
|
||||
|
||||
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}")
|
||||
|
||||
def deleteChannel(self, channelIndex):
|
||||
"""Delete the specifed channelIndex and shift other channels up"""
|
||||
ch = self.channels[channelIndex]
|
||||
if ch.role != channel_pb2.Channel.Role.SECONDARY:
|
||||
raise Exception("Only SECONDARY channels can be deleted")
|
||||
|
||||
# we are careful here because if we move the "admin" channel the channelIndex we need to use
|
||||
# for sending admin channels will also change
|
||||
adminIndex = self.iface.localNode._getAdminChannelIndex()
|
||||
|
||||
self.channels.pop(channelIndex)
|
||||
self._fixupChannels() # expand back to 8 channels
|
||||
|
||||
index = channelIndex
|
||||
while index < self.iface.myInfo.max_channels:
|
||||
self.writeChannel(index, adminIndex=adminIndex)
|
||||
index += 1
|
||||
|
||||
# if we are updating the local node, we might end up *moving* the admin channel index as we are writing
|
||||
if (self.iface.localNode == self) and index >= adminIndex:
|
||||
# We've now passed the old location for admin index (and writen it), so we can start finding it by name again
|
||||
adminIndex = 0
|
||||
|
||||
def getChannelByName(self, name):
|
||||
"""Try to find the named channel or return None"""
|
||||
for c in (self.channels or []):
|
||||
if c.settings and c.settings.name == name:
|
||||
return c
|
||||
return None
|
||||
|
||||
def getDisabledChannel(self):
|
||||
"""Return the first channel that is disabled (i.e. available for some new use)"""
|
||||
for c in self.channels:
|
||||
if c.role == channel_pb2.Channel.Role.DISABLED:
|
||||
return c
|
||||
return None
|
||||
|
||||
def _getAdminChannelIndex(self):
|
||||
"""Return the channel number of the admin channel, or 0 if no reserved channel"""
|
||||
c = self.getChannelByName("admin")
|
||||
if c:
|
||||
return c.index
|
||||
else:
|
||||
return 0
|
||||
|
||||
def setOwner(self, long_name, short_name=None, is_licensed=False):
|
||||
"""Set device owner name"""
|
||||
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()
|
||||
|
||||
if long_name is not None:
|
||||
p.set_owner.long_name = long_name
|
||||
if short_name is not None:
|
||||
short_name = short_name.strip()
|
||||
if len(short_name) > nChars:
|
||||
short_name = short_name[:nChars]
|
||||
p.set_owner.short_name = short_name
|
||||
p.set_owner.is_licensed = is_licensed
|
||||
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def getURL(self, includeAll: bool = True):
|
||||
"""The sharable URL that describes the current channel
|
||||
"""
|
||||
# Only keep the primary/secondary channels, assume primary is first
|
||||
channelSet = apponly_pb2.ChannelSet()
|
||||
for c in self.channels:
|
||||
if c.role == channel_pb2.Channel.Role.PRIMARY or (includeAll and c.role == channel_pb2.Channel.Role.SECONDARY):
|
||||
channelSet.settings.append(c.settings)
|
||||
bytes = channelSet.SerializeToString()
|
||||
s = base64.urlsafe_b64encode(bytes).decode('ascii')
|
||||
return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
|
||||
|
||||
def setURL(self, url):
|
||||
"""Set mesh network URL"""
|
||||
if self.radioConfig == None:
|
||||
raise Exception("No RadioConfig 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
|
||||
splitURL = url.split("/#")
|
||||
b64 = splitURL[-1]
|
||||
|
||||
# We normally strip padding to make for a shorter URL, but the python parser doesn't like
|
||||
# that. So add back any missing padding
|
||||
# per https://stackoverflow.com/a/9807138
|
||||
missing_padding = len(b64) % 4
|
||||
if missing_padding:
|
||||
b64 += '=' * (4 - missing_padding)
|
||||
|
||||
decodedURL = base64.urlsafe_b64decode(b64)
|
||||
channelSet = apponly_pb2.ChannelSet()
|
||||
channelSet.ParseFromString(decodedURL)
|
||||
|
||||
i = 0
|
||||
for chs in channelSet.settings:
|
||||
ch = channel_pb2.Channel()
|
||||
ch.role = channel_pb2.Channel.Role.PRIMARY if i == 0 else channel_pb2.Channel.Role.SECONDARY
|
||||
ch.index = i
|
||||
ch.settings.CopyFrom(chs)
|
||||
self.channels[ch.index] = ch
|
||||
self.writeChannel(ch.index)
|
||||
i = i + 1
|
||||
|
||||
def _requestSettings(self):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings
|
||||
"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_radio_request = True
|
||||
|
||||
def onResponse(p):
|
||||
"""A closure to handle the response packet"""
|
||||
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
|
||||
logging.debug("Received radio config, now fetching channels...")
|
||||
self._timeout.reset() # We made foreward progress
|
||||
self._requestChannel(0) # now start fetching channels
|
||||
|
||||
# Show progress message for super slow operations
|
||||
if self != self.iface.localNode:
|
||||
logging.info(
|
||||
"Requesting preferences from remote node (this could take a while)")
|
||||
|
||||
return self._sendAdmin(p,
|
||||
wantResponse=True,
|
||||
onResponse=onResponse)
|
||||
|
||||
def exitSimulator(self):
|
||||
"""
|
||||
Tell a simulator node to exit (this message is ignored for other nodes)
|
||||
"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.exit_simulator = True
|
||||
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def reboot(self, secs: int = 10):
|
||||
"""
|
||||
Tell the node to reboot
|
||||
"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.reboot_seconds = secs
|
||||
logging.info(f"Telling node to reboot in {secs} seconds")
|
||||
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def _fixupChannels(self):
|
||||
"""Fixup indexes and add disabled channels as needed"""
|
||||
|
||||
# Add extra disabled channels as needed
|
||||
for index, ch in enumerate(self.channels):
|
||||
ch.index = index # fixup indexes
|
||||
|
||||
self._fillChannels()
|
||||
|
||||
def _fillChannels(self):
|
||||
"""Mark unused channels as disabled"""
|
||||
|
||||
# Add extra disabled channels as needed
|
||||
index = len(self.channels)
|
||||
while index < self.iface.myInfo.max_channels:
|
||||
ch = channel_pb2.Channel()
|
||||
ch.role = channel_pb2.Channel.Role.DISABLED
|
||||
ch.index = index
|
||||
self.channels.append(ch)
|
||||
index += 1
|
||||
|
||||
def _requestChannel(self, channelNum: int):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings
|
||||
"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_channel_request = channelNum + 1
|
||||
|
||||
# Show progress message for super slow operations
|
||||
if self != self.iface.localNode:
|
||||
logging.info(
|
||||
f"Requesting channel {channelNum} info from remote node (this could take a while)")
|
||||
else:
|
||||
logging.debug(f"Requesting channel {channelNum}")
|
||||
|
||||
def onResponse(p):
|
||||
"""A closure to handle the response packet"""
|
||||
c = p["decoded"]["admin"]["raw"].get_channel_response
|
||||
self.partialChannels.append(c)
|
||||
self._timeout.reset() # We made foreward progress
|
||||
logging.debug(f"Received channel {stripnl(c)}")
|
||||
index = c.index
|
||||
|
||||
# for stress testing, we can always download all channels
|
||||
fastChannelDownload = True
|
||||
|
||||
# Once we see a response that has NO settings, assume we are at the end of channels and stop fetching
|
||||
quitEarly = (
|
||||
c.role == channel_pb2.Channel.Role.DISABLED) and fastChannelDownload
|
||||
|
||||
if quitEarly or index >= self.iface.myInfo.max_channels - 1:
|
||||
logging.debug("Finished downloading channels")
|
||||
|
||||
self.channels = self.partialChannels
|
||||
self._fixupChannels()
|
||||
|
||||
# FIXME, the following should only be called after we have settings and channels
|
||||
self.iface._connected() # Tell everone else we are ready to go
|
||||
else:
|
||||
self._requestChannel(index + 1)
|
||||
|
||||
return self._sendAdmin(p,
|
||||
wantResponse=True,
|
||||
onResponse=onResponse)
|
||||
|
||||
def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=False,
|
||||
onResponse=None,
|
||||
adminIndex=0):
|
||||
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
|
||||
|
||||
if adminIndex == 0: # unless a special channel index was used, we want to use the admin index
|
||||
adminIndex = self.iface.localNode._getAdminChannelIndex()
|
||||
|
||||
return self.iface.sendData(p, self.nodeNum,
|
||||
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
||||
wantAck=True,
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=adminIndex)
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: portnums.proto
|
||||
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='portnums.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\010PortnumsH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\x0eportnums.proto*\xcb\x02\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\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!\n\x1d\x45NVIRONMENTAL_MEASUREMENT_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\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\x44\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
)
|
||||
|
||||
_PORTNUM = _descriptor.EnumDescriptor(
|
||||
name='PortNum',
|
||||
full_name='PortNum',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='UNKNOWN_APP', index=0, number=0,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='TEXT_MESSAGE_APP', index=1, number=1,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='REMOTE_HARDWARE_APP', index=2, number=2,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='POSITION_APP', index=3, number=3,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='NODEINFO_APP', index=4, number=4,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ROUTING_APP', index=5, number=5,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ADMIN_APP', index=6, number=6,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='REPLY_APP', index=7, number=32,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='IP_TUNNEL_APP', index=8, number=33,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='SERIAL_APP', index=9, number=64,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='STORE_FORWARD_APP', index=10, number=65,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='RANGE_TEST_APP', index=11, number=66,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ENVIRONMENTAL_MEASUREMENT_APP', index=12, number=67,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ZPS_APP', index=13, number=68,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='PRIVATE_APP', index=14, number=256,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ATAK_FORWARDER', index=15, number=257,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MAX', index=16, number=511,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=19,
|
||||
serialized_end=350,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_PORTNUM)
|
||||
|
||||
PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM)
|
||||
UNKNOWN_APP = 0
|
||||
TEXT_MESSAGE_APP = 1
|
||||
REMOTE_HARDWARE_APP = 2
|
||||
POSITION_APP = 3
|
||||
NODEINFO_APP = 4
|
||||
ROUTING_APP = 5
|
||||
ADMIN_APP = 6
|
||||
REPLY_APP = 32
|
||||
IP_TUNNEL_APP = 33
|
||||
SERIAL_APP = 64
|
||||
STORE_FORWARD_APP = 65
|
||||
RANGE_TEST_APP = 66
|
||||
ENVIRONMENTAL_MEASUREMENT_APP = 67
|
||||
ZPS_APP = 68
|
||||
PRIVATE_APP = 256
|
||||
ATAK_FORWARDER = 257
|
||||
MAX = 511
|
||||
|
||||
|
||||
DESCRIPTOR.enum_types_by_name['PortNum'] = _PORTNUM
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
File diff suppressed because one or more lines are too long
@@ -1,67 +0,0 @@
|
||||
|
||||
from . import portnums_pb2, remote_hardware_pb2
|
||||
from pubsub import pub
|
||||
|
||||
|
||||
def onGPIOreceive(packet, interface):
|
||||
"""Callback for received GPIO responses
|
||||
|
||||
FIXME figure out how to do closures with methods in python"""
|
||||
hw = packet["decoded"]["remotehw"]
|
||||
print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={hw["gpioValue"]}')
|
||||
|
||||
|
||||
class RemoteHardwareClient:
|
||||
"""
|
||||
This is the client code to control/monitor simple hardware built into the
|
||||
meshtastic devices. It is intended to be both a useful API/service and example
|
||||
code for how you can connect to your own custom meshtastic services
|
||||
"""
|
||||
|
||||
def __init__(self, iface):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
iface is the already open MeshInterface instance
|
||||
"""
|
||||
self.iface = iface
|
||||
ch = iface.localNode.getChannelByName("gpio")
|
||||
if not ch:
|
||||
raise Exception(
|
||||
"No gpio channel found, please create on the sending and receive nodes to use this (secured) service (--ch-add gpio --info then --seturl)")
|
||||
self.channelIndex = ch.index
|
||||
|
||||
pub.subscribe(
|
||||
onGPIOreceive, "meshtastic.receive.remotehw")
|
||||
|
||||
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
|
||||
if not nodeid:
|
||||
raise Exception(
|
||||
"You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)")
|
||||
return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP,
|
||||
wantAck=True, channelIndex=self.channelIndex, wantResponse=wantResponse, onResponse=onResponse)
|
||||
|
||||
def writeGPIOs(self, nodeid, mask, vals):
|
||||
"""
|
||||
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
||||
are 1 will be changed
|
||||
"""
|
||||
r = remote_hardware_pb2.HardwareMessage()
|
||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
|
||||
r.gpio_mask = mask
|
||||
r.gpio_value = vals
|
||||
return self._sendHardware(nodeid, r)
|
||||
|
||||
def readGPIOs(self, nodeid, mask, onResponse = None):
|
||||
"""Read the specified bits from GPIO inputs on the device"""
|
||||
r = remote_hardware_pb2.HardwareMessage()
|
||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
|
||||
r.gpio_mask = mask
|
||||
return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse)
|
||||
|
||||
def watchGPIOs(self, nodeid, mask):
|
||||
"""Watch the specified bits from GPIO inputs on the device for changes"""
|
||||
r = remote_hardware_pb2.HardwareMessage()
|
||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
||||
r.gpio_mask = mask
|
||||
return self._sendHardware(nodeid, r)
|
||||
@@ -1,124 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: remote_hardware.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='remote_hardware.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\016RemoteHardwareH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\x15remote_hardware.proto\"\xca\x01\n\x0fHardwareMessage\x12\"\n\x03typ\x18\x01 \x01(\x0e\x32\x15.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42J\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
)
|
||||
|
||||
|
||||
|
||||
_HARDWAREMESSAGE_TYPE = _descriptor.EnumDescriptor(
|
||||
name='Type',
|
||||
full_name='HardwareMessage.Type',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='UNSET', index=0, number=0,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='WRITE_GPIOS', index=1, number=1,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='WATCH_GPIOS', index=2, number=2,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='GPIOS_CHANGED', index=3, number=3,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='READ_GPIOS', index=4, number=4,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='READ_GPIOS_REPLY', index=5, number=5,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=120,
|
||||
serialized_end=228,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_HARDWAREMESSAGE_TYPE)
|
||||
|
||||
|
||||
_HARDWAREMESSAGE = _descriptor.Descriptor(
|
||||
name='HardwareMessage',
|
||||
full_name='HardwareMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='typ', full_name='HardwareMessage.typ', index=0,
|
||||
number=1, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='gpio_mask', full_name='HardwareMessage.gpio_mask', index=1,
|
||||
number=2, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='gpio_value', full_name='HardwareMessage.gpio_value', index=2,
|
||||
number=3, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
_HARDWAREMESSAGE_TYPE,
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=26,
|
||||
serialized_end=228,
|
||||
)
|
||||
|
||||
_HARDWAREMESSAGE.fields_by_name['typ'].enum_type = _HARDWAREMESSAGE_TYPE
|
||||
_HARDWAREMESSAGE_TYPE.containing_type = _HARDWAREMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['HardwareMessage'] = _HARDWAREMESSAGE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
HardwareMessage = _reflection.GeneratedProtocolMessageType('HardwareMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _HARDWAREMESSAGE,
|
||||
'__module__' : 'remote_hardware_pb2'
|
||||
# @@protoc_insertion_point(class_scope:HardwareMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(HardwareMessage)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,291 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: storeforward.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='storeforward.proto',
|
||||
package='',
|
||||
syntax='proto3',
|
||||
serialized_options=b'\n\023com.geeksville.meshB\017StoreAndForwardH\003Z!github.com/meshtastic/gomeshproto',
|
||||
serialized_pb=b'\n\x12storeforward.proto\"\x8d\x05\n\x16StoreAndForwardMessage\x12\x33\n\x02rr\x18\x01 \x01(\x0e\x32\'.StoreAndForwardMessage.RequestResponse\x12\x31\n\x05stats\x18\x02 \x01(\x0b\x32\".StoreAndForwardMessage.Statistics\x12\x30\n\x07history\x18\x03 \x01(\x0b\x32\x1f.StoreAndForwardMessage.History\x1a\xc6\x01\n\nStatistics\x12\x15\n\rMessagesTotal\x18\x01 \x01(\r\x12\x15\n\rMessagesSaved\x18\x02 \x01(\r\x12\x13\n\x0bMessagesMax\x18\x03 \x01(\r\x12\x0e\n\x06UpTime\x18\x04 \x01(\r\x12\x10\n\x08Requests\x18\x05 \x01(\r\x12\x17\n\x0fRequestsHistory\x18\x06 \x01(\r\x12\x11\n\tHeartbeat\x18\x07 \x01(\x08\x12\x11\n\tReturnMax\x18\x08 \x01(\r\x12\x14\n\x0cReturnWindow\x18\t \x01(\r\x1a\x32\n\x07History\x12\x17\n\x0fHistoryMessages\x18\x01 \x01(\r\x12\x0e\n\x06Window\x18\x02 \x01(\r\"\xdb\x01\n\x0fRequestResponse\x12\t\n\x05UNSET\x10\x00\x12\x10\n\x0cROUTER_ERROR\x10\x01\x12\x14\n\x10ROUTER_HEARTBEAT\x10\x02\x12\x0f\n\x0bROUTER_PING\x10\x03\x12\x0f\n\x0bROUTER_PONG\x10\x04\x12\x0f\n\x0bROUTER_BUSY\x10\x05\x12\x10\n\x0c\x43LIENT_ERROR\x10\x65\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x66\x12\x10\n\x0c\x43LIENT_STATS\x10g\x12\x0f\n\x0b\x43LIENT_PING\x10h\x12\x0f\n\x0b\x43LIENT_PONG\x10i\x12\x08\n\x03MAX\x10\xff\x01\x42K\n\x13\x63om.geeksville.meshB\x0fStoreAndForwardH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
||||
)
|
||||
|
||||
|
||||
|
||||
_STOREANDFORWARDMESSAGE_REQUESTRESPONSE = _descriptor.EnumDescriptor(
|
||||
name='RequestResponse',
|
||||
full_name='StoreAndForwardMessage.RequestResponse',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='UNSET', index=0, number=0,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ROUTER_ERROR', index=1, number=1,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ROUTER_HEARTBEAT', index=2, number=2,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ROUTER_PING', index=3, number=3,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ROUTER_PONG', index=4, number=4,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ROUTER_BUSY', index=5, number=5,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='CLIENT_ERROR', index=6, number=101,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='CLIENT_HISTORY', index=7, number=102,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='CLIENT_STATS', index=8, number=103,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='CLIENT_PING', index=9, number=104,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='CLIENT_PONG', index=10, number=105,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MAX', index=11, number=255,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=457,
|
||||
serialized_end=676,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_STOREANDFORWARDMESSAGE_REQUESTRESPONSE)
|
||||
|
||||
|
||||
_STOREANDFORWARDMESSAGE_STATISTICS = _descriptor.Descriptor(
|
||||
name='Statistics',
|
||||
full_name='StoreAndForwardMessage.Statistics',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='MessagesTotal', full_name='StoreAndForwardMessage.Statistics.MessagesTotal', index=0,
|
||||
number=1, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='MessagesSaved', full_name='StoreAndForwardMessage.Statistics.MessagesSaved', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='MessagesMax', full_name='StoreAndForwardMessage.Statistics.MessagesMax', index=2,
|
||||
number=3, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='UpTime', full_name='StoreAndForwardMessage.Statistics.UpTime', index=3,
|
||||
number=4, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='Requests', full_name='StoreAndForwardMessage.Statistics.Requests', index=4,
|
||||
number=5, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='RequestsHistory', full_name='StoreAndForwardMessage.Statistics.RequestsHistory', index=5,
|
||||
number=6, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='Heartbeat', full_name='StoreAndForwardMessage.Statistics.Heartbeat', index=6,
|
||||
number=7, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='ReturnMax', full_name='StoreAndForwardMessage.Statistics.ReturnMax', index=7,
|
||||
number=8, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='ReturnWindow', full_name='StoreAndForwardMessage.Statistics.ReturnWindow', index=8,
|
||||
number=9, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=204,
|
||||
serialized_end=402,
|
||||
)
|
||||
|
||||
_STOREANDFORWARDMESSAGE_HISTORY = _descriptor.Descriptor(
|
||||
name='History',
|
||||
full_name='StoreAndForwardMessage.History',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='HistoryMessages', full_name='StoreAndForwardMessage.History.HistoryMessages', index=0,
|
||||
number=1, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='Window', full_name='StoreAndForwardMessage.History.Window', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=404,
|
||||
serialized_end=454,
|
||||
)
|
||||
|
||||
_STOREANDFORWARDMESSAGE = _descriptor.Descriptor(
|
||||
name='StoreAndForwardMessage',
|
||||
full_name='StoreAndForwardMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='rr', full_name='StoreAndForwardMessage.rr', index=0,
|
||||
number=1, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='stats', full_name='StoreAndForwardMessage.stats', index=1,
|
||||
number=2, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='history', full_name='StoreAndForwardMessage.history', index=2,
|
||||
number=3, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[_STOREANDFORWARDMESSAGE_STATISTICS, _STOREANDFORWARDMESSAGE_HISTORY, ],
|
||||
enum_types=[
|
||||
_STOREANDFORWARDMESSAGE_REQUESTRESPONSE,
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=23,
|
||||
serialized_end=676,
|
||||
)
|
||||
|
||||
_STOREANDFORWARDMESSAGE_STATISTICS.containing_type = _STOREANDFORWARDMESSAGE
|
||||
_STOREANDFORWARDMESSAGE_HISTORY.containing_type = _STOREANDFORWARDMESSAGE
|
||||
_STOREANDFORWARDMESSAGE.fields_by_name['rr'].enum_type = _STOREANDFORWARDMESSAGE_REQUESTRESPONSE
|
||||
_STOREANDFORWARDMESSAGE.fields_by_name['stats'].message_type = _STOREANDFORWARDMESSAGE_STATISTICS
|
||||
_STOREANDFORWARDMESSAGE.fields_by_name['history'].message_type = _STOREANDFORWARDMESSAGE_HISTORY
|
||||
_STOREANDFORWARDMESSAGE_REQUESTRESPONSE.containing_type = _STOREANDFORWARDMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['StoreAndForwardMessage'] = _STOREANDFORWARDMESSAGE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
StoreAndForwardMessage = _reflection.GeneratedProtocolMessageType('StoreAndForwardMessage', (_message.Message,), {
|
||||
|
||||
'Statistics' : _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), {
|
||||
'DESCRIPTOR' : _STOREANDFORWARDMESSAGE_STATISTICS,
|
||||
'__module__' : 'storeforward_pb2'
|
||||
# @@protoc_insertion_point(class_scope:StoreAndForwardMessage.Statistics)
|
||||
})
|
||||
,
|
||||
|
||||
'History' : _reflection.GeneratedProtocolMessageType('History', (_message.Message,), {
|
||||
'DESCRIPTOR' : _STOREANDFORWARDMESSAGE_HISTORY,
|
||||
'__module__' : 'storeforward_pb2'
|
||||
# @@protoc_insertion_point(class_scope:StoreAndForwardMessage.History)
|
||||
})
|
||||
,
|
||||
'DESCRIPTOR' : _STOREANDFORWARDMESSAGE,
|
||||
'__module__' : 'storeforward_pb2'
|
||||
# @@protoc_insertion_point(class_scope:StoreAndForwardMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(StoreAndForwardMessage)
|
||||
_sym_db.RegisterMessage(StoreAndForwardMessage.Statistics)
|
||||
_sym_db.RegisterMessage(StoreAndForwardMessage.History)
|
||||
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,182 +0,0 @@
|
||||
import logging
|
||||
from . import util
|
||||
from . import SerialInterface, TCPInterface, BROADCAST_NUM
|
||||
from pubsub import pub
|
||||
import time
|
||||
import sys
|
||||
import threading, traceback
|
||||
from dotmap import DotMap
|
||||
|
||||
"""The interfaces we are using for our tests"""
|
||||
interfaces = None
|
||||
|
||||
"""A list of all packets we received while the current test was running"""
|
||||
receivedPackets = None
|
||||
|
||||
testsRunning = False
|
||||
|
||||
testNumber = 0
|
||||
|
||||
sendingInterface = None
|
||||
|
||||
|
||||
def onReceive(packet, interface):
|
||||
"""Callback invoked when a packet arrives"""
|
||||
if sendingInterface == interface:
|
||||
pass
|
||||
# print("Ignoring sending interface")
|
||||
else:
|
||||
# print(f"From {interface.stream.port}: {packet}")
|
||||
p = DotMap(packet)
|
||||
|
||||
if p.decoded.portnum == "TEXT_MESSAGE_APP":
|
||||
# We only care a about clear text packets
|
||||
if receivedPackets != None:
|
||||
receivedPackets.append(p)
|
||||
|
||||
|
||||
def onNode(node):
|
||||
"""Callback invoked when the node DB changes"""
|
||||
print(f"Node changed: {node}")
|
||||
|
||||
|
||||
def subscribe():
|
||||
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
|
||||
|
||||
pub.subscribe(onNode, "meshtastic.node")
|
||||
|
||||
|
||||
def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False):
|
||||
"""
|
||||
Sends one test packet between two nodes and then returns success or failure
|
||||
|
||||
Arguments:
|
||||
fromInterface {[type]} -- [description]
|
||||
toInterface {[type]} -- [description]
|
||||
|
||||
Returns:
|
||||
boolean -- True for success
|
||||
"""
|
||||
global receivedPackets
|
||||
receivedPackets = []
|
||||
fromNode = fromInterface.myInfo.my_node_num
|
||||
|
||||
if isBroadcast:
|
||||
toNode = BROADCAST_NUM
|
||||
else:
|
||||
toNode = toInterface.myInfo.my_node_num
|
||||
|
||||
logging.debug(
|
||||
f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
||||
global sendingInterface
|
||||
sendingInterface = fromInterface
|
||||
if not asBinary:
|
||||
fromInterface.sendText(f"Test {testNumber}", toNode, wantAck=wantAck)
|
||||
else:
|
||||
fromInterface.sendData((f"Binary {testNumber}").encode(
|
||||
"utf-8"), toNode, wantAck=wantAck)
|
||||
for sec in range(60): # max of 60 secs before we timeout
|
||||
time.sleep(1)
|
||||
if (len(receivedPackets) >= 1):
|
||||
return True
|
||||
return False # Failed to send
|
||||
|
||||
|
||||
def runTests(numTests=50, wantAck=False, maxFailures=0):
|
||||
logging.info(f"Running {numTests} tests with wantAck={wantAck}")
|
||||
numFail = 0
|
||||
numSuccess = 0
|
||||
for i in range(numTests):
|
||||
global testNumber
|
||||
testNumber = testNumber + 1
|
||||
isBroadcast = True
|
||||
# asBinary=(i % 2 == 0)
|
||||
success = testSend(
|
||||
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck)
|
||||
if not success:
|
||||
numFail = numFail + 1
|
||||
logging.error(
|
||||
f"Test {testNumber} failed, expected packet not received ({numFail} failures so far)")
|
||||
else:
|
||||
numSuccess = numSuccess + 1
|
||||
logging.info(
|
||||
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far")
|
||||
|
||||
# if numFail >= 3:
|
||||
# for i in interfaces:
|
||||
# i.close()
|
||||
# return
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if numFail > maxFailures:
|
||||
logging.error("Too many failures! Test failed!")
|
||||
|
||||
return numFail
|
||||
|
||||
|
||||
def testThread(numTests=50):
|
||||
logging.info("Found devices, starting tests...")
|
||||
runTests(numTests, wantAck=True)
|
||||
# Allow a few dropped packets
|
||||
runTests(numTests, wantAck=False, maxFailures=5)
|
||||
|
||||
|
||||
def onConnection(topic=pub.AUTO_TOPIC):
|
||||
"""Callback invoked when we connect/disconnect from a radio"""
|
||||
print(f"Connection changed: {topic.getName()}")
|
||||
|
||||
|
||||
def openDebugLog(portName):
|
||||
debugname = "log" + portName.replace("/", "_")
|
||||
logging.info(f"Writing serial debugging to {debugname}")
|
||||
return open(debugname, 'w+', buffering=1)
|
||||
|
||||
|
||||
def testAll():
|
||||
"""
|
||||
Run a series of tests using devices we can find.
|
||||
|
||||
Raises:
|
||||
Exception: If not enough devices are found
|
||||
"""
|
||||
ports = util.findPorts()
|
||||
if (len(ports) < 2):
|
||||
raise Exception("Must have at least two devices connected to USB")
|
||||
|
||||
pub.subscribe(onConnection, "meshtastic.connection")
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
global interfaces
|
||||
interfaces = list(map(lambda port: SerialInterface(
|
||||
port, debugOut=openDebugLog(port), connectNow=True), ports))
|
||||
|
||||
logging.info("Ports opened, starting test")
|
||||
testThread()
|
||||
|
||||
for i in interfaces:
|
||||
i.close()
|
||||
|
||||
|
||||
def testSimulator():
|
||||
"""
|
||||
Assume that someone has launched meshtastic-native as a simulated node.
|
||||
Talk to that node over TCP, do some operations and if they are successful
|
||||
exit the process with a success code, else exit with a non zero exit code.
|
||||
|
||||
Run with
|
||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
||||
"""
|
||||
logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
|
||||
logging.info("Connecting to simulator on localhost!")
|
||||
try:
|
||||
iface = TCPInterface("localhost")
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.localNode.exitSimulator()
|
||||
iface.close()
|
||||
logging.info("Integration test successful!")
|
||||
except:
|
||||
print("Error while testing simulator:", sys.exc_info()[0])
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
@@ -1,207 +0,0 @@
|
||||
# code for IP tunnel over a mesh
|
||||
# Note python-pytuntap was too buggy
|
||||
# using pip3 install pytap2
|
||||
# make sure to "sudo setcap cap_net_admin+eip /usr/bin/python3.8" so python can access tun device without being root
|
||||
# sudo ip tuntap del mode tun tun0
|
||||
# sudo bin/run.sh --port /dev/ttyUSB0 --setch-shortfast
|
||||
# sudo bin/run.sh --port /dev/ttyUSB0 --tunnel --debug
|
||||
# ssh -Y root@192.168.10.151 (or dietpi), default password p
|
||||
# ncat -e /bin/cat -k -u -l 1235
|
||||
# ncat -u 10.115.64.152 1235
|
||||
# ping -c 1 -W 20 10.115.64.152
|
||||
# ping -i 30 -W 30 10.115.64.152
|
||||
|
||||
# FIXME: use a more optimal MTU
|
||||
|
||||
from . import portnums_pb2
|
||||
from pubsub import pub
|
||||
import logging
|
||||
import threading
|
||||
|
||||
# A new non standard log level that is lower level than DEBUG
|
||||
LOG_TRACE = 5
|
||||
|
||||
# fixme - find a way to move onTunnelReceive inside of the class
|
||||
tunnelInstance = None
|
||||
|
||||
"""A list of chatty UDP services we should never accidentally
|
||||
forward to our slow network"""
|
||||
udpBlacklist = {
|
||||
1900, # SSDP
|
||||
5353, # multicast DNS
|
||||
}
|
||||
|
||||
"""A list of TCP services to block"""
|
||||
tcpBlacklist = {}
|
||||
|
||||
"""A list of protocols we ignore"""
|
||||
protocolBlacklist = {
|
||||
0x02, # IGMP
|
||||
0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment
|
||||
}
|
||||
|
||||
|
||||
def hexstr(barray):
|
||||
"""Print a string of hex digits"""
|
||||
return ":".join('{:02x}'.format(x) for x in barray)
|
||||
|
||||
|
||||
def ipstr(barray):
|
||||
"""Print a string of ip digits"""
|
||||
return ".".join('{}'.format(x) for x in barray)
|
||||
|
||||
|
||||
def readnet_u16(p, offset):
|
||||
"""Read big endian u16 (network byte order)"""
|
||||
return p[offset] * 256 + p[offset + 1]
|
||||
|
||||
|
||||
def onTunnelReceive(packet, interface):
|
||||
"""Callback for received tunneled messages from mesh
|
||||
|
||||
FIXME figure out how to do closures with methods in python"""
|
||||
tunnelInstance.onReceive(packet)
|
||||
|
||||
|
||||
class Tunnel:
|
||||
"""A TUN based IP tunnel over meshtastic"""
|
||||
|
||||
def __init__(self, iface, subnet=None, netmask="255.255.0.0"):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
iface is the already open MeshInterface instance
|
||||
subnet is used to construct our network number (normally 10.115.x.x)
|
||||
"""
|
||||
|
||||
if subnet is None:
|
||||
subnet = "10.115"
|
||||
|
||||
self.iface = iface
|
||||
self.subnetPrefix = subnet
|
||||
|
||||
global tunnelInstance
|
||||
tunnelInstance = self
|
||||
|
||||
logging.info(
|
||||
"Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work). Mesh members:")
|
||||
|
||||
pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP")
|
||||
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
|
||||
|
||||
for node in self.iface.nodes.values():
|
||||
nodeId = node["user"]["id"]
|
||||
ip = self._nodeNumToIp(node["num"])
|
||||
logging.info(f"Node { nodeId } has IP address { ip }")
|
||||
|
||||
logging.debug("creating TUN device with MTU=200")
|
||||
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
|
||||
from pytap2 import TapDevice
|
||||
self.tun = TapDevice(name="mesh")
|
||||
self.tun.up()
|
||||
self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200)
|
||||
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
|
||||
self._rxThread = threading.Thread(
|
||||
target=self.__tunReader, args=(), daemon=True)
|
||||
self._rxThread.start()
|
||||
|
||||
def onReceive(self, packet):
|
||||
p = packet["decoded"]["payload"]
|
||||
if packet["from"] == self.iface.myInfo.my_node_num:
|
||||
logging.debug("Ignoring message we sent")
|
||||
else:
|
||||
logging.debug(
|
||||
f"Received mesh tunnel message type={type(p)} len={len(p)}")
|
||||
# we don't really need to check for filtering here (sender should have checked), but this provides
|
||||
# useful debug printing on types of packets received
|
||||
if not self._shouldFilterPacket(p):
|
||||
self.tun.write(p)
|
||||
|
||||
def _shouldFilterPacket(self, p):
|
||||
"""Given a packet, decode it and return true if it should be ignored"""
|
||||
protocol = p[8 + 1]
|
||||
srcaddr = p[12:16]
|
||||
destAddr = p[16:20]
|
||||
subheader = 20
|
||||
ignore = False # Assume we will be forwarding the packet
|
||||
if protocol in protocolBlacklist:
|
||||
ignore = True
|
||||
logging.log(
|
||||
LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}")
|
||||
elif protocol == 0x01: # ICMP
|
||||
icmpType = p[20]
|
||||
icmpCode = p[21]
|
||||
checksum = p[22:24]
|
||||
logging.debug(
|
||||
f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}")
|
||||
# reply to pings (swap src and dest but keep rest of packet unchanged)
|
||||
#pingback = p[:12]+p[16:20]+p[12:16]+p[20:]
|
||||
# tap.write(pingback)
|
||||
elif protocol == 0x11: # UDP
|
||||
srcport = readnet_u16(p, subheader)
|
||||
destport = readnet_u16(p, subheader + 2)
|
||||
if destport in udpBlacklist:
|
||||
ignore = True
|
||||
logging.log(
|
||||
LOG_TRACE, f"ignoring blacklisted UDP port {destport}")
|
||||
else:
|
||||
logging.debug(
|
||||
f"forwarding udp srcport={srcport}, destport={destport}")
|
||||
elif protocol == 0x06: # TCP
|
||||
srcport = readnet_u16(p, subheader)
|
||||
destport = readnet_u16(p, subheader + 2)
|
||||
if destport in tcpBlacklist:
|
||||
ignore = True
|
||||
logging.log(
|
||||
LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
||||
else:
|
||||
logging.debug(
|
||||
f"forwarding tcp srcport={srcport}, destport={destport}")
|
||||
else:
|
||||
logging.warning(
|
||||
f"forwarding unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destAddr)}")
|
||||
|
||||
return ignore
|
||||
|
||||
def __tunReader(self):
|
||||
tap = self.tun
|
||||
logging.debug("TUN reader running")
|
||||
while True:
|
||||
p = tap.read()
|
||||
#logging.debug(f"IP packet received on TUN interface, type={type(p)}")
|
||||
destAddr = p[16:20]
|
||||
|
||||
if not self._shouldFilterPacket(p):
|
||||
self.sendPacket(destAddr, p)
|
||||
|
||||
def _ipToNodeId(self, ipAddr):
|
||||
# We only consider the last 16 bits of the nodenum for IP address matching
|
||||
ipBits = ipAddr[2] * 256 + ipAddr[3]
|
||||
|
||||
if ipBits == 0xffff:
|
||||
return "^all"
|
||||
|
||||
for node in self.iface.nodes.values():
|
||||
nodeNum = node["num"] & 0xffff
|
||||
# logging.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}")
|
||||
if (nodeNum) == ipBits:
|
||||
return node["user"]["id"]
|
||||
return None
|
||||
|
||||
def _nodeNumToIp(self, nodeNum):
|
||||
return f"{self.subnetPrefix}.{(nodeNum >> 8) & 0xff}.{nodeNum & 0xff}"
|
||||
|
||||
def sendPacket(self, destAddr, p):
|
||||
"""Forward the provided IP packet into the mesh"""
|
||||
nodeId = self._ipToNodeId(destAddr)
|
||||
if nodeId is not None:
|
||||
logging.debug(
|
||||
f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
||||
self.iface.sendData(
|
||||
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
|
||||
else:
|
||||
logging.warning(
|
||||
f"Dropping packet because no node found for destIP={ipstr(destAddr)}")
|
||||
|
||||
def close(self):
|
||||
self.tun.close()
|
||||
@@ -1,90 +0,0 @@
|
||||
|
||||
from collections import defaultdict
|
||||
import serial, traceback
|
||||
import serial.tools.list_ports
|
||||
from queue import Queue
|
||||
import threading, sys, time, logging
|
||||
|
||||
"""Some devices such as a seger jlink we never want to accidentally open"""
|
||||
blacklistVids = dict.fromkeys([0x1366])
|
||||
|
||||
|
||||
def stripnl(s):
|
||||
"""remove newlines from a string (and remove extra whitespace)"""
|
||||
s = str(s).replace("\n", " ")
|
||||
return ' '.join(s.split())
|
||||
|
||||
|
||||
def fixme(message):
|
||||
raise Exception(f"FIXME: {message}")
|
||||
|
||||
|
||||
def catchAndIgnore(reason, closure):
|
||||
"""Call a closure but if it throws an excpetion print it and continue"""
|
||||
try:
|
||||
closure()
|
||||
except BaseException as ex:
|
||||
logging.error(f"Exception thrown in {reason}: {ex}")
|
||||
|
||||
|
||||
def findPorts():
|
||||
"""Find all ports that might have meshtastic devices
|
||||
|
||||
Returns:
|
||||
list -- a list of device paths
|
||||
"""
|
||||
l = list(map(lambda port: port.device,
|
||||
filter(lambda port: port.vid != None and port.vid not in blacklistVids,
|
||||
serial.tools.list_ports.comports())))
|
||||
l.sort()
|
||||
return l
|
||||
|
||||
|
||||
class dotdict(dict):
|
||||
"""dot.notation access to dictionary attributes"""
|
||||
__getattr__ = dict.get
|
||||
__setattr__ = dict.__setitem__
|
||||
__delattr__ = dict.__delitem__
|
||||
|
||||
|
||||
class Timeout:
|
||||
def __init__(self, maxSecs=20):
|
||||
self.expireTime = 0
|
||||
self.sleepInterval = 0.1
|
||||
self.expireTimeout = maxSecs
|
||||
|
||||
def reset(self):
|
||||
"""Restart the waitForSet timer"""
|
||||
self.expireTime = time.time() + self.expireTimeout
|
||||
|
||||
def waitForSet(self, target, attrs=()):
|
||||
"""Block until the specified attributes are set. Returns True if config has been received."""
|
||||
self.reset()
|
||||
while time.time() < self.expireTime:
|
||||
if all(map(lambda a: getattr(target, a, None), attrs)):
|
||||
return True
|
||||
time.sleep(self.sleepInterval)
|
||||
return False
|
||||
|
||||
|
||||
class DeferredExecution():
|
||||
"""A thread that accepts closures to run, and runs them as they are received"""
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.queue = Queue()
|
||||
self.thread = threading.Thread(target=self._run, args=(), name=name)
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
|
||||
def queueWork(self, runnable):
|
||||
self.queue.put(runnable)
|
||||
|
||||
def _run(self):
|
||||
while True:
|
||||
try:
|
||||
o = self.queue.get()
|
||||
o()
|
||||
except:
|
||||
logging.error(
|
||||
f"Unexpected error in deferred execution {sys.exc_info()[0]}")
|
||||
print(traceback.format_exc())
|
||||
Reference in New Issue
Block a user