diff --git a/README.md b/README.md index 5d90e8d..8256870 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Meshtastic-python + [![Open in Visual Studio Code](https://open.vscode.dev/badges/open-in-vscode.svg)](https://open.vscode.dev/meshtastic/Meshtastic-python) ![Unit Tests](https://github.com/meshtastic/Meshtastic-python/actions/workflows/ci.yml/badge.svg) - A python client for using [Meshtastic](https://www.meshtastic.org) devices. This small library (and example application) provides an easy API for sending and receiving messages over mesh radios. It also provides access to any of the operations/data available in the device user interface or the Android application. Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in. Full documentation including examples [here](https://meshtastic.github.io/Meshtastic-python/meshtastic/index.html). @@ -39,7 +39,7 @@ For the rough notes/implementation plan see [TODO](https://github.com/meshtastic This pip package will also install a "meshtastic" command line executable, which displays packets sent over the network as JSON and lets you see serial debugging information from the meshtastic devices. The source code for this tool is also a good [example](https://github.com/meshtastic/Meshtastic-python/blob/master/meshtastic/__main__.py) of a 'complete' application that uses the meshtastic python API. -NOTE: This command is not run inside of python; you run it from your operating system shell prompt directly. If when you type "meshtastic" it doesn't find the command and you are using Windows: Check that the python "scripts" directory [is in your path](https://datatofish.com/add-python-to-windows-path/). +NOTE: This command is not run inside of python; you run it from your operating system shell prompt directly. If when you type "meshtastic" it doesn't find the command and you are using Windows: Check that the python "scripts" directory [is in your path](https://datatofish.com/add-python-to-windows-path/). To display a (partial) list of the available commands: @@ -87,7 +87,7 @@ For a full list of preferences which can be set (and their documentation) see [h ### Changing channel settings -The channel settings can be changed similiarly. Either by using a standard (sharable) meshtastic URL or you can set partiular channel parameters (for advanced users). +The channel settings can be changed similiarly. Either by using a standard (sharable) meshtastic URL or you can set partiular channel parameters (for advanced users). The URL is constructed automatically based off of the current channel settings. So if you want to customize a channel you could do something like: @@ -117,7 +117,7 @@ meshtastic --ch-index 1 --ch-set name mychan --ch-set channel_num 4 --info ## Ham radio support -Meshtastic is designed to be used without a radio operator license. If you do have a license you can set your operator ID and turn off encryption with: +Meshtastic is designed to be used without a radio operator license. If you do have a license you can set your operator ID and turn off encryption with: ``` meshtastic --port /dev/ttyUSB1 --set-ham KI1345 @@ -131,6 +131,7 @@ Writing modified channels to device You can put parameters into a yaml file to update multiple values. See the [example_config.yaml](example_config.yaml). This is how you might call it: + ``` meshtastic --configure example_config.yaml ``` @@ -141,13 +142,13 @@ This is a collection of common questions and answers from our friendly forum. ### [Permission denied: ‘/dev/ttyUSB0’](https://meshtastic.discourse.group/t/question-on-permission-denied-dev-ttyusb0/590/3?u=geeksville) -This indicates an OS permission problem for access by your user to the USB serial port. Typically this is fixed by the following. +This indicates an OS permission problem for access by your user to the USB serial port. Typically this is fixed by the following. ``` sudo usermod -a -G dialout $USER ``` -This adds the "dialout" group to your user. You'll need to obtain a new login session (for example, by logging out and logging back in) for the group change (and thus permission change) to take effect. +This adds the "dialout" group to your user. You'll need to obtain a new login session (for example, by logging out and logging back in) for the group change (and thus permission change) to take effect. ## Mac OS Big Sur @@ -161,7 +162,7 @@ Afterwards you can use the Meshtastic python client again on MacOS. ## A note to developers of this lib -We use the visual-studio-code default python formatting conventions (autopep8). So if you use that IDE you should be able to use "Format Document" and not generate unrelated diffs. If you use some other editor, please don't change formatting on lines you haven't changed. +We use the visual-studio-code default python formatting conventions (autopep8). So if you use that IDE you should be able to use "Format Document" and not generate unrelated diffs. If you use some other editor, please don't change formatting on lines you haven't changed. If you need to build a new release you'll need: @@ -171,29 +172,33 @@ sudo pip3 install markdown pdoc3 webencodings pyparsing twine autopep8 pylint py ``` For development, you will probably want to run: + ``` pip3 install -r requirements.txt ``` - To lint, run: + ``` pylint meshtastic ``` To test, first install this code locally, then run pytest: + ``` pip3 install . pytest ``` + Possible options for testing: -* For more verbosity, add "-v" or even "-vv" like this: + +- For more verbosity, add "-v" or even "-vv" like this: ``` pytest -vv ``` -* To run just unit tests: +- To run just unit tests: ``` pytest @@ -203,13 +208,13 @@ pytest -m unit make ``` -* To run just integration tests: +- To run just integration tests: ``` pytest -m int ``` -* To run the smoke test with only one device connected serially (aka smoke1): +- To run the smoke test with only one device connected serially (aka smoke1): ``` pytest -m smoke1 @@ -218,19 +223,19 @@ pytest -m smoke1 CAUTION: Running smoke1 will reset values on the device, including the region to 1 (US). Be sure to hit the reset button on the device after the test is completed. -* To run the smoke test with only two device connected serially (aka smoke2): +- To run the smoke test with only two device connected serially (aka smoke2): ``` pytest -m smoke2 ``` -* To run the wifi smoke test: +- To run the wifi smoke test: ``` pytest -m smokewifi ``` -* To run a specific test: +- To run a specific test: ``` pytest -msmoke1 meshtastic/tests/test_smoke1.py::test_smoke1_info @@ -240,9 +245,9 @@ pytest -m smoke2 meshtastic/tests/test_smoke2.py::test_smoke2_info pytest -m smokewifi meshtastic/tests/test_smoke_wifi.py::test_smokewifi_info ``` -* To add another classification of tests such as "unit" or "smoke1", see [pytest.ini](pytest.ini). +- To add another classification of tests such as "unit" or "smoke1", see [pytest.ini](pytest.ini). -* To see the unit test code coverage: +- To see the unit test code coverage: ``` pytest --cov=meshtastic @@ -252,10 +257,12 @@ pytest --cov-report html --cov=meshtastic make cov ``` -* To see slowest unit tests, you can run: +- To see slowest unit tests, you can run: ``` pytest --durations=0 # or make slow ``` + +[![Powered by Vercel](https://raw.githubusercontent.com/abumalick/powered-by-vercel/master/powered-by-vercel.svg)](https://vercel.com?utm_source=meshtastic&utm_campaign=oss) diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index ea04c61..0000000 --- a/docs/index.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/meshtastic/__main__.html b/docs/meshtastic/__main__.html deleted file mode 100644 index 6fff0a1..0000000 --- a/docs/meshtastic/__main__.html +++ /dev/null @@ -1,1865 +0,0 @@ - - - - - - -meshtastic.__main__ API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.__main__

-
-
-

Main Meshtastic

-
- -Expand source code - -
#!python3
-""" Main Meshtastic
-"""
-
-import argparse
-import platform
-import logging
-import sys
-import time
-import yaml
-from pubsub import pub
-import pyqrcode
-import pkg_resources
-import meshtastic.util
-import meshtastic.test
-from . import remote_hardware
-from . import portnums_pb2, channel_pb2, radioconfig_pb2
-from .globals import Globals
-
-
-have_tunnel = platform.system() == 'Linux'
-"""We only import the tunnel code if we are on a platform that can run it. """
-
-def onReceive(packet, interface):
-    """Callback invoked when a packet arrives"""
-    our_globals = Globals.getInstance()
-    args = our_globals.get_args()
-    try:
-        d = packet.get('decoded')
-        logging.debug(f'in onReceive() d:{d}')
-
-        # Exit once we receive a reply
-        if args and 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 and args.reply:
-            msg = d.get('text')
-            if msg:
-                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()}")
-
-
-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__} does not have an attribute called {name}, so you can not get it.")
-        print(f"Choices in sorted order are:")
-        names = []
-        for f in objDesc.fields:
-            names.append(f'{f.name}')
-        for temp_name in sorted(names):
-            print(f"    {temp_name}")
-        return
-
-    # okay - try to read the value
-    try:
-        try:
-            val = getattr(attributes, name)
-        except TypeError:
-            # 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__} does not have an attribute called {name}, so you can not set it.")
-        print(f"Choices in sorted order are:")
-        names = []
-        for f in objDesc.fields:
-            names.append(f'{f.name}')
-        for temp_name in sorted(names):
-            print(f"    {temp_name}")
-        return
-
-    val = meshtastic.util.fromStr(valStr)
-
-    enumType = field.enum_type
-    # pylint: disable=C0123
-    if enumType and type(val) == str:
-        # We've failed so far to convert this string into an enum, try to find it by reflection
-        e = enumType.values_by_name.get(val)
-        if e:
-            val = e.number
-        else:
-            print(f"{name} does not have an enum called {val}, so you can not set it.")
-            print(f"Choices in sorted order are:")
-            names = []
-            for f in enumType.values:
-                names.append(f'{f.name}')
-            for temp_name in sorted(names):
-                print(f"    {temp_name}")
-            return
-
-    # okay - try to read the value
-    try:
-        try:
-            setattr(attributes, name, val)
-        except TypeError:
-            # 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}")
-
-
-def onConnected(interface):
-    """Callback invoked when we connect to a radio"""
-    closeNow = False  # Should we drop the connection after we finish?
-    try:
-        our_globals = Globals.getInstance()
-        args = our_globals.get_args()
-
-        print("Connected to radio")
-
-        def getNode():
-            """This operation could be expensive, so we try to cache the results"""
-            targetNode = our_globals.get_target_node()
-            if not targetNode:
-                targetNode = interface.getNode(args.destOrLocal)
-                our_globals.set_target_node(targetNode)
-            return targetNode
-
-        if args.setlat or args.setlon or args.setalt:
-            closeNow = True
-
-            alt = 0
-            lat = 0.0
-            lon = 0.0
-            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)
-            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.pos_fields:
-            # If --pos-fields invoked with args, set position fields
-            closeNow = True
-            prefs = getNode().radioConfig.preferences
-            allFields = 0
-
-            try:
-                for field in args.pos_fields:
-                    v_field = radioconfig_pb2.PositionFlags.Value(field)
-                    allFields |= v_field
-
-            except ValueError:
-                print("ERROR: supported position fields are:")
-                print(radioconfig_pb2.PositionFlags.keys())
-                print("If no fields are specified, will read and display current value.")
-
-            else:
-                print(f"Setting position fields to {allFields}")
-                setPref(prefs, 'position_flags', ('%d' % allFields))
-                print("Writing modified preferences to device")
-                getNode().writeConfig()
-
-        elif args.pos_fields is not None:
-            # If --pos-fields invoked without args, read and display current value
-            closeNow = True
-            prefs = getNode().radioConfig.preferences
-
-            fieldNames = []
-            for bit in radioconfig_pb2.PositionFlags.values():
-                if prefs.position_flags & bit:
-                    fieldNames.append(radioconfig_pb2.PositionFlags.Name(bit))
-            print(' '.join(fieldNames))
-
-        if args.set_team:
-            closeNow = True
-            try:
-                v_team = meshtastic.mesh_pb2.Team.Value(args.set_team.upper())
-            except ValueError:
-                v_team = 0
-                print(f"ERROR: Team \'{args.set_team}\' not found.")
-                print("Try a team name from the sorted list below, or use 'CLEAR' for unaffiliated:")
-                print(sorted(meshtastic.mesh_pb2.Team.keys()))
-            else:
-                print(f"Setting team to {meshtastic.mesh_pb2.Team.Name(v_team)}")
-                getNode().setOwner(team=v_team)
-
-        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 encryption on primary channel
-            getNode().turnOffEncryptionOnPrimaryChannel()
-
-        if args.reboot:
-            closeNow = True
-            getNode().reboot()
-
-        if args.sendtext:
-            closeNow = True
-            channelIndex = 0
-            if args.ch_index is not None:
-                channelIndex = int(args.ch_index)
-            ch = getNode().getChannelByChannelIndex(channelIndex)
-            if ch and ch.role != channel_pb2.Channel.Role.DISABLED:
-                print(f"Sending text message {args.sendtext} to {args.destOrAll} on channelIndex:{channelIndex}")
-                interface.sendText(args.sendtext, args.destOrAll, wantAck=True, channelIndex=channelIndex)
-            else:
-                meshtastic.util.our_exit(f"Warning: {channelIndex} is not a valid channel. Channel must not be DISABLED.")
-
-        if args.sendping:
-            payload = str.encode("test string")
-            print(f"Sending ping message to {args.destOrAll}")
-            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}")
-                interface.mask = bitmask
-                rhc.readGPIOs(args.dest, bitmask, None)
-                if not interface.noProto:
-                    # wait up to X seconds for a response
-                    for _ in range(10):
-                        time.sleep(1)
-                        if interface.gotResponse:
-                            break
-                logging.debug(f'end of gpio_rd')
-
-            if args.gpio_watch:
-                bitmask = int(args.gpio_watch, 16)
-                print(f"Watching GPIO mask 0x{bitmask:x} from {args.dest}. Press ctrl-c to exit")
-                while True:
-                    rhc.watchGPIOs(args.dest, bitmask)
-                    time.sleep(1)
-
-        # 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.configure:
-            with open(args.configure[0], encoding='utf8') as file:
-                configuration = yaml.safe_load(file)
-                closeNow = True
-
-                if 'owner' in configuration:
-                    print(f"Setting device owner to {configuration['owner']}")
-                    getNode().setOwner(configuration['owner'])
-
-                if 'channel_url' in configuration:
-                    print("Setting channel url to", configuration['channel_url'])
-                    getNode().setURL(configuration['channel_url'])
-
-                if 'location' in configuration:
-                    alt = 0
-                    lat = 0.0
-                    lon = 0.0
-                    prefs = interface.localNode.radioConfig.preferences
-
-                    if 'alt' in configuration['location']:
-                        alt = int(configuration['location']['alt'])
-                        prefs.fixed_position = True
-                        print(f"Fixing altitude at {alt} meters")
-                    if 'lat' in configuration['location']:
-                        lat = float(configuration['location']['lat'])
-                        prefs.fixed_position = True
-                        print(f"Fixing latitude at {lat} degrees")
-                    if 'lon' in configuration['location']:
-                        lon = float(configuration['location']['lon'])
-                        prefs.fixed_position = True
-                        print(f"Fixing longitude at {lon} degrees")
-                    print("Setting device position")
-                    interface.sendPosition(lat, lon, alt)
-                    interface.localNode.writeConfig()
-
-                if 'user_prefs' in configuration:
-                    prefs = getNode().radioConfig.preferences
-                    for pref in configuration['user_prefs']:
-                        setPref(prefs, pref, str(configuration['user_prefs'][pref]))
-                    print("Writing modified preferences to device")
-                    getNode().writeConfig()
-
-        if args.export_config:
-            # export the configuration (the opposite of '--configure')
-            closeNow = True
-            export_config(interface)
-
-        if args.seturl:
-            closeNow = True
-            getNode().setURL(args.seturl)
-
-        # handle changing channels
-
-        if args.ch_add:
-            closeNow = True
-            if len(args.ch_add) > 10:
-                meshtastic.util.our_exit("Warning: Channel name must be shorter. Channel not added.")
-            n = getNode()
-            ch = n.getChannelByName(args.ch_add)
-            if ch:
-                meshtastic.util.our_exit(f"Warning: This node already has a '{args.ch_add}' channel. No changes were made.")
-            else:
-                # get the first channel that is disabled (i.e., available)
-                ch = n.getDisabledChannel()
-                if not ch:
-                    meshtastic.util.our_exit("Warning: No free channels were found")
-                chs = channel_pb2.ChannelSettings()
-                chs.psk = meshtastic.util.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
-
-            channelIndex = our_globals.get_channel_index()
-            if channelIndex is None:
-                meshtastic.util.our_exit("Warning: Need to specify '--ch-index' for '--ch-del'.", 1)
-            else:
-                if channelIndex == 0:
-                    meshtastic.util.our_exit("Warning: Cannot delete primary channel.", 1)
-                else:
-                    print(f"Deleting channel {channelIndex}")
-                    ch = getNode().deleteChannel(channelIndex)
-
-        ch_changes = [args.ch_longslow, args.ch_longfast,
-                      args.ch_mediumslow, args.ch_mediumfast,
-                      args.ch_shortslow, args.ch_shortfast]
-        any_primary_channel_changes = any(x for x in ch_changes)
-        if args.ch_set or any_primary_channel_changes or args.ch_enable or args.ch_disable:
-            closeNow = True
-
-            channelIndex = our_globals.get_channel_index()
-            if channelIndex is None:
-                if any_primary_channel_changes:
-                    # we assume that they want the primary channel if they're setting range values
-                    channelIndex = 0
-                else:
-                    meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
-            ch = getNode().channels[channelIndex]
-
-            if any_primary_channel_changes or args.ch_enable or args.ch_disable:
-
-                if channelIndex == 0 and not any_primary_channel_changes:
-                    meshtastic.util.our_exit("Warning: Cannot enable/disable PRIMARY channel.")
-
-                if channelIndex != 0:
-                    if any_primary_channel_changes:
-                        meshtastic.util.our_exit("Warning: Standard channel settings can only be applied to the PRIMARY channel")
-
-                enable = True  # default to enable
-                if args.ch_enable:
-                    enable = True
-                if args.ch_disable:
-                    enable = False
-
-                def setSimpleChannel(modem_config):
-                    """Set one of the simple modem_config only based channels"""
-
-                    # Completely new channel settings
-                    chs = channel_pb2.ChannelSettings()
-                    chs.modem_config = modem_config
-                    chs.psk = bytes([1])  # Use default channel psk 1
-
-                    ch.settings.CopyFrom(chs)
-
-                # handle the simple channel set commands
-                if args.ch_longslow:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr48Sf4096)
-
-                if args.ch_longfast:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512)
-
-                if args.ch_mediumslow:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr46Sf2048)
-
-                if args.ch_mediumfast:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr47Sf1024)
-
-                if args.ch_shortslow:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr45Sf128)
-
-                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 = meshtastic.util.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:
-            # pylint: disable=C0415
-            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 export_config(interface):
-    """used in--export-config"""
-    owner = interface.getLongName()
-    channel_url = interface.localNode.getURL()
-    myinfo = interface.getMyNodeInfo()
-    pos = myinfo.get('position')
-    lat = None
-    lon = None
-    alt = None
-    if pos:
-        lat = pos.get('latitude')
-        lon = pos.get('longitude')
-        alt = pos.get('altitude')
-
-    config = "# start of Meshtastic configure yaml\n"
-    if owner:
-        config += f"owner: {owner}\n\n"
-    if channel_url:
-        config += f"channel_url: {channel_url}\n\n"
-    if lat or lon or alt:
-        config += "location:\n"
-        if lat:
-            config += f"  lat: {lat}\n"
-        if lon:
-            config += f"  lon: {lon}\n"
-        if alt:
-            config += f"  alt: {alt}\n"
-        config += "\n"
-    preferences = f'{interface.localNode.radioConfig.preferences}'
-    prefs = preferences.splitlines()
-    if prefs:
-        config += "user_prefs:\n"
-        for pref in prefs:
-            config += f"  {meshtastic.util.quoteBooleans(pref)}\n"
-    print(config)
-    return config
-
-
-def common():
-    """Shared code for all of our command line wrappers"""
-    our_globals = Globals.getInstance()
-    args = our_globals.get_args()
-    parser = our_globals.get_parser()
-    logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO,
-                        format='%(levelname)s file:%(filename)s %(funcName)s line:%(lineno)s %(message)s')
-
-    if len(sys.argv) == 1:
-        parser.print_help(sys.stderr)
-        meshtastic.util.our_exit("", 1)
-    else:
-        if args.support:
-            meshtastic.util.support_info()
-            meshtastic.util.our_exit("", 0)
-
-        if args.ch_index is not None:
-            channelIndex = int(args.ch_index)
-            our_globals.set_channel_index(channelIndex)
-
-        # 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 is not None:
-            logging.error(
-                'This option has been deprecated, see help below for the correct replacement...')
-            parser.print_help(sys.stderr)
-            meshtastic.util.our_exit('', 1)
-        elif args.test:
-            result = meshtastic.test.testAll()
-            if not result:
-                meshtastic.util.our_exit("Warning: Test was not successful.")
-            else:
-                meshtastic.util.our_exit("Test was a success.", 0)
-        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}")
-                # Note: using "line buffering"
-                # pylint: disable=R1732
-                logfile = open(args.seriallog, 'w+',
-                               buffering=1, encoding='utf8')
-
-            subscribe()
-            if args.ble:
-                client = meshtastic.ble_interface.BLEInterface(args.ble, debugOut=logfile, noProto=args.noproto)
-            elif args.host:
-                client = meshtastic.tcp_interface.TCPInterface(
-                    args.host, debugOut=logfile, noProto=args.noproto)
-            else:
-                client = meshtastic.serial_interface.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():
-    """Initialize the command line argument parsing."""
-    our_globals = Globals.getInstance()
-    parser = our_globals.get_parser()
-    args = our_globals.get_args()
-
-    parser.add_argument(
-        "--configure",
-        help="Specify a path to a yaml(.yml) file containing the desired settings for the connected device.",
-        action='append')
-
-    parser.add_argument(
-        "--export-config",
-        help="Export the configuration in yaml(.yml) format.",
-        action='store_true')
-
-    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. Use an invalid field such as '0' to get a list of all fields.", 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. Channels start at 0 (0 is the PRIMARY channel).", 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", default=False)
-
-    # Note: We are doing a double negative here (Do we want to disable? If ch_disable==True, then disable.)
-    parser.add_argument(
-        "--ch-disable", help="Disable the specified channel", action="store_true", dest="ch_disable", default=False)
-
-    parser.add_argument(
-        "--ch-set", help="Set a channel parameter", nargs=2, action='append')
-
-    parser.add_argument(
-        "--ch-longslow", help="Change to the long-range and slow channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-longfast", help="Change to the long-range and fast channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-mediumslow", help="Change to the medium-range and slow channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-mediumfast", help="Change to the medium-range and fast channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-shortslow", help="Change to the short-range and slow channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-shortfast", help="Change to the short-range and fast channel", action='store_true')
-
-
-    parser.add_argument(
-        "--set-owner", help="Set device owner name", action="store")
-
-    parser.add_argument(
-        "--set-team", help="Set team affiliation (an invalid team will list valid values)", action="store")
-
-    parser.add_argument(
-        "--set-ham", help="Set licensed Ham ID and turn off encryption", action="store")
-
-    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. Can specify a destination '--dest' and/or channel index '--ch-index'.")
-
-    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(
-        "--reply", help="Reply to received messages",
-        action="store_true")
-
-    parser.add_argument(
-        "--gpio-wrb", nargs=2, help="Set a particular GPIO # to 1 or 0", action='append')
-
-    parser.add_argument(
-        "--gpio-rd", help="Read from a GPIO mask (ex: '0x10')")
-
-    parser.add_argument(
-        "--gpio-watch", help="Start watching a GPIO mask for changes (ex: '0x10')")
-
-    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(
-        "--pos-fields", help="Specify fields to send when sending a position. Use no argument for a list of valid values. "\
-                             "Can pass multiple values as a space separated list like "\
-                             "this: '--pos-fields POS_ALTITUDE POS_ALT_MSL'",
-        nargs="*", action="store")
-
-    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}")
-
-    parser.add_argument(
-        "--support", action='store_true', help="Show support info (useful when troubleshooting an issue)")
-
-    args = parser.parse_args()
-    our_globals.set_args(args)
-    our_globals.set_parser(parser)
-
-
-def main():
-    """Perform command line meshtastic operations"""
-    our_globals = Globals.getInstance()
-    parser = argparse.ArgumentParser()
-    our_globals.set_parser(parser)
-    initParser()
-    common()
-
-
-def tunnelMain():
-    """Run a meshtastic IP tunnel"""
-    our_globals = Globals.getInstance()
-    parser = argparse.ArgumentParser()
-    our_globals.set_parser(parser)
-    initParser()
-    args = our_globals.get_args()
-    args.tunnel = True
-    our_globals.set_args(args)
-    common()
-
-
-if __name__ == "__main__":
-    main()
-
-
-
-
-
-

Global variables

-
-
var have_tunnel
-
-

We only import the tunnel code if we are on a platform that can run it.

-
-
-
-
-

Functions

-
-
-def common() -
-
-

Shared code for all of our command line wrappers

-
- -Expand source code - -
def common():
-    """Shared code for all of our command line wrappers"""
-    our_globals = Globals.getInstance()
-    args = our_globals.get_args()
-    parser = our_globals.get_parser()
-    logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO,
-                        format='%(levelname)s file:%(filename)s %(funcName)s line:%(lineno)s %(message)s')
-
-    if len(sys.argv) == 1:
-        parser.print_help(sys.stderr)
-        meshtastic.util.our_exit("", 1)
-    else:
-        if args.support:
-            meshtastic.util.support_info()
-            meshtastic.util.our_exit("", 0)
-
-        if args.ch_index is not None:
-            channelIndex = int(args.ch_index)
-            our_globals.set_channel_index(channelIndex)
-
-        # 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 is not None:
-            logging.error(
-                'This option has been deprecated, see help below for the correct replacement...')
-            parser.print_help(sys.stderr)
-            meshtastic.util.our_exit('', 1)
-        elif args.test:
-            result = meshtastic.test.testAll()
-            if not result:
-                meshtastic.util.our_exit("Warning: Test was not successful.")
-            else:
-                meshtastic.util.our_exit("Test was a success.", 0)
-        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}")
-                # Note: using "line buffering"
-                # pylint: disable=R1732
-                logfile = open(args.seriallog, 'w+',
-                               buffering=1, encoding='utf8')
-
-            subscribe()
-            if args.ble:
-                client = meshtastic.ble_interface.BLEInterface(args.ble, debugOut=logfile, noProto=args.noproto)
-            elif args.host:
-                client = meshtastic.tcp_interface.TCPInterface(
-                    args.host, debugOut=logfile, noProto=args.noproto)
-            else:
-                client = meshtastic.serial_interface.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 export_config(interface) -
-
-

used in–export-config

-
- -Expand source code - -
def export_config(interface):
-    """used in--export-config"""
-    owner = interface.getLongName()
-    channel_url = interface.localNode.getURL()
-    myinfo = interface.getMyNodeInfo()
-    pos = myinfo.get('position')
-    lat = None
-    lon = None
-    alt = None
-    if pos:
-        lat = pos.get('latitude')
-        lon = pos.get('longitude')
-        alt = pos.get('altitude')
-
-    config = "# start of Meshtastic configure yaml\n"
-    if owner:
-        config += f"owner: {owner}\n\n"
-    if channel_url:
-        config += f"channel_url: {channel_url}\n\n"
-    if lat or lon or alt:
-        config += "location:\n"
-        if lat:
-            config += f"  lat: {lat}\n"
-        if lon:
-            config += f"  lon: {lon}\n"
-        if alt:
-            config += f"  alt: {alt}\n"
-        config += "\n"
-    preferences = f'{interface.localNode.radioConfig.preferences}'
-    prefs = preferences.splitlines()
-    if prefs:
-        config += "user_prefs:\n"
-        for pref in prefs:
-            config += f"  {meshtastic.util.quoteBooleans(pref)}\n"
-    print(config)
-    return config
-
-
-
-def getPref(attributes, name) -
-
-

Get a channel or preferences value

-
- -Expand source code - -
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__} does not have an attribute called {name}, so you can not get it.")
-        print(f"Choices in sorted order are:")
-        names = []
-        for f in objDesc.fields:
-            names.append(f'{f.name}')
-        for temp_name in sorted(names):
-            print(f"    {temp_name}")
-        return
-
-    # okay - try to read the value
-    try:
-        try:
-            val = getattr(attributes, name)
-        except TypeError:
-            # 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 initParser() -
-
-

Initialize the command line argument parsing.

-
- -Expand source code - -
def initParser():
-    """Initialize the command line argument parsing."""
-    our_globals = Globals.getInstance()
-    parser = our_globals.get_parser()
-    args = our_globals.get_args()
-
-    parser.add_argument(
-        "--configure",
-        help="Specify a path to a yaml(.yml) file containing the desired settings for the connected device.",
-        action='append')
-
-    parser.add_argument(
-        "--export-config",
-        help="Export the configuration in yaml(.yml) format.",
-        action='store_true')
-
-    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. Use an invalid field such as '0' to get a list of all fields.", 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. Channels start at 0 (0 is the PRIMARY channel).", 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", default=False)
-
-    # Note: We are doing a double negative here (Do we want to disable? If ch_disable==True, then disable.)
-    parser.add_argument(
-        "--ch-disable", help="Disable the specified channel", action="store_true", dest="ch_disable", default=False)
-
-    parser.add_argument(
-        "--ch-set", help="Set a channel parameter", nargs=2, action='append')
-
-    parser.add_argument(
-        "--ch-longslow", help="Change to the long-range and slow channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-longfast", help="Change to the long-range and fast channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-mediumslow", help="Change to the medium-range and slow channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-mediumfast", help="Change to the medium-range and fast channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-shortslow", help="Change to the short-range and slow channel", action='store_true')
-
-    parser.add_argument(
-        "--ch-shortfast", help="Change to the short-range and fast channel", action='store_true')
-
-
-    parser.add_argument(
-        "--set-owner", help="Set device owner name", action="store")
-
-    parser.add_argument(
-        "--set-team", help="Set team affiliation (an invalid team will list valid values)", action="store")
-
-    parser.add_argument(
-        "--set-ham", help="Set licensed Ham ID and turn off encryption", action="store")
-
-    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. Can specify a destination '--dest' and/or channel index '--ch-index'.")
-
-    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(
-        "--reply", help="Reply to received messages",
-        action="store_true")
-
-    parser.add_argument(
-        "--gpio-wrb", nargs=2, help="Set a particular GPIO # to 1 or 0", action='append')
-
-    parser.add_argument(
-        "--gpio-rd", help="Read from a GPIO mask (ex: '0x10')")
-
-    parser.add_argument(
-        "--gpio-watch", help="Start watching a GPIO mask for changes (ex: '0x10')")
-
-    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(
-        "--pos-fields", help="Specify fields to send when sending a position. Use no argument for a list of valid values. "\
-                             "Can pass multiple values as a space separated list like "\
-                             "this: '--pos-fields POS_ALTITUDE POS_ALT_MSL'",
-        nargs="*", action="store")
-
-    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}")
-
-    parser.add_argument(
-        "--support", action='store_true', help="Show support info (useful when troubleshooting an issue)")
-
-    args = parser.parse_args()
-    our_globals.set_args(args)
-    our_globals.set_parser(parser)
-
-
-
-def main() -
-
-

Perform command line meshtastic operations

-
- -Expand source code - -
def main():
-    """Perform command line meshtastic operations"""
-    our_globals = Globals.getInstance()
-    parser = argparse.ArgumentParser()
-    our_globals.set_parser(parser)
-    initParser()
-    common()
-
-
-
-def onConnected(interface) -
-
-

Callback invoked when we connect to a radio

-
- -Expand source code - -
def onConnected(interface):
-    """Callback invoked when we connect to a radio"""
-    closeNow = False  # Should we drop the connection after we finish?
-    try:
-        our_globals = Globals.getInstance()
-        args = our_globals.get_args()
-
-        print("Connected to radio")
-
-        def getNode():
-            """This operation could be expensive, so we try to cache the results"""
-            targetNode = our_globals.get_target_node()
-            if not targetNode:
-                targetNode = interface.getNode(args.destOrLocal)
-                our_globals.set_target_node(targetNode)
-            return targetNode
-
-        if args.setlat or args.setlon or args.setalt:
-            closeNow = True
-
-            alt = 0
-            lat = 0.0
-            lon = 0.0
-            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)
-            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.pos_fields:
-            # If --pos-fields invoked with args, set position fields
-            closeNow = True
-            prefs = getNode().radioConfig.preferences
-            allFields = 0
-
-            try:
-                for field in args.pos_fields:
-                    v_field = radioconfig_pb2.PositionFlags.Value(field)
-                    allFields |= v_field
-
-            except ValueError:
-                print("ERROR: supported position fields are:")
-                print(radioconfig_pb2.PositionFlags.keys())
-                print("If no fields are specified, will read and display current value.")
-
-            else:
-                print(f"Setting position fields to {allFields}")
-                setPref(prefs, 'position_flags', ('%d' % allFields))
-                print("Writing modified preferences to device")
-                getNode().writeConfig()
-
-        elif args.pos_fields is not None:
-            # If --pos-fields invoked without args, read and display current value
-            closeNow = True
-            prefs = getNode().radioConfig.preferences
-
-            fieldNames = []
-            for bit in radioconfig_pb2.PositionFlags.values():
-                if prefs.position_flags & bit:
-                    fieldNames.append(radioconfig_pb2.PositionFlags.Name(bit))
-            print(' '.join(fieldNames))
-
-        if args.set_team:
-            closeNow = True
-            try:
-                v_team = meshtastic.mesh_pb2.Team.Value(args.set_team.upper())
-            except ValueError:
-                v_team = 0
-                print(f"ERROR: Team \'{args.set_team}\' not found.")
-                print("Try a team name from the sorted list below, or use 'CLEAR' for unaffiliated:")
-                print(sorted(meshtastic.mesh_pb2.Team.keys()))
-            else:
-                print(f"Setting team to {meshtastic.mesh_pb2.Team.Name(v_team)}")
-                getNode().setOwner(team=v_team)
-
-        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 encryption on primary channel
-            getNode().turnOffEncryptionOnPrimaryChannel()
-
-        if args.reboot:
-            closeNow = True
-            getNode().reboot()
-
-        if args.sendtext:
-            closeNow = True
-            channelIndex = 0
-            if args.ch_index is not None:
-                channelIndex = int(args.ch_index)
-            ch = getNode().getChannelByChannelIndex(channelIndex)
-            if ch and ch.role != channel_pb2.Channel.Role.DISABLED:
-                print(f"Sending text message {args.sendtext} to {args.destOrAll} on channelIndex:{channelIndex}")
-                interface.sendText(args.sendtext, args.destOrAll, wantAck=True, channelIndex=channelIndex)
-            else:
-                meshtastic.util.our_exit(f"Warning: {channelIndex} is not a valid channel. Channel must not be DISABLED.")
-
-        if args.sendping:
-            payload = str.encode("test string")
-            print(f"Sending ping message to {args.destOrAll}")
-            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}")
-                interface.mask = bitmask
-                rhc.readGPIOs(args.dest, bitmask, None)
-                if not interface.noProto:
-                    # wait up to X seconds for a response
-                    for _ in range(10):
-                        time.sleep(1)
-                        if interface.gotResponse:
-                            break
-                logging.debug(f'end of gpio_rd')
-
-            if args.gpio_watch:
-                bitmask = int(args.gpio_watch, 16)
-                print(f"Watching GPIO mask 0x{bitmask:x} from {args.dest}. Press ctrl-c to exit")
-                while True:
-                    rhc.watchGPIOs(args.dest, bitmask)
-                    time.sleep(1)
-
-        # 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.configure:
-            with open(args.configure[0], encoding='utf8') as file:
-                configuration = yaml.safe_load(file)
-                closeNow = True
-
-                if 'owner' in configuration:
-                    print(f"Setting device owner to {configuration['owner']}")
-                    getNode().setOwner(configuration['owner'])
-
-                if 'channel_url' in configuration:
-                    print("Setting channel url to", configuration['channel_url'])
-                    getNode().setURL(configuration['channel_url'])
-
-                if 'location' in configuration:
-                    alt = 0
-                    lat = 0.0
-                    lon = 0.0
-                    prefs = interface.localNode.radioConfig.preferences
-
-                    if 'alt' in configuration['location']:
-                        alt = int(configuration['location']['alt'])
-                        prefs.fixed_position = True
-                        print(f"Fixing altitude at {alt} meters")
-                    if 'lat' in configuration['location']:
-                        lat = float(configuration['location']['lat'])
-                        prefs.fixed_position = True
-                        print(f"Fixing latitude at {lat} degrees")
-                    if 'lon' in configuration['location']:
-                        lon = float(configuration['location']['lon'])
-                        prefs.fixed_position = True
-                        print(f"Fixing longitude at {lon} degrees")
-                    print("Setting device position")
-                    interface.sendPosition(lat, lon, alt)
-                    interface.localNode.writeConfig()
-
-                if 'user_prefs' in configuration:
-                    prefs = getNode().radioConfig.preferences
-                    for pref in configuration['user_prefs']:
-                        setPref(prefs, pref, str(configuration['user_prefs'][pref]))
-                    print("Writing modified preferences to device")
-                    getNode().writeConfig()
-
-        if args.export_config:
-            # export the configuration (the opposite of '--configure')
-            closeNow = True
-            export_config(interface)
-
-        if args.seturl:
-            closeNow = True
-            getNode().setURL(args.seturl)
-
-        # handle changing channels
-
-        if args.ch_add:
-            closeNow = True
-            if len(args.ch_add) > 10:
-                meshtastic.util.our_exit("Warning: Channel name must be shorter. Channel not added.")
-            n = getNode()
-            ch = n.getChannelByName(args.ch_add)
-            if ch:
-                meshtastic.util.our_exit(f"Warning: This node already has a '{args.ch_add}' channel. No changes were made.")
-            else:
-                # get the first channel that is disabled (i.e., available)
-                ch = n.getDisabledChannel()
-                if not ch:
-                    meshtastic.util.our_exit("Warning: No free channels were found")
-                chs = channel_pb2.ChannelSettings()
-                chs.psk = meshtastic.util.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
-
-            channelIndex = our_globals.get_channel_index()
-            if channelIndex is None:
-                meshtastic.util.our_exit("Warning: Need to specify '--ch-index' for '--ch-del'.", 1)
-            else:
-                if channelIndex == 0:
-                    meshtastic.util.our_exit("Warning: Cannot delete primary channel.", 1)
-                else:
-                    print(f"Deleting channel {channelIndex}")
-                    ch = getNode().deleteChannel(channelIndex)
-
-        ch_changes = [args.ch_longslow, args.ch_longfast,
-                      args.ch_mediumslow, args.ch_mediumfast,
-                      args.ch_shortslow, args.ch_shortfast]
-        any_primary_channel_changes = any(x for x in ch_changes)
-        if args.ch_set or any_primary_channel_changes or args.ch_enable or args.ch_disable:
-            closeNow = True
-
-            channelIndex = our_globals.get_channel_index()
-            if channelIndex is None:
-                if any_primary_channel_changes:
-                    # we assume that they want the primary channel if they're setting range values
-                    channelIndex = 0
-                else:
-                    meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
-            ch = getNode().channels[channelIndex]
-
-            if any_primary_channel_changes or args.ch_enable or args.ch_disable:
-
-                if channelIndex == 0 and not any_primary_channel_changes:
-                    meshtastic.util.our_exit("Warning: Cannot enable/disable PRIMARY channel.")
-
-                if channelIndex != 0:
-                    if any_primary_channel_changes:
-                        meshtastic.util.our_exit("Warning: Standard channel settings can only be applied to the PRIMARY channel")
-
-                enable = True  # default to enable
-                if args.ch_enable:
-                    enable = True
-                if args.ch_disable:
-                    enable = False
-
-                def setSimpleChannel(modem_config):
-                    """Set one of the simple modem_config only based channels"""
-
-                    # Completely new channel settings
-                    chs = channel_pb2.ChannelSettings()
-                    chs.modem_config = modem_config
-                    chs.psk = bytes([1])  # Use default channel psk 1
-
-                    ch.settings.CopyFrom(chs)
-
-                # handle the simple channel set commands
-                if args.ch_longslow:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr48Sf4096)
-
-                if args.ch_longfast:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512)
-
-                if args.ch_mediumslow:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr46Sf2048)
-
-                if args.ch_mediumfast:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr47Sf1024)
-
-                if args.ch_shortslow:
-                    setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr45Sf128)
-
-                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 = meshtastic.util.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:
-            # pylint: disable=C0415
-            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 onConnection(interface, topic=pubsub.core.callables.AUTO_TOPIC) -
-
-

Callback invoked when we connect/disconnect from a radio

-
- -Expand source code - -
def onConnection(interface, topic=pub.AUTO_TOPIC):
-    """Callback invoked when we connect/disconnect from a radio"""
-    print(f"Connection changed: {topic.getName()}")
-
-
-
-def onNode(node) -
-
-

Callback invoked when the node DB changes

-
- -Expand source code - -
def onNode(node):
-    """Callback invoked when the node DB changes"""
-    print(f"Node changed: {node}")
-
-
-
-def onReceive(packet, interface) -
-
-

Callback invoked when a packet arrives

-
- -Expand source code - -
def onReceive(packet, interface):
-    """Callback invoked when a packet arrives"""
-    our_globals = Globals.getInstance()
-    args = our_globals.get_args()
-    try:
-        d = packet.get('decoded')
-        logging.debug(f'in onReceive() d:{d}')
-
-        # Exit once we receive a reply
-        if args and 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 and args.reply:
-            msg = d.get('text')
-            if msg:
-                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 setPref(attributes, name, valStr) -
-
-

Set a channel or preferences value

-
- -Expand source code - -
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__} does not have an attribute called {name}, so you can not set it.")
-        print(f"Choices in sorted order are:")
-        names = []
-        for f in objDesc.fields:
-            names.append(f'{f.name}')
-        for temp_name in sorted(names):
-            print(f"    {temp_name}")
-        return
-
-    val = meshtastic.util.fromStr(valStr)
-
-    enumType = field.enum_type
-    # pylint: disable=C0123
-    if enumType and type(val) == str:
-        # We've failed so far to convert this string into an enum, try to find it by reflection
-        e = enumType.values_by_name.get(val)
-        if e:
-            val = e.number
-        else:
-            print(f"{name} does not have an enum called {val}, so you can not set it.")
-            print(f"Choices in sorted order are:")
-            names = []
-            for f in enumType.values:
-                names.append(f'{f.name}')
-            for temp_name in sorted(names):
-                print(f"    {temp_name}")
-            return
-
-    # okay - try to read the value
-    try:
-        try:
-            setattr(attributes, name, val)
-        except TypeError:
-            # 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}")
-
-
-
-def subscribe() -
-
-

Subscribe to the topics the user probably wants to see, prints output to stdout

-
- -Expand source code - -
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 tunnelMain() -
-
-

Run a meshtastic IP tunnel

-
- -Expand source code - -
def tunnelMain():
-    """Run a meshtastic IP tunnel"""
-    our_globals = Globals.getInstance()
-    parser = argparse.ArgumentParser()
-    our_globals.set_parser(parser)
-    initParser()
-    args = our_globals.get_args()
-    args.tunnel = True
-    our_globals.set_args(args)
-    common()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/admin_pb2.html b/docs/meshtastic/admin_pb2.html deleted file mode 100644 index 398e29c..0000000 --- a/docs/meshtastic/admin_pb2.html +++ /dev/null @@ -1,332 +0,0 @@ - - - - - - -meshtastic.admin_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.admin_pb2

-
-
-
- -Expand source code - -
# -*- 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()
-
-
-from . import channel_pb2 as channel__pb2
-from . import mesh_pb2 as mesh__pb2
-from . import radioconfig_pb2 as radioconfig__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\rchannel.proto\x1a\nmesh.proto\x1a\x11radioconfig.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=[channel__pb2.DESCRIPTOR,mesh__pb2.DESCRIPTOR,radioconfig__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)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class AdminMessage -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var confirm_set_channel
-
-

Field AdminMessage.confirm_set_channel

-
-
var confirm_set_radio
-
-

Field AdminMessage.confirm_set_radio

-
-
var exit_simulator
-
-

Field AdminMessage.exit_simulator

-
-
var get_channel_request
-
-

Field AdminMessage.get_channel_request

-
-
var get_channel_response
-
-

Field AdminMessage.get_channel_response

-
-
var get_radio_request
-
-

Field AdminMessage.get_radio_request

-
-
var get_radio_response
-
-

Field AdminMessage.get_radio_response

-
-
var reboot_seconds
-
-

Field AdminMessage.reboot_seconds

-
-
var set_channel
-
-

Field AdminMessage.set_channel

-
-
var set_owner
-
-

Field AdminMessage.set_owner

-
-
var set_radio
-
-

Field AdminMessage.set_radio

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/apponly_pb2.html b/docs/meshtastic/apponly_pb2.html deleted file mode 100644 index 10f9fb6..0000000 --- a/docs/meshtastic/apponly_pb2.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - -meshtastic.apponly_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.apponly_pb2

-
-
-
- -Expand source code - -
# -*- 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()
-
-
-from . 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)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ChannelSet -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var settings
-
-

Field ChannelSet.settings

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/ble.html b/docs/meshtastic/ble.html deleted file mode 100644 index 99ccd7a..0000000 --- a/docs/meshtastic/ble.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - -meshtastic.ble API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.ble

-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/ble_interface.html b/docs/meshtastic/ble_interface.html deleted file mode 100644 index ae4a971..0000000 --- a/docs/meshtastic/ble_interface.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - - -meshtastic.ble_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.ble_interface

-
-
-

Bluetooth interface

-
- -Expand source code - -
"""Bluetooth interface
-"""
-import logging
-import pygatt
-
-
-from .mesh_interface import MeshInterface
-
-# Our standard BLE characteristics
-TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
-FROMRADIO_UUID = "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
-FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453"
-
-
-class BLEInterface(MeshInterface):
-    """A not quite ready - FIXME - BLE interface to devices"""
-
-    def __init__(self, address, noProto=False, debugOut=None):
-        self.address = address
-        if not noProto:
-            self.adapter = pygatt.GATTToolBackend()  # BGAPIBackend()
-            self.adapter.start()
-            logging.debug(f"Connecting to {self.address}")
-            self.device = self.adapter.connect(address)
-        else:
-            self.adapter = None
-            self.device = None
-        logging.debug("Connected to device")
-        # fromradio = self.device.char_read(FROMRADIO_UUID)
-        MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
-
-        self._readFromRadio()  # read the initial responses
-
-        def handle_data(handle, data):
-            self._handleFromRadio(data)
-
-        if self.device:
-            self.device.subscribe(FROMNUM_UUID, callback=handle_data)
-
-    def _sendToRadioImpl(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        #logging.debug(f"Sending: {stripnl(toRadio)}")
-        b = toRadio.SerializeToString()
-        self.device.char_write(TORADIO_UUID, b)
-
-    def close(self):
-        MeshInterface.close(self)
-        if self.adapter:
-            self.adapter.stop()
-
-    def _readFromRadio(self):
-        if not self.noProto:
-            wasEmpty = False
-            while not wasEmpty:
-                if self.device:
-                    b = self.device.char_read(FROMRADIO_UUID)
-                    wasEmpty = len(b) == 0
-                    if not wasEmpty:
-                        self._handleFromRadio(b)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class BLEInterface -(address, noProto=False, debugOut=None) -
-
-

A not quite ready - FIXME - BLE interface to devices

-

Constructor

-

Keyword Arguments: -noProto – If True, don't try to run our protocol on the -link - just be a dumb serial client.

-
- -Expand source code - -
class BLEInterface(MeshInterface):
-    """A not quite ready - FIXME - BLE interface to devices"""
-
-    def __init__(self, address, noProto=False, debugOut=None):
-        self.address = address
-        if not noProto:
-            self.adapter = pygatt.GATTToolBackend()  # BGAPIBackend()
-            self.adapter.start()
-            logging.debug(f"Connecting to {self.address}")
-            self.device = self.adapter.connect(address)
-        else:
-            self.adapter = None
-            self.device = None
-        logging.debug("Connected to device")
-        # fromradio = self.device.char_read(FROMRADIO_UUID)
-        MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
-
-        self._readFromRadio()  # read the initial responses
-
-        def handle_data(handle, data):
-            self._handleFromRadio(data)
-
-        if self.device:
-            self.device.subscribe(FROMNUM_UUID, callback=handle_data)
-
-    def _sendToRadioImpl(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        #logging.debug(f"Sending: {stripnl(toRadio)}")
-        b = toRadio.SerializeToString()
-        self.device.char_write(TORADIO_UUID, b)
-
-    def close(self):
-        MeshInterface.close(self)
-        if self.adapter:
-            self.adapter.stop()
-
-    def _readFromRadio(self):
-        if not self.noProto:
-            wasEmpty = False
-            while not wasEmpty:
-                if self.device:
-                    b = self.device.char_read(FROMRADIO_UUID)
-                    wasEmpty = len(b) == 0
-                    if not wasEmpty:
-                        self._handleFromRadio(b)
-
-

Ancestors

- -

Methods

-
-
-def _readFromRadio(self) -
-
-
-
- -Expand source code - -
def _readFromRadio(self):
-    if not self.noProto:
-        wasEmpty = False
-        while not wasEmpty:
-            if self.device:
-                b = self.device.char_read(FROMRADIO_UUID)
-                wasEmpty = len(b) == 0
-                if not wasEmpty:
-                    self._handleFromRadio(b)
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/channel_pb2.html b/docs/meshtastic/channel_pb2.html deleted file mode 100644 index 86e9233..0000000 --- a/docs/meshtastic/channel_pb2.html +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - -meshtastic.channel_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.channel_pb2

-
-
-
- -Expand source code - -
# -*- 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\"\x91\x03\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\"\x8a\x01\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\x12\x13\n\x0f\x42w250Cr46Sf2048\x10\x04\x12\x13\n\x0f\x42w250Cr47Sf1024\x10\x05\"\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),
-    _descriptor.EnumValueDescriptor(
-      name='Bw250Cr46Sf2048', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='Bw250Cr47Sf1024', index=5, number=5,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=281,
-  serialized_end=419,
-)
-_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=513,
-  serialized_end=561,
-)
-_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=419,
-)
-
-
-_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=422,
-  serialized_end=561,
-)
-
-_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)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Channel -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var DISABLED
-
-
-
-
var PRIMARY
-
-
-
-
var Role
-
-
-
-
var SECONDARY
-
-
-
-
-

Instance variables

-
-
var index
-
-

Field Channel.index

-
-
var role
-
-

Field Channel.role

-
-
var settings
-
-

Field Channel.settings

-
-
-
-
-class ChannelSettings -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var Bw125Cr45Sf128
-
-
-
-
var Bw125Cr48Sf4096
-
-
-
-
var Bw250Cr46Sf2048
-
-
-
-
var Bw250Cr47Sf1024
-
-
-
-
var Bw31_25Cr48Sf512
-
-
-
-
var Bw500Cr45Sf128
-
-
-
-
var DESCRIPTOR
-
-
-
-
var ModemConfig
-
-
-
-
-

Instance variables

-
-
var bandwidth
-
-

Field ChannelSettings.bandwidth

-
-
var channel_num
-
-

Field ChannelSettings.channel_num

-
-
var coding_rate
-
-

Field ChannelSettings.coding_rate

-
- -
-

Field ChannelSettings.downlink_enabled

-
-
var id
-
-

Field ChannelSettings.id

-
-
var modem_config
-
-

Field ChannelSettings.modem_config

-
-
var name
-
-

Field ChannelSettings.name

-
-
var psk
-
-

Field ChannelSettings.psk

-
-
var spread_factor
-
-

Field ChannelSettings.spread_factor

-
-
var tx_power
-
-

Field ChannelSettings.tx_power

-
- -
-

Field ChannelSettings.uplink_enabled

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/deviceonly_pb2.html b/docs/meshtastic/deviceonly_pb2.html deleted file mode 100644 index 2062f76..0000000 --- a/docs/meshtastic/deviceonly_pb2.html +++ /dev/null @@ -1,464 +0,0 @@ - - - - - - -meshtastic.deviceonly_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.deviceonly_pb2

-
-
-
- -Expand source code - -
# -*- 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()
-
-
-from . import channel_pb2 as channel__pb2
-from . import mesh_pb2 as mesh__pb2
-from . 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\rchannel.proto\x1a\nmesh.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=[channel__pb2.DESCRIPTOR,mesh__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)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ChannelFile -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var channels
-
-

Field ChannelFile.channels

-
-
-
-
-class DeviceState -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var did_gps_reset
-
-

Field DeviceState.did_gps_reset

-
-
var legacyRadio
-
-

Field DeviceState.legacyRadio

-
-
var my_node
-
-

Field DeviceState.my_node

-
-
var no_save
-
-

Field DeviceState.no_save

-
-
var node_db
-
-

Field DeviceState.node_db

-
-
var owner
-
-

Field DeviceState.owner

-
-
var receive_queue
-
-

Field DeviceState.receive_queue

-
-
var rx_text_message
-
-

Field DeviceState.rx_text_message

-
-
var version
-
-

Field DeviceState.version

-
-
-
-
-class LegacyRadioConfig -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var LegacyPreferences
-
-

A ProtocolMessage

-
-
-

Instance variables

-
-
var preferences
-
-

Field LegacyRadioConfig.preferences

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/environmental_measurement_pb2.html b/docs/meshtastic/environmental_measurement_pb2.html deleted file mode 100644 index fa6ff99..0000000 --- a/docs/meshtastic/environmental_measurement_pb2.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - -meshtastic.environmental_measurement_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.environmental_measurement_pb2

-
-
-
- -Expand source code - -
# -*- 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)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class EnvironmentalMeasurement -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var barometric_pressure
-
-

Field EnvironmentalMeasurement.barometric_pressure

-
-
var relative_humidity
-
-

Field EnvironmentalMeasurement.relative_humidity

-
-
var temperature
-
-

Field EnvironmentalMeasurement.temperature

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/globals.html b/docs/meshtastic/globals.html deleted file mode 100644 index 35b945b..0000000 --- a/docs/meshtastic/globals.html +++ /dev/null @@ -1,388 +0,0 @@ - - - - - - -meshtastic.globals API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.globals

-
-
-

Globals singleton class.

-

Instead of using a global, stuff your variables in this "trash can". -This is not much better than using python's globals, but it allows -us to better test meshtastic. Plus, there are some weird python -global issues/gotcha that we can hopefully avoid by using this -class instead.

-
- -Expand source code - -
"""Globals singleton class.
-
-   Instead of using a global, stuff your variables in this "trash can".
-   This is not much better than using python's globals, but it allows
-   us to better test meshtastic. Plus, there are some weird python
-   global issues/gotcha that we can hopefully avoid by using this
-   class instead.
-
-"""
-
-class Globals:
-    """Globals class is a Singleton."""
-    __instance = None
-
-    @staticmethod
-    def getInstance():
-        """Get an instance of the Globals class."""
-        if Globals.__instance is None:
-            Globals()
-        return Globals.__instance
-
-    def __init__(self):
-        """Constructor for the Globals CLass"""
-        if Globals.__instance is not None:
-            raise Exception("This class is a singleton")
-        else:
-            Globals.__instance = self
-        self.args = None
-        self.parser = None
-        self.target_node = None
-        self.channel_index = None
-
-    def reset(self):
-        """Reset all of our globals. If you add a member, add it to this method, too."""
-        self.args = None
-        self.parser = None
-        self.target_node = None
-        self.channel_index = None
-
-    def set_args(self, args):
-        """Set the args"""
-        self.args = args
-
-    def set_parser(self, parser):
-        """Set the parser"""
-        self.parser = parser
-
-    def set_target_node(self, target_node):
-        """Set the target_node"""
-        self.target_node = target_node
-
-    def set_channel_index(self, channel_index):
-        """Set the channel_index"""
-        self.channel_index = channel_index
-
-    def get_args(self):
-        """Get args"""
-        return self.args
-
-    def get_parser(self):
-        """Get parser"""
-        return self.parser
-
-    def get_target_node(self):
-        """Get target_node"""
-        return self.target_node
-
-    def get_channel_index(self):
-        """Get channel_index"""
-        return self.channel_index
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Globals -
-
-

Globals class is a Singleton.

-

Constructor for the Globals CLass

-
- -Expand source code - -
class Globals:
-    """Globals class is a Singleton."""
-    __instance = None
-
-    @staticmethod
-    def getInstance():
-        """Get an instance of the Globals class."""
-        if Globals.__instance is None:
-            Globals()
-        return Globals.__instance
-
-    def __init__(self):
-        """Constructor for the Globals CLass"""
-        if Globals.__instance is not None:
-            raise Exception("This class is a singleton")
-        else:
-            Globals.__instance = self
-        self.args = None
-        self.parser = None
-        self.target_node = None
-        self.channel_index = None
-
-    def reset(self):
-        """Reset all of our globals. If you add a member, add it to this method, too."""
-        self.args = None
-        self.parser = None
-        self.target_node = None
-        self.channel_index = None
-
-    def set_args(self, args):
-        """Set the args"""
-        self.args = args
-
-    def set_parser(self, parser):
-        """Set the parser"""
-        self.parser = parser
-
-    def set_target_node(self, target_node):
-        """Set the target_node"""
-        self.target_node = target_node
-
-    def set_channel_index(self, channel_index):
-        """Set the channel_index"""
-        self.channel_index = channel_index
-
-    def get_args(self):
-        """Get args"""
-        return self.args
-
-    def get_parser(self):
-        """Get parser"""
-        return self.parser
-
-    def get_target_node(self):
-        """Get target_node"""
-        return self.target_node
-
-    def get_channel_index(self):
-        """Get channel_index"""
-        return self.channel_index
-
-

Class variables

-
-
var _Globals__instance
-
-
-
-
-

Static methods

-
-
-def getInstance() -
-
-

Get an instance of the Globals class.

-
- -Expand source code - -
@staticmethod
-def getInstance():
-    """Get an instance of the Globals class."""
-    if Globals.__instance is None:
-        Globals()
-    return Globals.__instance
-
-
-
-

Methods

-
-
-def get_args(self) -
-
-

Get args

-
- -Expand source code - -
def get_args(self):
-    """Get args"""
-    return self.args
-
-
-
-def get_channel_index(self) -
-
-

Get channel_index

-
- -Expand source code - -
def get_channel_index(self):
-    """Get channel_index"""
-    return self.channel_index
-
-
-
-def get_parser(self) -
-
-

Get parser

-
- -Expand source code - -
def get_parser(self):
-    """Get parser"""
-    return self.parser
-
-
-
-def get_target_node(self) -
-
-

Get target_node

-
- -Expand source code - -
def get_target_node(self):
-    """Get target_node"""
-    return self.target_node
-
-
-
-def reset(self) -
-
-

Reset all of our globals. If you add a member, add it to this method, too.

-
- -Expand source code - -
def reset(self):
-    """Reset all of our globals. If you add a member, add it to this method, too."""
-    self.args = None
-    self.parser = None
-    self.target_node = None
-    self.channel_index = None
-
-
-
-def set_args(self, args) -
-
-

Set the args

-
- -Expand source code - -
def set_args(self, args):
-    """Set the args"""
-    self.args = args
-
-
-
-def set_channel_index(self, channel_index) -
-
-

Set the channel_index

-
- -Expand source code - -
def set_channel_index(self, channel_index):
-    """Set the channel_index"""
-    self.channel_index = channel_index
-
-
-
-def set_parser(self, parser) -
-
-

Set the parser

-
- -Expand source code - -
def set_parser(self, parser):
-    """Set the parser"""
-    self.parser = parser
-
-
-
-def set_target_node(self, target_node) -
-
-

Set the target_node

-
- -Expand source code - -
def set_target_node(self, target_node):
-    """Set the target_node"""
-    self.target_node = target_node
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/index.html b/docs/meshtastic/index.html deleted file mode 100644 index 54c3fc0..0000000 --- a/docs/meshtastic/index.html +++ /dev/null @@ -1,767 +0,0 @@ - - - - - - -meshtastic API documentation - - - - - - - - - - - -
-
-
-

Package meshtastic

-
-
-

an API for Meshtastic devices

-

Primary class: SerialInterface -Install with pip: "pip3 install meshtastic" -Source code on github

-

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 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
-import meshtastic.serial_interface
-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.serial_interface.SerialInterface()
-
-
-
- -Expand source code - -
"""
-# 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
-import meshtastic.serial_interface
-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.serial_interface.SerialInterface()
-
-```
-
-"""
-
-import base64
-import logging
-import os
-import platform
-import random
-import socket
-import sys
-import stat
-import threading
-import traceback
-import time
-from datetime import datetime
-from typing import *
-import serial
-import timeago
-import google.protobuf.json_format
-import pygatt
-from pubsub import pub
-from dotmap import DotMap
-from tabulate import tabulate
-from google.protobuf.json_format import MessageToJson
-from .util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
-from .node import Node
-from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util
-
-# Note: To follow PEP224, comments should be after the module variable.
-
-LOCAL_ADDR = "^local"
-"""A special ID that means the local node"""
-
-BROADCAST_NUM = 0xffffffff
-"""if using 8 bit nodenums this will be shortend on the target"""
-
-BROADCAST_ADDR = "^all"
-"""A special ID that means broadcast"""
-
-OUR_APP_VERSION = 20200
-"""The numeric buildnumber (shared with android apps) specifying the
-   level of device code we are guaranteed to understand
-
-   format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
-"""
-
-publishingThread = DeferredExecution("publishing")
-
-
-class ResponseHandler(NamedTuple):
-    """A pending response callback, waiting for a response to one of our messages"""
-    # requestId: int - used only as a key
-    callback: Callable
-    # FIXME, add timestamp and age out old requests
-
-
-class KnownProtocol(NamedTuple):
-    """Used to automatically decode known protocol payloads"""
-    name: str
-    # portnum: int, now a key
-    # If set, will be called to prase as a protocol buffer
-    protobufFactory: Callable = None
-    # If set, invoked as onReceive(interface, packet)
-    onReceive: Callable = None
-
-
-def _onTextReceive(iface, asDict):
-    """Special text auto parsing for received messages"""
-    # We don't throw if the utf8 is invalid in the text message.  Instead we just don't populate
-    # the decoded.data.text and we log an error message.  This at least allows some delivery to
-    # the app and the app can deal with the missing decoded representation.
-    #
-    # Usually btw this problem is caused by apps sending binary data but setting the payload type to
-    # text.
-    try:
-        asBytes = asDict["decoded"]["payload"]
-        asDict["decoded"]["text"] = asBytes.decode("utf-8")
-    except Exception as ex:
-        logging.error(f"Malformatted utf8 in text message: {ex}")
-    _receiveInfoUpdate(iface, asDict)
-
-
-def _onPositionReceive(iface, asDict):
-    """Special auto parsing for received messages"""
-    p = asDict["decoded"]["position"]
-    iface._fixupPosition(p)
-    # update node DB as needed
-    iface._getOrCreateByNum(asDict["from"])["position"] = p
-
-
-def _onNodeInfoReceive(iface, asDict):
-    """Special auto parsing for received messages"""
-    p = asDict["decoded"]["user"]
-    # decode user protobufs and update nodedb, provide decoded version as "position" in the published msg
-    # update node DB as needed
-    n = iface._getOrCreateByNum(asDict["from"])
-    n["user"] = p
-    # We now have a node ID, make sure it is uptodate in that table
-    iface.nodes[p["id"]] = n
-    _receiveInfoUpdate(iface, asDict)
-
-
-def _receiveInfoUpdate(iface, asDict):
-    if "from" in asDict:
-        iface._getOrCreateByNum(asDict["from"])["lastReceived"] = asDict
-        iface._getOrCreateByNum(asDict["from"])["lastHeard"] = asDict.get("rxTime")
-        iface._getOrCreateByNum(asDict["from"])["snr"] = asDict.get("rxSnr")
-        iface._getOrCreateByNum(asDict["from"])["hopLimit"] = asDict.get("hopLimit")
-
-
-"""Well known message payloads can register decoders for automatic protobuf parsing"""
-protocols = {
-    portnums_pb2.PortNum.TEXT_MESSAGE_APP: KnownProtocol("text", onReceive=_onTextReceive),
-    portnums_pb2.PortNum.POSITION_APP: KnownProtocol("position", mesh_pb2.Position, _onPositionReceive),
-    portnums_pb2.PortNum.NODEINFO_APP: KnownProtocol("user", mesh_pb2.User, _onNodeInfoReceive),
-    portnums_pb2.PortNum.ADMIN_APP: KnownProtocol("admin", admin_pb2.AdminMessage),
-    portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
-    portnums_pb2.PortNum.ENVIRONMENTAL_MEASUREMENT_APP: KnownProtocol("environmental", environmental_measurement_pb2.EnvironmentalMeasurement),
-    portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
-        "remotehw", remote_hardware_pb2.HardwareMessage)
-}
-
-
-
-

Sub-modules

-
-
meshtastic.admin_pb2
-
-
-
-
meshtastic.apponly_pb2
-
-
-
-
meshtastic.ble
-
-
-
-
meshtastic.ble_interface
-
-

Bluetooth interface

-
-
meshtastic.channel_pb2
-
-
-
-
meshtastic.deviceonly_pb2
-
-
-
-
meshtastic.environmental_measurement_pb2
-
-
-
-
meshtastic.globals
-
-

Globals singleton class …

-
-
meshtastic.mesh_interface
-
-

Mesh Interface class

-
-
meshtastic.mesh_pb2
-
-
-
-
meshtastic.mqtt_pb2
-
-
-
-
meshtastic.node
-
-

Node class

-
-
meshtastic.portnums_pb2
-
-
-
-
meshtastic.radioconfig_pb2
-
-
-
-
meshtastic.remote_hardware
-
-

Remote hardware

-
-
meshtastic.remote_hardware_pb2
-
-
-
-
meshtastic.serial_interface
-
-

Serial interface class

-
-
meshtastic.storeforward_pb2
-
-
-
-
meshtastic.stream_interface
-
-

Stream Interface base class

-
-
meshtastic.tcp_interface
-
-

TCPInterface class for interfacing with http endpoint

-
-
meshtastic.test
-
-

With two radios connected serially, send and receive test -messages and report back if successful.

-
-
meshtastic.tests
-
-
-
-
meshtastic.tunnel
-
-

Code for IP tunnel over a mesh …

-
-
meshtastic.util
-
-

Utility functions.

-
-
-
-
-

Global variables

-
-
var BROADCAST_ADDR
-
-

A special ID that means broadcast

-
-
var BROADCAST_NUM
-
-

if using 8 bit nodenums this will be shortend on the target

-
-
var LOCAL_ADDR
-
-

A special ID that means the local node

-
-
var OUR_APP_VERSION
-
-

The numeric buildnumber (shared with android apps) specifying the -level of device code we are guaranteed to understand

-

format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20

-
-
-
-
-

Functions

-
-
-def _onNodeInfoReceive(iface, asDict) -
-
-

Special auto parsing for received messages

-
- -Expand source code - -
def _onNodeInfoReceive(iface, asDict):
-    """Special auto parsing for received messages"""
-    p = asDict["decoded"]["user"]
-    # decode user protobufs and update nodedb, provide decoded version as "position" in the published msg
-    # update node DB as needed
-    n = iface._getOrCreateByNum(asDict["from"])
-    n["user"] = p
-    # We now have a node ID, make sure it is uptodate in that table
-    iface.nodes[p["id"]] = n
-    _receiveInfoUpdate(iface, asDict)
-
-
-
-def _onPositionReceive(iface, asDict) -
-
-

Special auto parsing for received messages

-
- -Expand source code - -
def _onPositionReceive(iface, asDict):
-    """Special auto parsing for received messages"""
-    p = asDict["decoded"]["position"]
-    iface._fixupPosition(p)
-    # update node DB as needed
-    iface._getOrCreateByNum(asDict["from"])["position"] = p
-
-
-
-def _onTextReceive(iface, asDict) -
-
-

Special text auto parsing for received messages

-
- -Expand source code - -
def _onTextReceive(iface, asDict):
-    """Special text auto parsing for received messages"""
-    # We don't throw if the utf8 is invalid in the text message.  Instead we just don't populate
-    # the decoded.data.text and we log an error message.  This at least allows some delivery to
-    # the app and the app can deal with the missing decoded representation.
-    #
-    # Usually btw this problem is caused by apps sending binary data but setting the payload type to
-    # text.
-    try:
-        asBytes = asDict["decoded"]["payload"]
-        asDict["decoded"]["text"] = asBytes.decode("utf-8")
-    except Exception as ex:
-        logging.error(f"Malformatted utf8 in text message: {ex}")
-    _receiveInfoUpdate(iface, asDict)
-
-
-
-def _receiveInfoUpdate(iface, asDict) -
-
-
-
- -Expand source code - -
def _receiveInfoUpdate(iface, asDict):
-    if "from" in asDict:
-        iface._getOrCreateByNum(asDict["from"])["lastReceived"] = asDict
-        iface._getOrCreateByNum(asDict["from"])["lastHeard"] = asDict.get("rxTime")
-        iface._getOrCreateByNum(asDict["from"])["snr"] = asDict.get("rxSnr")
-        iface._getOrCreateByNum(asDict["from"])["hopLimit"] = asDict.get("hopLimit")
-
-
-
-
-
-

Classes

-
-
-class KnownProtocol -(name: str, protobufFactory: Callable = None, onReceive: Callable = None) -
-
-

Used to automatically decode known protocol payloads

-
- -Expand source code - -
class KnownProtocol(NamedTuple):
-    """Used to automatically decode known protocol payloads"""
-    name: str
-    # portnum: int, now a key
-    # If set, will be called to prase as a protocol buffer
-    protobufFactory: Callable = None
-    # If set, invoked as onReceive(interface, packet)
-    onReceive: Callable = None
-
-

Ancestors

-
    -
  • builtins.tuple
  • -
-

Class variables

-
-
var _field_defaults
-
-
-
-
var _fields
-
-
-
-
-

Static methods

-
-
-def _make(iterable) -
-
-

Make a new KnownProtocol object from a sequence or iterable

-
- -Expand source code - -
@classmethod
-def _make(cls, iterable):
-    result = tuple_new(cls, iterable)
-    if _len(result) != num_fields:
-        raise TypeError(f'Expected {num_fields} arguments, got {len(result)}')
-    return result
-
-
-
-

Instance variables

-
-
var name : str
-
-

Alias for field number 0

-
-
var onReceive : Callable
-
-

Alias for field number 2

-
-
var protobufFactory : Callable
-
-

Alias for field number 1

-
-
-

Methods

-
-
-def _asdict(self) -
-
-

Return a new dict which maps field names to their values.

-
- -Expand source code - -
def _asdict(self):
-    'Return a new dict which maps field names to their values.'
-    return _dict(_zip(self._fields, self))
-
-
-
-def _replace(self, /, **kwds) -
-
-

Return a new KnownProtocol object replacing specified fields with new values

-
- -Expand source code - -
def _replace(self, /, **kwds):
-    result = self._make(_map(kwds.pop, field_names, self))
-    if kwds:
-        raise ValueError(f'Got unexpected field names: {list(kwds)!r}')
-    return result
-
-
-
-
-
-class ResponseHandler -(callback: Callable) -
-
-

A pending response callback, waiting for a response to one of our messages

-
- -Expand source code - -
class ResponseHandler(NamedTuple):
-    """A pending response callback, waiting for a response to one of our messages"""
-    # requestId: int - used only as a key
-    callback: Callable
-    # FIXME, add timestamp and age out old requests
-
-

Ancestors

-
    -
  • builtins.tuple
  • -
-

Class variables

-
-
var _field_defaults
-
-
-
-
var _fields
-
-
-
-
-

Static methods

-
-
-def _make(iterable) -
-
-

Make a new ResponseHandler object from a sequence or iterable

-
- -Expand source code - -
@classmethod
-def _make(cls, iterable):
-    result = tuple_new(cls, iterable)
-    if _len(result) != num_fields:
-        raise TypeError(f'Expected {num_fields} arguments, got {len(result)}')
-    return result
-
-
-
-

Instance variables

-
-
var callback : Callable
-
-

Alias for field number 0

-
-
-

Methods

-
-
-def _asdict(self) -
-
-

Return a new dict which maps field names to their values.

-
- -Expand source code - -
def _asdict(self):
-    'Return a new dict which maps field names to their values.'
-    return _dict(_zip(self._fields, self))
-
-
-
-def _replace(self, /, **kwds) -
-
-

Return a new ResponseHandler object replacing specified fields with new values

-
- -Expand source code - -
def _replace(self, /, **kwds):
-    result = self._make(_map(kwds.pop, field_names, self))
-    if kwds:
-        raise ValueError(f'Got unexpected field names: {list(kwds)!r}')
-    return result
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/mesh_interface.html b/docs/meshtastic/mesh_interface.html deleted file mode 100644 index 016c690..0000000 --- a/docs/meshtastic/mesh_interface.html +++ /dev/null @@ -1,2417 +0,0 @@ - - - - - - -meshtastic.mesh_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.mesh_interface

-
-
-

Mesh Interface class

-
- -Expand source code - -
"""Mesh Interface class
-"""
-import sys
-import random
-import time
-import logging
-from typing import AnyStr
-import threading
-from datetime import datetime
-import timeago
-from tabulate import tabulate
-
-import google.protobuf.json_format
-
-from pubsub import pub
-from google.protobuf.json_format import MessageToJson
-
-
-import meshtastic.node
-from . import portnums_pb2, mesh_pb2
-from .util import stripnl, Timeout, our_exit
-from .__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols
-
-class MeshInterface:
-    """Interface class for meshtastic devices
-
-    Properties:
-
-    isConnected
-    nodes
-    debugOut
-    """
-
-    def __init__(self, debugOut=None, noProto=False):
-        """Constructor
-
-        Keyword Arguments:
-            noProto -- If True, don't try to run our protocol on the
-                       link - just be a dumb serial client.
-        """
-        self.debugOut = debugOut
-        self.nodes = None  # FIXME
-        self.isConnected = threading.Event()
-        self.noProto = noProto
-        self.localNode = meshtastic.node.Node(self, -1)  # We fixup nodenum later
-        self.myInfo = None  # We don't have device info yet
-        self.responseHandlers = {}  # A map from request ID to the handler
-        self.failure = None  # If we've encountered a fatal exception it will be kept here
-        self._timeout = Timeout()
-        self.heartbeatTimer = None
-        random.seed()  # FIXME, we should not clobber the random seedval here, instead tell user they must call it
-        self.currentPacketId = random.randint(0, 0xffffffff)
-        self.nodesByNum = None
-        self.configId = None
-        self.defaultHopLimit = 3
-        self.gotResponse = False # used in gpio read
-        self.mask = None # used in gpio read and gpio watch
-
-    def close(self):
-        """Shutdown this interface"""
-        if self.heartbeatTimer:
-            self.heartbeatTimer.cancel()
-
-        self._sendDisconnect()
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        if exc_type is not None and exc_value is not None:
-            logging.error(
-                f'An exception of type {exc_type} with value {exc_value} has occurred')
-        if traceback is not None:
-            logging.error(f'Traceback: {traceback}')
-        self.close()
-
-    def showInfo(self, file=sys.stdout):
-        """Show human readable summary about this object"""
-        owner = f"Owner: {self.getLongName()} ({self.getShortName()})"
-        myinfo = ''
-        if self.myInfo:
-            myinfo = f"\nMy info: {stripnl(MessageToJson(self.myInfo))}"
-        mesh = "\nNodes in mesh:"
-        nodes = ""
-        if self.nodes:
-            for n in self.nodes.values():
-                nodes = nodes + f"  {stripnl(n)}"
-        infos = owner + myinfo + mesh + nodes
-        print(infos)
-        return infos
-
-    def showNodes(self, includeSelf=True, file=sys.stdout):
-        """Show table summary of nodes in mesh"""
-        def formatFloat(value, precision=2, unit=''):
-            """Format a float value with precsion."""
-            return f'{value:.{precision}f}{unit}' if value else None
-
-        def getLH(ts):
-            """Format last heard"""
-            return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
-
-        def getTimeAgo(ts):
-            """Format how long ago have we heard from this node (aka timeago)."""
-            return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
-
-        rows = []
-        if self.nodes:
-            logging.debug(f'self.nodes:{self.nodes}')
-            for node in self.nodes.values():
-                if not includeSelf and node['num'] == self.localNode.nodeNum:
-                    continue
-
-                row = {"N": 0}
-
-                user = node.get('user')
-                if user:
-                    row.update({
-                        "User": user['longName'],
-                        "AKA":  user['shortName'],
-                        "ID":   user['id'],
-                    })
-
-                pos = node.get('position')
-                if pos:
-                    row.update({
-                        "Latitude":  formatFloat(pos.get("latitude"),     4, "°"),
-                        "Longitude": formatFloat(pos.get("longitude"),    4, "°"),
-                        "Altitude":  formatFloat(pos.get("altitude"),     0, " m"),
-                        "Battery":   formatFloat(pos.get("batteryLevel"), 2, "%"),
-                    })
-
-                row.update({
-                    "SNR":       formatFloat(node.get("snr"), 2, " dB"),
-                    "LastHeard": getLH(node.get("lastHeard")),
-                    "Since":     getTimeAgo(node.get("lastHeard")),
-                })
-
-                rows.append(row)
-
-        rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
-        for i, row in enumerate(rows):
-            row['N'] = i+1
-
-        table = tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid')
-        print(table)
-        return table
-
-
-    def getNode(self, nodeId):
-        """Return a node object which contains device settings and channel info"""
-        if nodeId == LOCAL_ADDR:
-            return self.localNode
-        else:
-            n = meshtastic.node.Node(self, nodeId)
-            logging.debug("About to requestConfig")
-            n.requestConfig()
-            if not n.waitForConfig():
-                our_exit("Error: Timed out waiting for node config")
-            return n
-
-    def sendText(self, text: AnyStr,
-                 destinationId=BROADCAST_ADDR,
-                 wantAck=False,
-                 wantResponse=False,
-                 hopLimit=None,
-                 onResponse=None,
-                 channelIndex=0):
-        """Send a utf8 string to some other node, if the node has a display it
-           will also be shown on the device.
-
-        Arguments:
-            text {string} -- The text to send
-
-        Keyword Arguments:
-            destinationId {nodeId or nodeNum} -- where to send this
-                                                 message (default: {BROADCAST_ADDR})
-            portNum -- the application portnum (similar to IP port numbers)
-                       of the destination, see portnums.proto for a list
-            wantAck -- True if you want the message sent in a reliable manner
-                       (with retries and ack/nak provided for delivery)
-            wantResponse -- True if you want the service on the other side to
-                            send an application layer response
-
-        Returns the sent packet. The id field will be populated in this packet
-        and can be used to track future message acks/naks.
-        """
-        if hopLimit is None:
-            hopLimit = self.defaultHopLimit
-
-        return self.sendData(text.encode("utf-8"), destinationId,
-                             portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
-                             wantAck=wantAck,
-                             wantResponse=wantResponse,
-                             hopLimit=hopLimit,
-                             onResponse=onResponse,
-                             channelIndex=channelIndex)
-
-    def sendData(self, data, destinationId=BROADCAST_ADDR,
-                 portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False,
-                 wantResponse=False,
-                 hopLimit=None,
-                 onResponse=None,
-                 channelIndex=0):
-        """Send a data packet to some other node
-
-        Keyword Arguments:
-            data -- the data to send, either as an array of bytes or
-                    as a protobuf (which will be automatically
-                    serialized to bytes)
-            destinationId {nodeId or nodeNum} -- where to send this
-                    message (default: {BROADCAST_ADDR})
-            portNum -- the application portnum (similar to IP port numbers)
-                    of the destination, see portnums.proto for a list
-            wantAck -- True if you want the message sent in a reliable
-                    manner (with retries and ack/nak provided for delivery)
-            wantResponse -- True if you want the service on the other
-                    side to send an application layer response
-            onResponse -- A closure of the form funct(packet), that will be
-                    called when a response packet arrives (or the transaction
-                    is NAKed due to non receipt)
-            channelIndex - channel number to use
-
-        Returns the sent packet. The id field will be populated in this packet
-        and can be used to track future message acks/naks.
-        """
-        if hopLimit is None:
-            hopLimit = self.defaultHopLimit
-
-        if getattr(data, "SerializeToString", None):
-            logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
-            data = data.SerializeToString()
-
-        logging.debug(f"len(data): {len(data)}")
-        logging.debug(f"mesh_pb2.Constants.DATA_PAYLOAD_LEN: {mesh_pb2.Constants.DATA_PAYLOAD_LEN}")
-        if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
-            raise Exception("Data payload too big")
-
-        if portNum == portnums_pb2.PortNum.UNKNOWN_APP:  # we are now more strict wrt port numbers
-            our_exit("Warning: A non-zero port number must be specified")
-
-        meshPacket = mesh_pb2.MeshPacket()
-        meshPacket.channel = channelIndex
-        meshPacket.decoded.payload = data
-        meshPacket.decoded.portnum = portNum
-        meshPacket.decoded.want_response = wantResponse
-
-        p = self._sendPacket(meshPacket, destinationId,
-                             wantAck=wantAck, hopLimit=hopLimit)
-        if onResponse is not None:
-            self._addResponseHandler(p.id, onResponse)
-        return p
-
-    def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
-                     destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
-        """
-        Send a position packet to some other node (normally a broadcast)
-
-        Also, the device software will notice this packet and use it to automatically
-        set its notion of the local position.
-
-        If timeSec is not specified (recommended), we will use the local machine time.
-
-        Returns the sent packet. The id field will be populated in this packet and
-        can be used to track future message acks/naks.
-        """
-        p = mesh_pb2.Position()
-        if latitude != 0.0:
-            p.latitude_i = int(latitude / 1e-7)
-            logging.debug(f'p.latitude_i:{p.latitude_i}')
-
-        if longitude != 0.0:
-            p.longitude_i = int(longitude / 1e-7)
-            logging.debug(f'p.longitude_i:{p.longitude_i}')
-
-        if altitude != 0:
-            p.altitude = int(altitude)
-            logging.debug(f'p.altitude:{p.altitude}')
-
-        if timeSec == 0:
-            timeSec = time.time()  # returns unix timestamp in seconds
-        p.time = int(timeSec)
-        logging.debug(f'p.time:{p.time}')
-
-        return self.sendData(p, destinationId,
-                             portNum=portnums_pb2.PortNum.POSITION_APP,
-                             wantAck=wantAck,
-                             wantResponse=wantResponse)
-
-    def _addResponseHandler(self, requestId, callback):
-        self.responseHandlers[requestId] = ResponseHandler(callback)
-
-    def _sendPacket(self, meshPacket,
-                    destinationId=BROADCAST_ADDR,
-                    wantAck=False, hopLimit=None):
-        """Send a MeshPacket to the specified node (or if unspecified, broadcast).
-        You probably don't want this - use sendData instead.
-
-        Returns the sent packet. The id field will be populated in this packet and
-        can be used to track future message acks/naks.
-        """
-        if hopLimit is None:
-            hopLimit = self.defaultHopLimit
-
-        # We allow users to talk to the local node before we've completed the full connection flow...
-        if(self.myInfo is not None and destinationId != self.myInfo.my_node_num):
-            self._waitConnected()
-
-        toRadio = mesh_pb2.ToRadio()
-
-        nodeNum = 0
-        if destinationId is None:
-            our_exit("Warning: destinationId must not be None")
-        elif isinstance(destinationId, int):
-            nodeNum = destinationId
-        elif destinationId == BROADCAST_ADDR:
-            nodeNum = BROADCAST_NUM
-        elif destinationId == LOCAL_ADDR:
-            if self.myInfo:
-                nodeNum = self.myInfo.my_node_num
-            else:
-                our_exit("Warning: No myInfo found.")
-        # A simple hex style nodeid - we can parse this without needing the DB
-        elif destinationId.startswith("!"):
-            nodeNum = int(destinationId[1:], 16)
-        else:
-            if self.nodes:
-                node = self.nodes.get(destinationId)
-                if not node:
-                    our_exit(f"Warning: NodeId {destinationId} not found in DB")
-                nodeNum = node['num']
-            else:
-                logging.warning("Warning: There were no self.nodes.")
-
-        meshPacket.to = nodeNum
-        meshPacket.want_ack = wantAck
-        meshPacket.hop_limit = hopLimit
-
-        # if the user hasn't set an ID for this packet (likely and recommended),
-        # we should pick a new unique ID so the message can be tracked.
-        if meshPacket.id == 0:
-            meshPacket.id = self._generatePacketId()
-
-        toRadio.packet.CopyFrom(meshPacket)
-        if self.noProto:
-            logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-        else:
-            logging.debug(f"Sending packet: {stripnl(meshPacket)}")
-            self._sendToRadio(toRadio)
-        return meshPacket
-
-    def waitForConfig(self):
-        """Block until radio config is received. Returns True if config has been received."""
-        success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
-        if not success:
-            raise Exception("Timed out waiting for interface config")
-
-    def getMyNodeInfo(self):
-        """Get info about my node."""
-        if self.myInfo is None:
-            return None
-        logging.debug(f'self.nodesByNum:{self.nodesByNum}')
-        return self.nodesByNum.get(self.myInfo.my_node_num)
-
-    def getMyUser(self):
-        """Get user"""
-        nodeInfo = self.getMyNodeInfo()
-        if nodeInfo is not None:
-            return nodeInfo.get('user')
-        return None
-
-    def getLongName(self):
-        """Get long name"""
-        user = self.getMyUser()
-        if user is not None:
-            return user.get('longName', None)
-        return None
-
-    def getShortName(self):
-        """Get short name"""
-        user = self.getMyUser()
-        if user is not None:
-            return user.get('shortName', None)
-        return None
-
-    def _waitConnected(self):
-        """Block until the initial node db download is complete, or timeout
-        and raise an exception"""
-        if not self.noProto:
-            if not self.isConnected.wait(15.0):  # timeout after x seconds
-                raise Exception("Timed out waiting for connection completion")
-
-        # If we failed while connecting, raise the connection to the client
-        if self.failure:
-            raise self.failure
-
-    def _generatePacketId(self):
-        """Get a new unique packet ID"""
-        if self.currentPacketId is None:
-            raise Exception("Not connected yet, can not generate packet")
-        else:
-            self.currentPacketId = (self.currentPacketId + 1) & 0xffffffff
-            return self.currentPacketId
-
-    def _disconnected(self):
-        """Called by subclasses to tell clients this interface has disconnected"""
-        self.isConnected.clear()
-        publishingThread.queueWork(lambda: pub.sendMessage(
-            "meshtastic.connection.lost", interface=self))
-
-    def _startHeartbeat(self):
-        """We need to send a heartbeat message to the device every X seconds"""
-        def callback():
-            self.heartbeatTimer = None
-            prefs = self.localNode.radioConfig.preferences
-            i = prefs.phone_timeout_secs / 2
-            logging.debug(f"Sending heartbeat, interval {i}")
-            if i != 0:
-                self.heartbeatTimer = threading.Timer(i, callback)
-                self.heartbeatTimer.start()
-                p = mesh_pb2.ToRadio()
-                self._sendToRadio(p)
-
-        callback()  # run our periodic callback now, it will make another timer if necessary
-
-    def _connected(self):
-        """Called by this class to tell clients we are now fully connected to a node
-        """
-        # (because I'm lazy) _connected might be called when remote Node
-        # objects complete their config reads, don't generate redundant isConnected
-        # for the local interface
-        if not self.isConnected.is_set():
-            self.isConnected.set()
-            self._startHeartbeat()
-            publishingThread.queueWork(lambda: pub.sendMessage(
-                "meshtastic.connection.established", interface=self))
-
-    def _startConfig(self):
-        """Start device packets flowing"""
-        self.myInfo = None
-        self.nodes = {}  # nodes keyed by ID
-        self.nodesByNum = {}  # nodes keyed by nodenum
-
-        startConfig = mesh_pb2.ToRadio()
-        self.configId = random.randint(0, 0xffffffff)
-        startConfig.want_config_id = self.configId
-        self._sendToRadio(startConfig)
-
-    def _sendDisconnect(self):
-        """Tell device we are done using it"""
-        m = mesh_pb2.ToRadio()
-        m.disconnect = True
-        self._sendToRadio(m)
-
-    def _sendToRadio(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        if self.noProto:
-            logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-        else:
-            #logging.debug(f"Sending toRadio: {stripnl(toRadio)}")
-            self._sendToRadioImpl(toRadio)
-
-    def _sendToRadioImpl(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        logging.error(f"Subclass must provide toradio: {toRadio}")
-
-    def _handleConfigComplete(self):
-        """
-        Done with initial config messages, now send regular MeshPackets
-        to ask for settings and channels
-        """
-        self.localNode.requestConfig()
-
-    def _handleFromRadio(self, fromRadioBytes):
-        """
-        Handle a packet that arrived from the radio(update model and publish events)
-
-        Called by subclasses."""
-        fromRadio = mesh_pb2.FromRadio()
-        fromRadio.ParseFromString(fromRadioBytes)
-        logging.debug(f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}")
-        asDict = google.protobuf.json_format.MessageToDict(fromRadio)
-        logging.debug(f"Received from radio: {fromRadio}")
-        if fromRadio.HasField("my_info"):
-            self.myInfo = fromRadio.my_info
-            self.localNode.nodeNum = self.myInfo.my_node_num
-            logging.debug(f"Received myinfo: {stripnl(fromRadio.my_info)}")
-
-            failmsg = None
-            # Check for app too old
-            if self.myInfo.min_app_version > OUR_APP_VERSION:
-                failmsg = "This device needs a newer python client, run 'pip install --upgrade meshtastic'."\
-                          "For more information see https://tinyurl.com/5bjsxu32"
-
-            # check for firmware too old
-            if self.myInfo.max_channels == 0:
-                failmsg = "This version of meshtastic-python requires device firmware version 1.2 or later. "\
-                          "For more information see https://tinyurl.com/5bjsxu32"
-
-            if failmsg:
-                self.failure = Exception(failmsg)
-                self.isConnected.set()  # let waitConnected return this exception
-                self.close()
-
-        elif fromRadio.HasField("node_info"):
-            node = asDict["nodeInfo"]
-            try:
-                self._fixupPosition(node["position"])
-            except:
-                logging.debug("Node without position")
-
-            logging.debug(f"Received nodeinfo: {node}")
-
-            self.nodesByNum[node["num"]] = node
-            if "user" in node:  # Some nodes might not have user/ids assigned yet
-                if "id" in node["user"]:
-                    self.nodes[node["user"]["id"]] = node
-            publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.node.updated",
-                                                               node=node, interface=self))
-        elif fromRadio.config_complete_id == self.configId:
-            # we ignore the config_complete_id, it is unneeded for our
-            # stream API fromRadio.config_complete_id
-            logging.debug(f"Config complete ID {self.configId}")
-            self._handleConfigComplete()
-        elif fromRadio.HasField("packet"):
-            self._handlePacketFromRadio(fromRadio.packet)
-        elif fromRadio.rebooted:
-            # Tell clients the device went away.  Careful not to call the overridden
-            # subclass version that closes the serial port
-            MeshInterface._disconnected(self)
-
-            self._startConfig()  # redownload the node db etc...
-        else:
-            logging.debug("Unexpected FromRadio payload")
-
-    def _fixupPosition(self, position):
-        """Convert integer lat/lon into floats
-
-        Arguments:
-            position {Position dictionary} -- object ot fix up
-        """
-        if "latitudeI" in position:
-            position["latitude"] = position["latitudeI"] * 1e-7
-        if "longitudeI" in position:
-            position["longitude"] = position["longitudeI"] * 1e-7
-
-    def _nodeNumToId(self, num):
-        """Map a node node number to a node ID
-
-        Arguments:
-            num {int} -- Node number
-
-        Returns:
-            string -- Node ID
-        """
-        if num == BROADCAST_NUM:
-            return BROADCAST_ADDR
-
-        try:
-            return self.nodesByNum[num]["user"]["id"]
-        except:
-            logging.debug(f"Node {num} not found for fromId")
-            return None
-
-    def _getOrCreateByNum(self, nodeNum):
-        """Given a nodenum find the NodeInfo in the DB (or create if necessary)"""
-        if nodeNum == BROADCAST_NUM:
-            raise Exception("Can not create/find nodenum by the broadcast num")
-
-        if nodeNum in self.nodesByNum:
-            return self.nodesByNum[nodeNum]
-        else:
-            n = {"num": nodeNum}  # Create a minimial node db entry
-            self.nodesByNum[nodeNum] = n
-            return n
-
-    def _handlePacketFromRadio(self, meshPacket, hack=False):
-        """Handle a MeshPacket that just arrived from the radio
-
-        hack - well, since we used 'from', which is a python keyword,
-               as an attribute to MeshPacket in protobufs,
-               there really is no way to do something like this:
-                    meshPacket = mesh_pb2.MeshPacket()
-                    meshPacket.from = 123
-               If hack is True, we can unit test this code.
-
-        Will publish one of the following events:
-        - meshtastic.receive.text(packet = MeshPacket dictionary)
-        - meshtastic.receive.position(packet = MeshPacket dictionary)
-        - meshtastic.receive.user(packet = MeshPacket dictionary)
-        - meshtastic.receive.data(packet = MeshPacket dictionary)
-        """
-        asDict = google.protobuf.json_format.MessageToDict(meshPacket)
-
-        # We normally decompose the payload into a dictionary so that the client
-        # doesn't need to understand protobufs.  But advanced clients might
-        # want the raw protobuf, so we provide it in "raw"
-        asDict["raw"] = meshPacket
-
-        # from might be missing if the nodenum was zero.
-        if not hack and "from" not in asDict:
-            asDict["from"] = 0
-            logging.error(f"Device returned a packet we sent, ignoring: {stripnl(asDict)}")
-            print(f"Error: Device returned a packet we sent, ignoring: {stripnl(asDict)}")
-            return
-        if "to" not in asDict:
-            asDict["to"] = 0
-
-        # /add fromId and toId fields based on the node ID
-        try:
-            asDict["fromId"] = self._nodeNumToId(asDict["from"])
-        except Exception as ex:
-            logging.warning(f"Not populating fromId {ex}")
-        try:
-            asDict["toId"] = self._nodeNumToId(asDict["to"])
-        except Exception as ex:
-            logging.warning(f"Not populating toId {ex}")
-
-        # We could provide our objects as DotMaps - which work with . notation or as dictionaries
-        # asObj = DotMap(asDict)
-        topic = "meshtastic.receive"  # Generic unknown packet type
-
-        decoded = asDict["decoded"]
-        # The default MessageToDict converts byte arrays into base64 strings.
-        # We don't want that - it messes up data payload.  So slam in the correct
-        # byte array.
-        decoded["payload"] = meshPacket.decoded.payload
-
-        # UNKNOWN_APP is the default protobuf portnum value, and therefore if not
-        # set it will not be populated at all to make API usage easier, set
-        # it to prevent confusion
-        if "portnum" not in decoded:
-            new_portnum = portnums_pb2.PortNum.Name(portnums_pb2.PortNum.UNKNOWN_APP)
-            decoded["portnum"] = new_portnum
-            logging.warning(f"portnum was not in decoded. Setting to:{new_portnum}")
-
-        portnum = decoded["portnum"]
-
-        topic = f"meshtastic.receive.data.{portnum}"
-
-        # decode position protobufs and update nodedb, provide decoded version
-        # as "position" in the published msg move the following into a 'decoders'
-        # API that clients could register?
-        portNumInt = meshPacket.decoded.portnum  # we want portnum as an int
-        handler = protocols.get(portNumInt)
-        # The decoded protobuf as a dictionary (if we understand this message)
-        p = None
-        if handler is not None:
-            topic = f"meshtastic.receive.{handler.name}"
-
-            # Convert to protobuf if possible
-            if handler.protobufFactory is not None:
-                pb = handler.protobufFactory()
-                pb.ParseFromString(meshPacket.decoded.payload)
-                p = google.protobuf.json_format.MessageToDict(pb)
-                asDict["decoded"][handler.name] = p
-                # Also provide the protobuf raw
-                asDict["decoded"][handler.name]["raw"] = pb
-
-            # Call specialized onReceive if necessary
-            if handler.onReceive is not None:
-                handler.onReceive(self, asDict)
-
-        # Is this message in response to a request, if so, look for a handler
-        requestId = decoded.get("requestId")
-        if requestId is not None:
-            # We ignore ACK packets, but send NAKs and data responses to the handlers
-            routing = decoded.get("routing")
-            isAck = routing is not None and ("errorReason" not in routing)
-            if not isAck:
-                # we keep the responseHandler in dict until we get a non ack
-                handler = self.responseHandlers.pop(requestId, None)
-                if handler is not None:
-                    handler.callback(asDict)
-
-        logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ")
-        publishingThread.queueWork(lambda: pub.sendMessage(
-            topic, packet=asDict, interface=self))
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class MeshInterface -(debugOut=None, noProto=False) -
-
-

Interface class for meshtastic devices

-

Properties:

-

isConnected -nodes -debugOut

-

Constructor

-

Keyword Arguments: -noProto – If True, don't try to run our protocol on the -link - just be a dumb serial client.

-
- -Expand source code - -
class MeshInterface:
-    """Interface class for meshtastic devices
-
-    Properties:
-
-    isConnected
-    nodes
-    debugOut
-    """
-
-    def __init__(self, debugOut=None, noProto=False):
-        """Constructor
-
-        Keyword Arguments:
-            noProto -- If True, don't try to run our protocol on the
-                       link - just be a dumb serial client.
-        """
-        self.debugOut = debugOut
-        self.nodes = None  # FIXME
-        self.isConnected = threading.Event()
-        self.noProto = noProto
-        self.localNode = meshtastic.node.Node(self, -1)  # We fixup nodenum later
-        self.myInfo = None  # We don't have device info yet
-        self.responseHandlers = {}  # A map from request ID to the handler
-        self.failure = None  # If we've encountered a fatal exception it will be kept here
-        self._timeout = Timeout()
-        self.heartbeatTimer = None
-        random.seed()  # FIXME, we should not clobber the random seedval here, instead tell user they must call it
-        self.currentPacketId = random.randint(0, 0xffffffff)
-        self.nodesByNum = None
-        self.configId = None
-        self.defaultHopLimit = 3
-        self.gotResponse = False # used in gpio read
-        self.mask = None # used in gpio read and gpio watch
-
-    def close(self):
-        """Shutdown this interface"""
-        if self.heartbeatTimer:
-            self.heartbeatTimer.cancel()
-
-        self._sendDisconnect()
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        if exc_type is not None and exc_value is not None:
-            logging.error(
-                f'An exception of type {exc_type} with value {exc_value} has occurred')
-        if traceback is not None:
-            logging.error(f'Traceback: {traceback}')
-        self.close()
-
-    def showInfo(self, file=sys.stdout):
-        """Show human readable summary about this object"""
-        owner = f"Owner: {self.getLongName()} ({self.getShortName()})"
-        myinfo = ''
-        if self.myInfo:
-            myinfo = f"\nMy info: {stripnl(MessageToJson(self.myInfo))}"
-        mesh = "\nNodes in mesh:"
-        nodes = ""
-        if self.nodes:
-            for n in self.nodes.values():
-                nodes = nodes + f"  {stripnl(n)}"
-        infos = owner + myinfo + mesh + nodes
-        print(infos)
-        return infos
-
-    def showNodes(self, includeSelf=True, file=sys.stdout):
-        """Show table summary of nodes in mesh"""
-        def formatFloat(value, precision=2, unit=''):
-            """Format a float value with precsion."""
-            return f'{value:.{precision}f}{unit}' if value else None
-
-        def getLH(ts):
-            """Format last heard"""
-            return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
-
-        def getTimeAgo(ts):
-            """Format how long ago have we heard from this node (aka timeago)."""
-            return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
-
-        rows = []
-        if self.nodes:
-            logging.debug(f'self.nodes:{self.nodes}')
-            for node in self.nodes.values():
-                if not includeSelf and node['num'] == self.localNode.nodeNum:
-                    continue
-
-                row = {"N": 0}
-
-                user = node.get('user')
-                if user:
-                    row.update({
-                        "User": user['longName'],
-                        "AKA":  user['shortName'],
-                        "ID":   user['id'],
-                    })
-
-                pos = node.get('position')
-                if pos:
-                    row.update({
-                        "Latitude":  formatFloat(pos.get("latitude"),     4, "°"),
-                        "Longitude": formatFloat(pos.get("longitude"),    4, "°"),
-                        "Altitude":  formatFloat(pos.get("altitude"),     0, " m"),
-                        "Battery":   formatFloat(pos.get("batteryLevel"), 2, "%"),
-                    })
-
-                row.update({
-                    "SNR":       formatFloat(node.get("snr"), 2, " dB"),
-                    "LastHeard": getLH(node.get("lastHeard")),
-                    "Since":     getTimeAgo(node.get("lastHeard")),
-                })
-
-                rows.append(row)
-
-        rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
-        for i, row in enumerate(rows):
-            row['N'] = i+1
-
-        table = tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid')
-        print(table)
-        return table
-
-
-    def getNode(self, nodeId):
-        """Return a node object which contains device settings and channel info"""
-        if nodeId == LOCAL_ADDR:
-            return self.localNode
-        else:
-            n = meshtastic.node.Node(self, nodeId)
-            logging.debug("About to requestConfig")
-            n.requestConfig()
-            if not n.waitForConfig():
-                our_exit("Error: Timed out waiting for node config")
-            return n
-
-    def sendText(self, text: AnyStr,
-                 destinationId=BROADCAST_ADDR,
-                 wantAck=False,
-                 wantResponse=False,
-                 hopLimit=None,
-                 onResponse=None,
-                 channelIndex=0):
-        """Send a utf8 string to some other node, if the node has a display it
-           will also be shown on the device.
-
-        Arguments:
-            text {string} -- The text to send
-
-        Keyword Arguments:
-            destinationId {nodeId or nodeNum} -- where to send this
-                                                 message (default: {BROADCAST_ADDR})
-            portNum -- the application portnum (similar to IP port numbers)
-                       of the destination, see portnums.proto for a list
-            wantAck -- True if you want the message sent in a reliable manner
-                       (with retries and ack/nak provided for delivery)
-            wantResponse -- True if you want the service on the other side to
-                            send an application layer response
-
-        Returns the sent packet. The id field will be populated in this packet
-        and can be used to track future message acks/naks.
-        """
-        if hopLimit is None:
-            hopLimit = self.defaultHopLimit
-
-        return self.sendData(text.encode("utf-8"), destinationId,
-                             portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
-                             wantAck=wantAck,
-                             wantResponse=wantResponse,
-                             hopLimit=hopLimit,
-                             onResponse=onResponse,
-                             channelIndex=channelIndex)
-
-    def sendData(self, data, destinationId=BROADCAST_ADDR,
-                 portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False,
-                 wantResponse=False,
-                 hopLimit=None,
-                 onResponse=None,
-                 channelIndex=0):
-        """Send a data packet to some other node
-
-        Keyword Arguments:
-            data -- the data to send, either as an array of bytes or
-                    as a protobuf (which will be automatically
-                    serialized to bytes)
-            destinationId {nodeId or nodeNum} -- where to send this
-                    message (default: {BROADCAST_ADDR})
-            portNum -- the application portnum (similar to IP port numbers)
-                    of the destination, see portnums.proto for a list
-            wantAck -- True if you want the message sent in a reliable
-                    manner (with retries and ack/nak provided for delivery)
-            wantResponse -- True if you want the service on the other
-                    side to send an application layer response
-            onResponse -- A closure of the form funct(packet), that will be
-                    called when a response packet arrives (or the transaction
-                    is NAKed due to non receipt)
-            channelIndex - channel number to use
-
-        Returns the sent packet. The id field will be populated in this packet
-        and can be used to track future message acks/naks.
-        """
-        if hopLimit is None:
-            hopLimit = self.defaultHopLimit
-
-        if getattr(data, "SerializeToString", None):
-            logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
-            data = data.SerializeToString()
-
-        logging.debug(f"len(data): {len(data)}")
-        logging.debug(f"mesh_pb2.Constants.DATA_PAYLOAD_LEN: {mesh_pb2.Constants.DATA_PAYLOAD_LEN}")
-        if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
-            raise Exception("Data payload too big")
-
-        if portNum == portnums_pb2.PortNum.UNKNOWN_APP:  # we are now more strict wrt port numbers
-            our_exit("Warning: A non-zero port number must be specified")
-
-        meshPacket = mesh_pb2.MeshPacket()
-        meshPacket.channel = channelIndex
-        meshPacket.decoded.payload = data
-        meshPacket.decoded.portnum = portNum
-        meshPacket.decoded.want_response = wantResponse
-
-        p = self._sendPacket(meshPacket, destinationId,
-                             wantAck=wantAck, hopLimit=hopLimit)
-        if onResponse is not None:
-            self._addResponseHandler(p.id, onResponse)
-        return p
-
-    def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
-                     destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
-        """
-        Send a position packet to some other node (normally a broadcast)
-
-        Also, the device software will notice this packet and use it to automatically
-        set its notion of the local position.
-
-        If timeSec is not specified (recommended), we will use the local machine time.
-
-        Returns the sent packet. The id field will be populated in this packet and
-        can be used to track future message acks/naks.
-        """
-        p = mesh_pb2.Position()
-        if latitude != 0.0:
-            p.latitude_i = int(latitude / 1e-7)
-            logging.debug(f'p.latitude_i:{p.latitude_i}')
-
-        if longitude != 0.0:
-            p.longitude_i = int(longitude / 1e-7)
-            logging.debug(f'p.longitude_i:{p.longitude_i}')
-
-        if altitude != 0:
-            p.altitude = int(altitude)
-            logging.debug(f'p.altitude:{p.altitude}')
-
-        if timeSec == 0:
-            timeSec = time.time()  # returns unix timestamp in seconds
-        p.time = int(timeSec)
-        logging.debug(f'p.time:{p.time}')
-
-        return self.sendData(p, destinationId,
-                             portNum=portnums_pb2.PortNum.POSITION_APP,
-                             wantAck=wantAck,
-                             wantResponse=wantResponse)
-
-    def _addResponseHandler(self, requestId, callback):
-        self.responseHandlers[requestId] = ResponseHandler(callback)
-
-    def _sendPacket(self, meshPacket,
-                    destinationId=BROADCAST_ADDR,
-                    wantAck=False, hopLimit=None):
-        """Send a MeshPacket to the specified node (or if unspecified, broadcast).
-        You probably don't want this - use sendData instead.
-
-        Returns the sent packet. The id field will be populated in this packet and
-        can be used to track future message acks/naks.
-        """
-        if hopLimit is None:
-            hopLimit = self.defaultHopLimit
-
-        # We allow users to talk to the local node before we've completed the full connection flow...
-        if(self.myInfo is not None and destinationId != self.myInfo.my_node_num):
-            self._waitConnected()
-
-        toRadio = mesh_pb2.ToRadio()
-
-        nodeNum = 0
-        if destinationId is None:
-            our_exit("Warning: destinationId must not be None")
-        elif isinstance(destinationId, int):
-            nodeNum = destinationId
-        elif destinationId == BROADCAST_ADDR:
-            nodeNum = BROADCAST_NUM
-        elif destinationId == LOCAL_ADDR:
-            if self.myInfo:
-                nodeNum = self.myInfo.my_node_num
-            else:
-                our_exit("Warning: No myInfo found.")
-        # A simple hex style nodeid - we can parse this without needing the DB
-        elif destinationId.startswith("!"):
-            nodeNum = int(destinationId[1:], 16)
-        else:
-            if self.nodes:
-                node = self.nodes.get(destinationId)
-                if not node:
-                    our_exit(f"Warning: NodeId {destinationId} not found in DB")
-                nodeNum = node['num']
-            else:
-                logging.warning("Warning: There were no self.nodes.")
-
-        meshPacket.to = nodeNum
-        meshPacket.want_ack = wantAck
-        meshPacket.hop_limit = hopLimit
-
-        # if the user hasn't set an ID for this packet (likely and recommended),
-        # we should pick a new unique ID so the message can be tracked.
-        if meshPacket.id == 0:
-            meshPacket.id = self._generatePacketId()
-
-        toRadio.packet.CopyFrom(meshPacket)
-        if self.noProto:
-            logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-        else:
-            logging.debug(f"Sending packet: {stripnl(meshPacket)}")
-            self._sendToRadio(toRadio)
-        return meshPacket
-
-    def waitForConfig(self):
-        """Block until radio config is received. Returns True if config has been received."""
-        success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
-        if not success:
-            raise Exception("Timed out waiting for interface config")
-
-    def getMyNodeInfo(self):
-        """Get info about my node."""
-        if self.myInfo is None:
-            return None
-        logging.debug(f'self.nodesByNum:{self.nodesByNum}')
-        return self.nodesByNum.get(self.myInfo.my_node_num)
-
-    def getMyUser(self):
-        """Get user"""
-        nodeInfo = self.getMyNodeInfo()
-        if nodeInfo is not None:
-            return nodeInfo.get('user')
-        return None
-
-    def getLongName(self):
-        """Get long name"""
-        user = self.getMyUser()
-        if user is not None:
-            return user.get('longName', None)
-        return None
-
-    def getShortName(self):
-        """Get short name"""
-        user = self.getMyUser()
-        if user is not None:
-            return user.get('shortName', None)
-        return None
-
-    def _waitConnected(self):
-        """Block until the initial node db download is complete, or timeout
-        and raise an exception"""
-        if not self.noProto:
-            if not self.isConnected.wait(15.0):  # timeout after x seconds
-                raise Exception("Timed out waiting for connection completion")
-
-        # If we failed while connecting, raise the connection to the client
-        if self.failure:
-            raise self.failure
-
-    def _generatePacketId(self):
-        """Get a new unique packet ID"""
-        if self.currentPacketId is None:
-            raise Exception("Not connected yet, can not generate packet")
-        else:
-            self.currentPacketId = (self.currentPacketId + 1) & 0xffffffff
-            return self.currentPacketId
-
-    def _disconnected(self):
-        """Called by subclasses to tell clients this interface has disconnected"""
-        self.isConnected.clear()
-        publishingThread.queueWork(lambda: pub.sendMessage(
-            "meshtastic.connection.lost", interface=self))
-
-    def _startHeartbeat(self):
-        """We need to send a heartbeat message to the device every X seconds"""
-        def callback():
-            self.heartbeatTimer = None
-            prefs = self.localNode.radioConfig.preferences
-            i = prefs.phone_timeout_secs / 2
-            logging.debug(f"Sending heartbeat, interval {i}")
-            if i != 0:
-                self.heartbeatTimer = threading.Timer(i, callback)
-                self.heartbeatTimer.start()
-                p = mesh_pb2.ToRadio()
-                self._sendToRadio(p)
-
-        callback()  # run our periodic callback now, it will make another timer if necessary
-
-    def _connected(self):
-        """Called by this class to tell clients we are now fully connected to a node
-        """
-        # (because I'm lazy) _connected might be called when remote Node
-        # objects complete their config reads, don't generate redundant isConnected
-        # for the local interface
-        if not self.isConnected.is_set():
-            self.isConnected.set()
-            self._startHeartbeat()
-            publishingThread.queueWork(lambda: pub.sendMessage(
-                "meshtastic.connection.established", interface=self))
-
-    def _startConfig(self):
-        """Start device packets flowing"""
-        self.myInfo = None
-        self.nodes = {}  # nodes keyed by ID
-        self.nodesByNum = {}  # nodes keyed by nodenum
-
-        startConfig = mesh_pb2.ToRadio()
-        self.configId = random.randint(0, 0xffffffff)
-        startConfig.want_config_id = self.configId
-        self._sendToRadio(startConfig)
-
-    def _sendDisconnect(self):
-        """Tell device we are done using it"""
-        m = mesh_pb2.ToRadio()
-        m.disconnect = True
-        self._sendToRadio(m)
-
-    def _sendToRadio(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        if self.noProto:
-            logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-        else:
-            #logging.debug(f"Sending toRadio: {stripnl(toRadio)}")
-            self._sendToRadioImpl(toRadio)
-
-    def _sendToRadioImpl(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        logging.error(f"Subclass must provide toradio: {toRadio}")
-
-    def _handleConfigComplete(self):
-        """
-        Done with initial config messages, now send regular MeshPackets
-        to ask for settings and channels
-        """
-        self.localNode.requestConfig()
-
-    def _handleFromRadio(self, fromRadioBytes):
-        """
-        Handle a packet that arrived from the radio(update model and publish events)
-
-        Called by subclasses."""
-        fromRadio = mesh_pb2.FromRadio()
-        fromRadio.ParseFromString(fromRadioBytes)
-        logging.debug(f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}")
-        asDict = google.protobuf.json_format.MessageToDict(fromRadio)
-        logging.debug(f"Received from radio: {fromRadio}")
-        if fromRadio.HasField("my_info"):
-            self.myInfo = fromRadio.my_info
-            self.localNode.nodeNum = self.myInfo.my_node_num
-            logging.debug(f"Received myinfo: {stripnl(fromRadio.my_info)}")
-
-            failmsg = None
-            # Check for app too old
-            if self.myInfo.min_app_version > OUR_APP_VERSION:
-                failmsg = "This device needs a newer python client, run 'pip install --upgrade meshtastic'."\
-                          "For more information see https://tinyurl.com/5bjsxu32"
-
-            # check for firmware too old
-            if self.myInfo.max_channels == 0:
-                failmsg = "This version of meshtastic-python requires device firmware version 1.2 or later. "\
-                          "For more information see https://tinyurl.com/5bjsxu32"
-
-            if failmsg:
-                self.failure = Exception(failmsg)
-                self.isConnected.set()  # let waitConnected return this exception
-                self.close()
-
-        elif fromRadio.HasField("node_info"):
-            node = asDict["nodeInfo"]
-            try:
-                self._fixupPosition(node["position"])
-            except:
-                logging.debug("Node without position")
-
-            logging.debug(f"Received nodeinfo: {node}")
-
-            self.nodesByNum[node["num"]] = node
-            if "user" in node:  # Some nodes might not have user/ids assigned yet
-                if "id" in node["user"]:
-                    self.nodes[node["user"]["id"]] = node
-            publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.node.updated",
-                                                               node=node, interface=self))
-        elif fromRadio.config_complete_id == self.configId:
-            # we ignore the config_complete_id, it is unneeded for our
-            # stream API fromRadio.config_complete_id
-            logging.debug(f"Config complete ID {self.configId}")
-            self._handleConfigComplete()
-        elif fromRadio.HasField("packet"):
-            self._handlePacketFromRadio(fromRadio.packet)
-        elif fromRadio.rebooted:
-            # Tell clients the device went away.  Careful not to call the overridden
-            # subclass version that closes the serial port
-            MeshInterface._disconnected(self)
-
-            self._startConfig()  # redownload the node db etc...
-        else:
-            logging.debug("Unexpected FromRadio payload")
-
-    def _fixupPosition(self, position):
-        """Convert integer lat/lon into floats
-
-        Arguments:
-            position {Position dictionary} -- object ot fix up
-        """
-        if "latitudeI" in position:
-            position["latitude"] = position["latitudeI"] * 1e-7
-        if "longitudeI" in position:
-            position["longitude"] = position["longitudeI"] * 1e-7
-
-    def _nodeNumToId(self, num):
-        """Map a node node number to a node ID
-
-        Arguments:
-            num {int} -- Node number
-
-        Returns:
-            string -- Node ID
-        """
-        if num == BROADCAST_NUM:
-            return BROADCAST_ADDR
-
-        try:
-            return self.nodesByNum[num]["user"]["id"]
-        except:
-            logging.debug(f"Node {num} not found for fromId")
-            return None
-
-    def _getOrCreateByNum(self, nodeNum):
-        """Given a nodenum find the NodeInfo in the DB (or create if necessary)"""
-        if nodeNum == BROADCAST_NUM:
-            raise Exception("Can not create/find nodenum by the broadcast num")
-
-        if nodeNum in self.nodesByNum:
-            return self.nodesByNum[nodeNum]
-        else:
-            n = {"num": nodeNum}  # Create a minimial node db entry
-            self.nodesByNum[nodeNum] = n
-            return n
-
-    def _handlePacketFromRadio(self, meshPacket, hack=False):
-        """Handle a MeshPacket that just arrived from the radio
-
-        hack - well, since we used 'from', which is a python keyword,
-               as an attribute to MeshPacket in protobufs,
-               there really is no way to do something like this:
-                    meshPacket = mesh_pb2.MeshPacket()
-                    meshPacket.from = 123
-               If hack is True, we can unit test this code.
-
-        Will publish one of the following events:
-        - meshtastic.receive.text(packet = MeshPacket dictionary)
-        - meshtastic.receive.position(packet = MeshPacket dictionary)
-        - meshtastic.receive.user(packet = MeshPacket dictionary)
-        - meshtastic.receive.data(packet = MeshPacket dictionary)
-        """
-        asDict = google.protobuf.json_format.MessageToDict(meshPacket)
-
-        # We normally decompose the payload into a dictionary so that the client
-        # doesn't need to understand protobufs.  But advanced clients might
-        # want the raw protobuf, so we provide it in "raw"
-        asDict["raw"] = meshPacket
-
-        # from might be missing if the nodenum was zero.
-        if not hack and "from" not in asDict:
-            asDict["from"] = 0
-            logging.error(f"Device returned a packet we sent, ignoring: {stripnl(asDict)}")
-            print(f"Error: Device returned a packet we sent, ignoring: {stripnl(asDict)}")
-            return
-        if "to" not in asDict:
-            asDict["to"] = 0
-
-        # /add fromId and toId fields based on the node ID
-        try:
-            asDict["fromId"] = self._nodeNumToId(asDict["from"])
-        except Exception as ex:
-            logging.warning(f"Not populating fromId {ex}")
-        try:
-            asDict["toId"] = self._nodeNumToId(asDict["to"])
-        except Exception as ex:
-            logging.warning(f"Not populating toId {ex}")
-
-        # We could provide our objects as DotMaps - which work with . notation or as dictionaries
-        # asObj = DotMap(asDict)
-        topic = "meshtastic.receive"  # Generic unknown packet type
-
-        decoded = asDict["decoded"]
-        # The default MessageToDict converts byte arrays into base64 strings.
-        # We don't want that - it messes up data payload.  So slam in the correct
-        # byte array.
-        decoded["payload"] = meshPacket.decoded.payload
-
-        # UNKNOWN_APP is the default protobuf portnum value, and therefore if not
-        # set it will not be populated at all to make API usage easier, set
-        # it to prevent confusion
-        if "portnum" not in decoded:
-            new_portnum = portnums_pb2.PortNum.Name(portnums_pb2.PortNum.UNKNOWN_APP)
-            decoded["portnum"] = new_portnum
-            logging.warning(f"portnum was not in decoded. Setting to:{new_portnum}")
-
-        portnum = decoded["portnum"]
-
-        topic = f"meshtastic.receive.data.{portnum}"
-
-        # decode position protobufs and update nodedb, provide decoded version
-        # as "position" in the published msg move the following into a 'decoders'
-        # API that clients could register?
-        portNumInt = meshPacket.decoded.portnum  # we want portnum as an int
-        handler = protocols.get(portNumInt)
-        # The decoded protobuf as a dictionary (if we understand this message)
-        p = None
-        if handler is not None:
-            topic = f"meshtastic.receive.{handler.name}"
-
-            # Convert to protobuf if possible
-            if handler.protobufFactory is not None:
-                pb = handler.protobufFactory()
-                pb.ParseFromString(meshPacket.decoded.payload)
-                p = google.protobuf.json_format.MessageToDict(pb)
-                asDict["decoded"][handler.name] = p
-                # Also provide the protobuf raw
-                asDict["decoded"][handler.name]["raw"] = pb
-
-            # Call specialized onReceive if necessary
-            if handler.onReceive is not None:
-                handler.onReceive(self, asDict)
-
-        # Is this message in response to a request, if so, look for a handler
-        requestId = decoded.get("requestId")
-        if requestId is not None:
-            # We ignore ACK packets, but send NAKs and data responses to the handlers
-            routing = decoded.get("routing")
-            isAck = routing is not None and ("errorReason" not in routing)
-            if not isAck:
-                # we keep the responseHandler in dict until we get a non ack
-                handler = self.responseHandlers.pop(requestId, None)
-                if handler is not None:
-                    handler.callback(asDict)
-
-        logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ")
-        publishingThread.queueWork(lambda: pub.sendMessage(
-            topic, packet=asDict, interface=self))
-
-

Subclasses

- -

Methods

-
-
-def _addResponseHandler(self, requestId, callback) -
-
-
-
- -Expand source code - -
def _addResponseHandler(self, requestId, callback):
-    self.responseHandlers[requestId] = ResponseHandler(callback)
-
-
-
-def _connected(self) -
-
-

Called by this class to tell clients we are now fully connected to a node

-
- -Expand source code - -
def _connected(self):
-    """Called by this class to tell clients we are now fully connected to a node
-    """
-    # (because I'm lazy) _connected might be called when remote Node
-    # objects complete their config reads, don't generate redundant isConnected
-    # for the local interface
-    if not self.isConnected.is_set():
-        self.isConnected.set()
-        self._startHeartbeat()
-        publishingThread.queueWork(lambda: pub.sendMessage(
-            "meshtastic.connection.established", interface=self))
-
-
-
-def _disconnected(self) -
-
-

Called by subclasses to tell clients this interface has disconnected

-
- -Expand source code - -
def _disconnected(self):
-    """Called by subclasses to tell clients this interface has disconnected"""
-    self.isConnected.clear()
-    publishingThread.queueWork(lambda: pub.sendMessage(
-        "meshtastic.connection.lost", interface=self))
-
-
-
-def _fixupPosition(self, position) -
-
-

Convert integer lat/lon into floats

-

Arguments

-

position {Position dictionary} – object ot fix up

-
- -Expand source code - -
def _fixupPosition(self, position):
-    """Convert integer lat/lon into floats
-
-    Arguments:
-        position {Position dictionary} -- object ot fix up
-    """
-    if "latitudeI" in position:
-        position["latitude"] = position["latitudeI"] * 1e-7
-    if "longitudeI" in position:
-        position["longitude"] = position["longitudeI"] * 1e-7
-
-
-
-def _generatePacketId(self) -
-
-

Get a new unique packet ID

-
- -Expand source code - -
def _generatePacketId(self):
-    """Get a new unique packet ID"""
-    if self.currentPacketId is None:
-        raise Exception("Not connected yet, can not generate packet")
-    else:
-        self.currentPacketId = (self.currentPacketId + 1) & 0xffffffff
-        return self.currentPacketId
-
-
-
-def _getOrCreateByNum(self, nodeNum) -
-
-

Given a nodenum find the NodeInfo in the DB (or create if necessary)

-
- -Expand source code - -
def _getOrCreateByNum(self, nodeNum):
-    """Given a nodenum find the NodeInfo in the DB (or create if necessary)"""
-    if nodeNum == BROADCAST_NUM:
-        raise Exception("Can not create/find nodenum by the broadcast num")
-
-    if nodeNum in self.nodesByNum:
-        return self.nodesByNum[nodeNum]
-    else:
-        n = {"num": nodeNum}  # Create a minimial node db entry
-        self.nodesByNum[nodeNum] = n
-        return n
-
-
-
-def _handleConfigComplete(self) -
-
-

Done with initial config messages, now send regular MeshPackets -to ask for settings and channels

-
- -Expand source code - -
def _handleConfigComplete(self):
-    """
-    Done with initial config messages, now send regular MeshPackets
-    to ask for settings and channels
-    """
-    self.localNode.requestConfig()
-
-
-
-def _handleFromRadio(self, fromRadioBytes) -
-
-

Handle a packet that arrived from the radio(update model and publish events)

-

Called by subclasses.

-
- -Expand source code - -
def _handleFromRadio(self, fromRadioBytes):
-    """
-    Handle a packet that arrived from the radio(update model and publish events)
-
-    Called by subclasses."""
-    fromRadio = mesh_pb2.FromRadio()
-    fromRadio.ParseFromString(fromRadioBytes)
-    logging.debug(f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}")
-    asDict = google.protobuf.json_format.MessageToDict(fromRadio)
-    logging.debug(f"Received from radio: {fromRadio}")
-    if fromRadio.HasField("my_info"):
-        self.myInfo = fromRadio.my_info
-        self.localNode.nodeNum = self.myInfo.my_node_num
-        logging.debug(f"Received myinfo: {stripnl(fromRadio.my_info)}")
-
-        failmsg = None
-        # Check for app too old
-        if self.myInfo.min_app_version > OUR_APP_VERSION:
-            failmsg = "This device needs a newer python client, run 'pip install --upgrade meshtastic'."\
-                      "For more information see https://tinyurl.com/5bjsxu32"
-
-        # check for firmware too old
-        if self.myInfo.max_channels == 0:
-            failmsg = "This version of meshtastic-python requires device firmware version 1.2 or later. "\
-                      "For more information see https://tinyurl.com/5bjsxu32"
-
-        if failmsg:
-            self.failure = Exception(failmsg)
-            self.isConnected.set()  # let waitConnected return this exception
-            self.close()
-
-    elif fromRadio.HasField("node_info"):
-        node = asDict["nodeInfo"]
-        try:
-            self._fixupPosition(node["position"])
-        except:
-            logging.debug("Node without position")
-
-        logging.debug(f"Received nodeinfo: {node}")
-
-        self.nodesByNum[node["num"]] = node
-        if "user" in node:  # Some nodes might not have user/ids assigned yet
-            if "id" in node["user"]:
-                self.nodes[node["user"]["id"]] = node
-        publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.node.updated",
-                                                           node=node, interface=self))
-    elif fromRadio.config_complete_id == self.configId:
-        # we ignore the config_complete_id, it is unneeded for our
-        # stream API fromRadio.config_complete_id
-        logging.debug(f"Config complete ID {self.configId}")
-        self._handleConfigComplete()
-    elif fromRadio.HasField("packet"):
-        self._handlePacketFromRadio(fromRadio.packet)
-    elif fromRadio.rebooted:
-        # Tell clients the device went away.  Careful not to call the overridden
-        # subclass version that closes the serial port
-        MeshInterface._disconnected(self)
-
-        self._startConfig()  # redownload the node db etc...
-    else:
-        logging.debug("Unexpected FromRadio payload")
-
-
-
-def _handlePacketFromRadio(self, meshPacket, hack=False) -
-
-

Handle a MeshPacket that just arrived from the radio

-

hack - well, since we used 'from', which is a python keyword, -as an attribute to MeshPacket in protobufs, -there really is no way to do something like this: -meshPacket = mesh_pb2.MeshPacket() -meshPacket.from = 123 -If hack is True, we can unit test this code.

-

Will publish one of the following events: -- meshtastic.receive.text(packet = MeshPacket dictionary) -- meshtastic.receive.position(packet = MeshPacket dictionary) -- meshtastic.receive.user(packet = MeshPacket dictionary) -- meshtastic.receive.data(packet = MeshPacket dictionary)

-
- -Expand source code - -
def _handlePacketFromRadio(self, meshPacket, hack=False):
-    """Handle a MeshPacket that just arrived from the radio
-
-    hack - well, since we used 'from', which is a python keyword,
-           as an attribute to MeshPacket in protobufs,
-           there really is no way to do something like this:
-                meshPacket = mesh_pb2.MeshPacket()
-                meshPacket.from = 123
-           If hack is True, we can unit test this code.
-
-    Will publish one of the following events:
-    - meshtastic.receive.text(packet = MeshPacket dictionary)
-    - meshtastic.receive.position(packet = MeshPacket dictionary)
-    - meshtastic.receive.user(packet = MeshPacket dictionary)
-    - meshtastic.receive.data(packet = MeshPacket dictionary)
-    """
-    asDict = google.protobuf.json_format.MessageToDict(meshPacket)
-
-    # We normally decompose the payload into a dictionary so that the client
-    # doesn't need to understand protobufs.  But advanced clients might
-    # want the raw protobuf, so we provide it in "raw"
-    asDict["raw"] = meshPacket
-
-    # from might be missing if the nodenum was zero.
-    if not hack and "from" not in asDict:
-        asDict["from"] = 0
-        logging.error(f"Device returned a packet we sent, ignoring: {stripnl(asDict)}")
-        print(f"Error: Device returned a packet we sent, ignoring: {stripnl(asDict)}")
-        return
-    if "to" not in asDict:
-        asDict["to"] = 0
-
-    # /add fromId and toId fields based on the node ID
-    try:
-        asDict["fromId"] = self._nodeNumToId(asDict["from"])
-    except Exception as ex:
-        logging.warning(f"Not populating fromId {ex}")
-    try:
-        asDict["toId"] = self._nodeNumToId(asDict["to"])
-    except Exception as ex:
-        logging.warning(f"Not populating toId {ex}")
-
-    # We could provide our objects as DotMaps - which work with . notation or as dictionaries
-    # asObj = DotMap(asDict)
-    topic = "meshtastic.receive"  # Generic unknown packet type
-
-    decoded = asDict["decoded"]
-    # The default MessageToDict converts byte arrays into base64 strings.
-    # We don't want that - it messes up data payload.  So slam in the correct
-    # byte array.
-    decoded["payload"] = meshPacket.decoded.payload
-
-    # UNKNOWN_APP is the default protobuf portnum value, and therefore if not
-    # set it will not be populated at all to make API usage easier, set
-    # it to prevent confusion
-    if "portnum" not in decoded:
-        new_portnum = portnums_pb2.PortNum.Name(portnums_pb2.PortNum.UNKNOWN_APP)
-        decoded["portnum"] = new_portnum
-        logging.warning(f"portnum was not in decoded. Setting to:{new_portnum}")
-
-    portnum = decoded["portnum"]
-
-    topic = f"meshtastic.receive.data.{portnum}"
-
-    # decode position protobufs and update nodedb, provide decoded version
-    # as "position" in the published msg move the following into a 'decoders'
-    # API that clients could register?
-    portNumInt = meshPacket.decoded.portnum  # we want portnum as an int
-    handler = protocols.get(portNumInt)
-    # The decoded protobuf as a dictionary (if we understand this message)
-    p = None
-    if handler is not None:
-        topic = f"meshtastic.receive.{handler.name}"
-
-        # Convert to protobuf if possible
-        if handler.protobufFactory is not None:
-            pb = handler.protobufFactory()
-            pb.ParseFromString(meshPacket.decoded.payload)
-            p = google.protobuf.json_format.MessageToDict(pb)
-            asDict["decoded"][handler.name] = p
-            # Also provide the protobuf raw
-            asDict["decoded"][handler.name]["raw"] = pb
-
-        # Call specialized onReceive if necessary
-        if handler.onReceive is not None:
-            handler.onReceive(self, asDict)
-
-    # Is this message in response to a request, if so, look for a handler
-    requestId = decoded.get("requestId")
-    if requestId is not None:
-        # We ignore ACK packets, but send NAKs and data responses to the handlers
-        routing = decoded.get("routing")
-        isAck = routing is not None and ("errorReason" not in routing)
-        if not isAck:
-            # we keep the responseHandler in dict until we get a non ack
-            handler = self.responseHandlers.pop(requestId, None)
-            if handler is not None:
-                handler.callback(asDict)
-
-    logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ")
-    publishingThread.queueWork(lambda: pub.sendMessage(
-        topic, packet=asDict, interface=self))
-
-
-
-def _nodeNumToId(self, num) -
-
-

Map a node node number to a node ID

-

Arguments

-

num {int} – Node number

-

Returns

-

string – Node ID

-
- -Expand source code - -
def _nodeNumToId(self, num):
-    """Map a node node number to a node ID
-
-    Arguments:
-        num {int} -- Node number
-
-    Returns:
-        string -- Node ID
-    """
-    if num == BROADCAST_NUM:
-        return BROADCAST_ADDR
-
-    try:
-        return self.nodesByNum[num]["user"]["id"]
-    except:
-        logging.debug(f"Node {num} not found for fromId")
-        return None
-
-
-
-def _sendDisconnect(self) -
-
-

Tell device we are done using it

-
- -Expand source code - -
def _sendDisconnect(self):
-    """Tell device we are done using it"""
-    m = mesh_pb2.ToRadio()
-    m.disconnect = True
-    self._sendToRadio(m)
-
-
-
-def _sendPacket(self, meshPacket, destinationId='^all', wantAck=False, hopLimit=None) -
-
-

Send a MeshPacket to the specified node (or if unspecified, broadcast). -You probably don't want this - use sendData instead.

-

Returns the sent packet. The id field will be populated in this packet and -can be used to track future message acks/naks.

-
- -Expand source code - -
def _sendPacket(self, meshPacket,
-                destinationId=BROADCAST_ADDR,
-                wantAck=False, hopLimit=None):
-    """Send a MeshPacket to the specified node (or if unspecified, broadcast).
-    You probably don't want this - use sendData instead.
-
-    Returns the sent packet. The id field will be populated in this packet and
-    can be used to track future message acks/naks.
-    """
-    if hopLimit is None:
-        hopLimit = self.defaultHopLimit
-
-    # We allow users to talk to the local node before we've completed the full connection flow...
-    if(self.myInfo is not None and destinationId != self.myInfo.my_node_num):
-        self._waitConnected()
-
-    toRadio = mesh_pb2.ToRadio()
-
-    nodeNum = 0
-    if destinationId is None:
-        our_exit("Warning: destinationId must not be None")
-    elif isinstance(destinationId, int):
-        nodeNum = destinationId
-    elif destinationId == BROADCAST_ADDR:
-        nodeNum = BROADCAST_NUM
-    elif destinationId == LOCAL_ADDR:
-        if self.myInfo:
-            nodeNum = self.myInfo.my_node_num
-        else:
-            our_exit("Warning: No myInfo found.")
-    # A simple hex style nodeid - we can parse this without needing the DB
-    elif destinationId.startswith("!"):
-        nodeNum = int(destinationId[1:], 16)
-    else:
-        if self.nodes:
-            node = self.nodes.get(destinationId)
-            if not node:
-                our_exit(f"Warning: NodeId {destinationId} not found in DB")
-            nodeNum = node['num']
-        else:
-            logging.warning("Warning: There were no self.nodes.")
-
-    meshPacket.to = nodeNum
-    meshPacket.want_ack = wantAck
-    meshPacket.hop_limit = hopLimit
-
-    # if the user hasn't set an ID for this packet (likely and recommended),
-    # we should pick a new unique ID so the message can be tracked.
-    if meshPacket.id == 0:
-        meshPacket.id = self._generatePacketId()
-
-    toRadio.packet.CopyFrom(meshPacket)
-    if self.noProto:
-        logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-    else:
-        logging.debug(f"Sending packet: {stripnl(meshPacket)}")
-        self._sendToRadio(toRadio)
-    return meshPacket
-
-
-
-def _sendToRadio(self, toRadio) -
-
-

Send a ToRadio protobuf to the device

-
- -Expand source code - -
def _sendToRadio(self, toRadio):
-    """Send a ToRadio protobuf to the device"""
-    if self.noProto:
-        logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-    else:
-        #logging.debug(f"Sending toRadio: {stripnl(toRadio)}")
-        self._sendToRadioImpl(toRadio)
-
-
-
-def _sendToRadioImpl(self, toRadio) -
-
-

Send a ToRadio protobuf to the device

-
- -Expand source code - -
def _sendToRadioImpl(self, toRadio):
-    """Send a ToRadio protobuf to the device"""
-    logging.error(f"Subclass must provide toradio: {toRadio}")
-
-
-
-def _startConfig(self) -
-
-

Start device packets flowing

-
- -Expand source code - -
def _startConfig(self):
-    """Start device packets flowing"""
-    self.myInfo = None
-    self.nodes = {}  # nodes keyed by ID
-    self.nodesByNum = {}  # nodes keyed by nodenum
-
-    startConfig = mesh_pb2.ToRadio()
-    self.configId = random.randint(0, 0xffffffff)
-    startConfig.want_config_id = self.configId
-    self._sendToRadio(startConfig)
-
-
-
-def _startHeartbeat(self) -
-
-

We need to send a heartbeat message to the device every X seconds

-
- -Expand source code - -
def _startHeartbeat(self):
-    """We need to send a heartbeat message to the device every X seconds"""
-    def callback():
-        self.heartbeatTimer = None
-        prefs = self.localNode.radioConfig.preferences
-        i = prefs.phone_timeout_secs / 2
-        logging.debug(f"Sending heartbeat, interval {i}")
-        if i != 0:
-            self.heartbeatTimer = threading.Timer(i, callback)
-            self.heartbeatTimer.start()
-            p = mesh_pb2.ToRadio()
-            self._sendToRadio(p)
-
-    callback()  # run our periodic callback now, it will make another timer if necessary
-
-
-
-def _waitConnected(self) -
-
-

Block until the initial node db download is complete, or timeout -and raise an exception

-
- -Expand source code - -
def _waitConnected(self):
-    """Block until the initial node db download is complete, or timeout
-    and raise an exception"""
-    if not self.noProto:
-        if not self.isConnected.wait(15.0):  # timeout after x seconds
-            raise Exception("Timed out waiting for connection completion")
-
-    # If we failed while connecting, raise the connection to the client
-    if self.failure:
-        raise self.failure
-
-
-
-def close(self) -
-
-

Shutdown this interface

-
- -Expand source code - -
def close(self):
-    """Shutdown this interface"""
-    if self.heartbeatTimer:
-        self.heartbeatTimer.cancel()
-
-    self._sendDisconnect()
-
-
-
-def getLongName(self) -
-
-

Get long name

-
- -Expand source code - -
def getLongName(self):
-    """Get long name"""
-    user = self.getMyUser()
-    if user is not None:
-        return user.get('longName', None)
-    return None
-
-
-
-def getMyNodeInfo(self) -
-
-

Get info about my node.

-
- -Expand source code - -
def getMyNodeInfo(self):
-    """Get info about my node."""
-    if self.myInfo is None:
-        return None
-    logging.debug(f'self.nodesByNum:{self.nodesByNum}')
-    return self.nodesByNum.get(self.myInfo.my_node_num)
-
-
-
-def getMyUser(self) -
-
-

Get user

-
- -Expand source code - -
def getMyUser(self):
-    """Get user"""
-    nodeInfo = self.getMyNodeInfo()
-    if nodeInfo is not None:
-        return nodeInfo.get('user')
-    return None
-
-
-
-def getNode(self, nodeId) -
-
-

Return a node object which contains device settings and channel info

-
- -Expand source code - -
def getNode(self, nodeId):
-    """Return a node object which contains device settings and channel info"""
-    if nodeId == LOCAL_ADDR:
-        return self.localNode
-    else:
-        n = meshtastic.node.Node(self, nodeId)
-        logging.debug("About to requestConfig")
-        n.requestConfig()
-        if not n.waitForConfig():
-            our_exit("Error: Timed out waiting for node config")
-        return n
-
-
-
-def getShortName(self) -
-
-

Get short name

-
- -Expand source code - -
def getShortName(self):
-    """Get short name"""
-    user = self.getMyUser()
-    if user is not None:
-        return user.get('shortName', None)
-    return None
-
-
-
-def sendData(self, data, destinationId='^all', portNum=256, wantAck=False, wantResponse=False, hopLimit=None, onResponse=None, channelIndex=0) -
-
-

Send a data packet to some other node

-

Keyword Arguments: -data – the data to send, either as an array of bytes or -as a protobuf (which will be automatically -serialized to bytes) -destinationId {nodeId or nodeNum} – where to send this -message (default: {BROADCAST_ADDR}) -portNum – the application portnum (similar to IP port numbers) -of the destination, see portnums.proto for a list -wantAck – True if you want the message sent in a reliable -manner (with retries and ack/nak provided for delivery) -wantResponse – True if you want the service on the other -side to send an application layer response -onResponse – A closure of the form funct(packet), that will be -called when a response packet arrives (or the transaction -is NAKed due to non receipt) -channelIndex - channel number to use

-

Returns the sent packet. The id field will be populated in this packet -and can be used to track future message acks/naks.

-
- -Expand source code - -
def sendData(self, data, destinationId=BROADCAST_ADDR,
-             portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False,
-             wantResponse=False,
-             hopLimit=None,
-             onResponse=None,
-             channelIndex=0):
-    """Send a data packet to some other node
-
-    Keyword Arguments:
-        data -- the data to send, either as an array of bytes or
-                as a protobuf (which will be automatically
-                serialized to bytes)
-        destinationId {nodeId or nodeNum} -- where to send this
-                message (default: {BROADCAST_ADDR})
-        portNum -- the application portnum (similar to IP port numbers)
-                of the destination, see portnums.proto for a list
-        wantAck -- True if you want the message sent in a reliable
-                manner (with retries and ack/nak provided for delivery)
-        wantResponse -- True if you want the service on the other
-                side to send an application layer response
-        onResponse -- A closure of the form funct(packet), that will be
-                called when a response packet arrives (or the transaction
-                is NAKed due to non receipt)
-        channelIndex - channel number to use
-
-    Returns the sent packet. The id field will be populated in this packet
-    and can be used to track future message acks/naks.
-    """
-    if hopLimit is None:
-        hopLimit = self.defaultHopLimit
-
-    if getattr(data, "SerializeToString", None):
-        logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
-        data = data.SerializeToString()
-
-    logging.debug(f"len(data): {len(data)}")
-    logging.debug(f"mesh_pb2.Constants.DATA_PAYLOAD_LEN: {mesh_pb2.Constants.DATA_PAYLOAD_LEN}")
-    if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
-        raise Exception("Data payload too big")
-
-    if portNum == portnums_pb2.PortNum.UNKNOWN_APP:  # we are now more strict wrt port numbers
-        our_exit("Warning: A non-zero port number must be specified")
-
-    meshPacket = mesh_pb2.MeshPacket()
-    meshPacket.channel = channelIndex
-    meshPacket.decoded.payload = data
-    meshPacket.decoded.portnum = portNum
-    meshPacket.decoded.want_response = wantResponse
-
-    p = self._sendPacket(meshPacket, destinationId,
-                         wantAck=wantAck, hopLimit=hopLimit)
-    if onResponse is not None:
-        self._addResponseHandler(p.id, onResponse)
-    return p
-
-
-
-def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, destinationId='^all', wantAck=False, wantResponse=False) -
-
-

Send a position packet to some other node (normally a broadcast)

-

Also, the device software will notice this packet and use it to automatically -set its notion of the local position.

-

If timeSec is not specified (recommended), we will use the local machine time.

-

Returns the sent packet. The id field will be populated in this packet and -can be used to track future message acks/naks.

-
- -Expand source code - -
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
-                 destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
-    """
-    Send a position packet to some other node (normally a broadcast)
-
-    Also, the device software will notice this packet and use it to automatically
-    set its notion of the local position.
-
-    If timeSec is not specified (recommended), we will use the local machine time.
-
-    Returns the sent packet. The id field will be populated in this packet and
-    can be used to track future message acks/naks.
-    """
-    p = mesh_pb2.Position()
-    if latitude != 0.0:
-        p.latitude_i = int(latitude / 1e-7)
-        logging.debug(f'p.latitude_i:{p.latitude_i}')
-
-    if longitude != 0.0:
-        p.longitude_i = int(longitude / 1e-7)
-        logging.debug(f'p.longitude_i:{p.longitude_i}')
-
-    if altitude != 0:
-        p.altitude = int(altitude)
-        logging.debug(f'p.altitude:{p.altitude}')
-
-    if timeSec == 0:
-        timeSec = time.time()  # returns unix timestamp in seconds
-    p.time = int(timeSec)
-    logging.debug(f'p.time:{p.time}')
-
-    return self.sendData(p, destinationId,
-                         portNum=portnums_pb2.PortNum.POSITION_APP,
-                         wantAck=wantAck,
-                         wantResponse=wantResponse)
-
-
-
-def sendText(self, text: ~AnyStr, destinationId='^all', wantAck=False, wantResponse=False, hopLimit=None, onResponse=None, channelIndex=0) -
-
-

Send a utf8 string to some other node, if the node has a display it -will also be shown on the device.

-

Arguments

-

text {string} – The text to send

-

Keyword Arguments: -destinationId {nodeId or nodeNum} – where to send this -message (default: {BROADCAST_ADDR}) -portNum – the application portnum (similar to IP port numbers) -of the destination, see portnums.proto for a list -wantAck – True if you want the message sent in a reliable manner -(with retries and ack/nak provided for delivery) -wantResponse – True if you want the service on the other side to -send an application layer response

-

Returns the sent packet. The id field will be populated in this packet -and can be used to track future message acks/naks.

-
- -Expand source code - -
def sendText(self, text: AnyStr,
-             destinationId=BROADCAST_ADDR,
-             wantAck=False,
-             wantResponse=False,
-             hopLimit=None,
-             onResponse=None,
-             channelIndex=0):
-    """Send a utf8 string to some other node, if the node has a display it
-       will also be shown on the device.
-
-    Arguments:
-        text {string} -- The text to send
-
-    Keyword Arguments:
-        destinationId {nodeId or nodeNum} -- where to send this
-                                             message (default: {BROADCAST_ADDR})
-        portNum -- the application portnum (similar to IP port numbers)
-                   of the destination, see portnums.proto for a list
-        wantAck -- True if you want the message sent in a reliable manner
-                   (with retries and ack/nak provided for delivery)
-        wantResponse -- True if you want the service on the other side to
-                        send an application layer response
-
-    Returns the sent packet. The id field will be populated in this packet
-    and can be used to track future message acks/naks.
-    """
-    if hopLimit is None:
-        hopLimit = self.defaultHopLimit
-
-    return self.sendData(text.encode("utf-8"), destinationId,
-                         portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
-                         wantAck=wantAck,
-                         wantResponse=wantResponse,
-                         hopLimit=hopLimit,
-                         onResponse=onResponse,
-                         channelIndex=channelIndex)
-
-
-
-def showInfo(self, file=sys.stdout) -
-
-

Show human readable summary about this object

-
- -Expand source code - -
def showInfo(self, file=sys.stdout):
-    """Show human readable summary about this object"""
-    owner = f"Owner: {self.getLongName()} ({self.getShortName()})"
-    myinfo = ''
-    if self.myInfo:
-        myinfo = f"\nMy info: {stripnl(MessageToJson(self.myInfo))}"
-    mesh = "\nNodes in mesh:"
-    nodes = ""
-    if self.nodes:
-        for n in self.nodes.values():
-            nodes = nodes + f"  {stripnl(n)}"
-    infos = owner + myinfo + mesh + nodes
-    print(infos)
-    return infos
-
-
-
-def showNodes(self, includeSelf=True, file=sys.stdout) -
-
-

Show table summary of nodes in mesh

-
- -Expand source code - -
def showNodes(self, includeSelf=True, file=sys.stdout):
-    """Show table summary of nodes in mesh"""
-    def formatFloat(value, precision=2, unit=''):
-        """Format a float value with precsion."""
-        return f'{value:.{precision}f}{unit}' if value else None
-
-    def getLH(ts):
-        """Format last heard"""
-        return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
-
-    def getTimeAgo(ts):
-        """Format how long ago have we heard from this node (aka timeago)."""
-        return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
-
-    rows = []
-    if self.nodes:
-        logging.debug(f'self.nodes:{self.nodes}')
-        for node in self.nodes.values():
-            if not includeSelf and node['num'] == self.localNode.nodeNum:
-                continue
-
-            row = {"N": 0}
-
-            user = node.get('user')
-            if user:
-                row.update({
-                    "User": user['longName'],
-                    "AKA":  user['shortName'],
-                    "ID":   user['id'],
-                })
-
-            pos = node.get('position')
-            if pos:
-                row.update({
-                    "Latitude":  formatFloat(pos.get("latitude"),     4, "°"),
-                    "Longitude": formatFloat(pos.get("longitude"),    4, "°"),
-                    "Altitude":  formatFloat(pos.get("altitude"),     0, " m"),
-                    "Battery":   formatFloat(pos.get("batteryLevel"), 2, "%"),
-                })
-
-            row.update({
-                "SNR":       formatFloat(node.get("snr"), 2, " dB"),
-                "LastHeard": getLH(node.get("lastHeard")),
-                "Since":     getTimeAgo(node.get("lastHeard")),
-            })
-
-            rows.append(row)
-
-    rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
-    for i, row in enumerate(rows):
-        row['N'] = i+1
-
-    table = tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid')
-    print(table)
-    return table
-
-
-
-def waitForConfig(self) -
-
-

Block until radio config is received. Returns True if config has been received.

-
- -Expand source code - -
def waitForConfig(self):
-    """Block until radio config is received. Returns True if config has been received."""
-    success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
-    if not success:
-        raise Exception("Timed out waiting for interface config")
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/mesh_pb2.html b/docs/meshtastic/mesh_pb2.html deleted file mode 100644 index 08b484c..0000000 --- a/docs/meshtastic/mesh_pb2.html +++ /dev/null @@ -1,2695 +0,0 @@ - - - - - - -meshtastic.mesh_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.mesh_pb2

-
-
-
- -Expand source code - -
# -*- coding: utf-8 -*-
-# Generated by the protocol buffer compiler.  DO NOT EDIT!
-# source: mesh.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()
-
-
-from . import portnums_pb2 as portnums__pb2
-
-
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='mesh.proto',
-  package='',
-  syntax='proto3',
-  serialized_options=b'\n\023com.geeksville.meshB\nMeshProtosH\003Z!github.com/meshtastic/gomeshproto',
-  serialized_pb=b'\n\nmesh.proto\x1a\x0eportnums.proto\"\x94\x06\n\x08Position\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x15\n\rbattery_level\x18\x04 \x01(\x05\x12\x0c\n\x04time\x18\t \x01(\x07\x12,\n\x0flocation_source\x18\n \x01(\x0e\x32\x13.Position.LocSource\x12,\n\x0f\x61ltitude_source\x18\x0b \x01(\x0e\x32\x13.Position.AltSource\x12\x15\n\rpos_timestamp\x18\x0c \x01(\x07\x12\x17\n\x0fpos_time_millis\x18\r \x01(\x05\x12\x14\n\x0c\x61ltitude_hae\x18\x0e \x01(\x11\x12\x15\n\ralt_geoid_sep\x18\x0f \x01(\x11\x12\x0c\n\x04PDOP\x18\x10 \x01(\r\x12\x0c\n\x04HDOP\x18\x11 \x01(\r\x12\x0c\n\x04VDOP\x18\x12 \x01(\r\x12\x14\n\x0cgps_accuracy\x18\x13 \x01(\r\x12\x14\n\x0cground_speed\x18\x14 \x01(\r\x12\x14\n\x0cground_track\x18\x15 \x01(\r\x12\x13\n\x0b\x66ix_quality\x18\x16 \x01(\r\x12\x10\n\x08\x66ix_type\x18\x17 \x01(\r\x12\x14\n\x0csats_in_view\x18\x18 \x01(\r\x12\x11\n\tsensor_id\x18\x19 \x01(\r\x12\x17\n\x0fpos_next_update\x18( \x01(\r\x12\x16\n\x0epos_seq_number\x18) \x01(\r\"n\n\tLocSource\x12\x16\n\x12LOCSRC_UNSPECIFIED\x10\x00\x12\x17\n\x13LOCSRC_MANUAL_ENTRY\x10\x01\x12\x17\n\x13LOCSRC_GPS_INTERNAL\x10\x02\x12\x17\n\x13LOCSRC_GPS_EXTERNAL\x10\x03\"\x85\x01\n\tAltSource\x12\x16\n\x12\x41LTSRC_UNSPECIFIED\x10\x00\x12\x17\n\x13\x41LTSRC_MANUAL_ENTRY\x10\x01\x12\x17\n\x13\x41LTSRC_GPS_INTERNAL\x10\x02\x12\x17\n\x13\x41LTSRC_GPS_EXTERNAL\x10\x03\x12\x15\n\x11\x41LTSRC_BAROMETRIC\x10\x04J\x04\x08\x07\x10\x08J\x04\x08\x08\x10\t\"\xd7\x01\n\x04User\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x0f\n\x07macaddr\x18\x04 \x01(\x0c\x12 \n\x08hw_model\x18\x06 \x01(\x0e\x32\x0e.HardwareModel\x12\x13\n\x0bis_licensed\x18\x07 \x01(\x08\x12\x13\n\x04team\x18\x08 \x01(\x0e\x32\x05.Team\x12\x14\n\x0ctx_power_dbm\x18\n \x01(\r\x12\x14\n\x0c\x61nt_gain_dbi\x18\x0b \x01(\r\x12\x13\n\x0b\x61nt_azimuth\x18\x0c \x01(\r\"\x1f\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x02 \x03(\x07\"\xc5\x02\n\x07Routing\x12(\n\rroute_request\x18\x01 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0broute_reply\x18\x02 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0c\x65rror_reason\x18\x03 \x01(\x0e\x32\x0e.Routing.ErrorH\x00\"\xb4\x01\n\x05\x45rror\x12\x08\n\x04NONE\x10\x00\x12\x0c\n\x08NO_ROUTE\x10\x01\x12\x0b\n\x07GOT_NAK\x10\x02\x12\x0b\n\x07TIMEOUT\x10\x03\x12\x10\n\x0cNO_INTERFACE\x10\x04\x12\x12\n\x0eMAX_RETRANSMIT\x10\x05\x12\x0e\n\nNO_CHANNEL\x10\x06\x12\r\n\tTOO_LARGE\x10\x07\x12\x0f\n\x0bNO_RESPONSE\x10\x08\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10 \x12\x12\n\x0eNOT_AUTHORIZED\x10!B\t\n\x07variant\"{\n\x04\x44\x61ta\x12\x19\n\x07portnum\x18\x01 \x01(\x0e\x32\x08.PortNum\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x15\n\rwant_response\x18\x03 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x04 \x01(\x07\x12\x0e\n\x06source\x18\x05 \x01(\x07\x12\x12\n\nrequest_id\x18\x06 \x01(\x07\"\xe0\x02\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x07\x12\n\n\x02to\x18\x02 \x01(\x07\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12\x18\n\x07\x64\x65\x63oded\x18\x04 \x01(\x0b\x32\x05.DataH\x00\x12\x13\n\tencrypted\x18\x05 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\x07\x12\x0f\n\x07rx_time\x18\x07 \x01(\x07\x12\x0e\n\x06rx_snr\x18\x08 \x01(\x02\x12\x11\n\thop_limit\x18\n \x01(\r\x12\x10\n\x08want_ack\x18\x0b \x01(\x08\x12&\n\x08priority\x18\x0c \x01(\x0e\x32\x14.MeshPacket.Priority\x12\x0f\n\x07rx_rssi\x18\r \x01(\x05\"[\n\x08Priority\x12\t\n\x05UNSET\x10\x00\x12\x07\n\x03MIN\x10\x01\x12\x0e\n\nBACKGROUND\x10\n\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10@\x12\x0c\n\x08RELIABLE\x10\x46\x12\x07\n\x03\x41\x43K\x10x\x12\x07\n\x03MAX\x10\x7f\x42\x10\n\x0epayloadVariant\"j\n\x08NodeInfo\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x13\n\x04user\x18\x02 \x01(\x0b\x32\x05.User\x12\x1b\n\x08position\x18\x03 \x01(\x0b\x32\t.Position\x12\x0b\n\x03snr\x18\x07 \x01(\x02\x12\x12\n\nlast_heard\x18\x04 \x01(\x07\"\x8a\x03\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x0f\n\x07has_gps\x18\x02 \x01(\x08\x12\x11\n\tnum_bands\x18\x03 \x01(\r\x12\x14\n\x0cmax_channels\x18\x0f \x01(\r\x12\x12\n\x06region\x18\x04 \x01(\tB\x02\x18\x01\x12\x1f\n\x13hw_model_deprecated\x18\x05 \x01(\tB\x02\x18\x01\x12\x18\n\x10\x66irmware_version\x18\x06 \x01(\t\x12&\n\nerror_code\x18\x07 \x01(\x0e\x32\x12.CriticalErrorCode\x12\x15\n\rerror_address\x18\x08 \x01(\r\x12\x13\n\x0b\x65rror_count\x18\t \x01(\r\x12\x14\n\x0creboot_count\x18\n \x01(\r\x12\x0f\n\x07\x62itrate\x18\x0b \x01(\x02\x12\x1c\n\x14message_timeout_msec\x18\r \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0e \x01(\r\x12\x15\n\rair_period_tx\x18\x10 \x03(\r\x12\x15\n\rair_period_rx\x18\x11 \x03(\r\"\xb5\x01\n\tLogRecord\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12\x0e\n\x06source\x18\x03 \x01(\t\x12\x1f\n\x05level\x18\x04 \x01(\x0e\x32\x10.LogRecord.Level\"X\n\x05Level\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x43RITICAL\x10\x32\x12\t\n\x05\x45RROR\x10(\x12\x0b\n\x07WARNING\x10\x1e\x12\x08\n\x04INFO\x10\x14\x12\t\n\x05\x44\x45\x42UG\x10\n\x12\t\n\x05TRACE\x10\x05\"\xe9\x01\n\tFromRadio\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x1d\n\x06packet\x18\x0b \x01(\x0b\x32\x0b.MeshPacketH\x00\x12\x1e\n\x07my_info\x18\x03 \x01(\x0b\x32\x0b.MyNodeInfoH\x00\x12\x1e\n\tnode_info\x18\x04 \x01(\x0b\x32\t.NodeInfoH\x00\x12 \n\nlog_record\x18\x07 \x01(\x0b\x32\n.LogRecordH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x08 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\t \x01(\x08H\x00\x42\x10\n\x0epayloadVariantJ\x04\x08\x02\x10\x03J\x04\x08\x06\x10\x07\"\xe1\x01\n\x07ToRadio\x12\x1d\n\x06packet\x18\x02 \x01(\x0b\x32\x0b.MeshPacketH\x00\x12&\n\tpeer_info\x18\x03 \x01(\x0b\x32\x11.ToRadio.PeerInfoH\x00\x12\x18\n\x0ewant_config_id\x18\x64 \x01(\rH\x00\x12\x14\n\ndisconnect\x18h \x01(\x08H\x00\x1a\x35\n\x08PeerInfo\x12\x13\n\x0b\x61pp_version\x18\x01 \x01(\r\x12\x14\n\x0cmqtt_gateway\x18\x02 \x01(\x08\x42\x10\n\x0epayloadVariantJ\x04\x08\x01\x10\x02J\x04\x08\x65\x10\x66J\x04\x08\x66\x10gJ\x04\x08g\x10h*\xac\x02\n\rHardwareModel\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08TLORA_V2\x10\x01\x12\x0c\n\x08TLORA_V1\x10\x02\x12\x12\n\x0eTLORA_V2_1_1p6\x10\x03\x12\t\n\x05TBEAM\x10\x04\x12\x0f\n\x0bHELTEC_V2_0\x10\x05\x12\x0c\n\x08TBEAM0p7\x10\x06\x12\n\n\x06T_ECHO\x10\x07\x12\x10\n\x0cTLORA_V1_1p3\x10\x08\x12\x0b\n\x07RAK4631\x10\t\x12\x0f\n\x0bHELTEC_V2_1\x10\n\x12\x11\n\rLORA_RELAY_V1\x10 \x12\x0e\n\nNRF52840DK\x10!\x12\x07\n\x03PPR\x10\"\x12\x0f\n\x0bGENIEBLOCKS\x10#\x12\x11\n\rNRF52_UNKNOWN\x10$\x12\r\n\tPORTDUINO\x10%\x12\x0f\n\x0b\x41NDROID_SIM\x10&\x12\n\n\x06\x44IY_V1\x10\'*\xb5\x01\n\x04Team\x12\t\n\x05\x43LEAR\x10\x00\x12\x08\n\x04\x43YAN\x10\x01\x12\t\n\x05WHITE\x10\x02\x12\n\n\x06YELLOW\x10\x03\x12\n\n\x06ORANGE\x10\x04\x12\x0b\n\x07MAGENTA\x10\x05\x12\x07\n\x03RED\x10\x06\x12\n\n\x06MAROON\x10\x07\x12\n\n\x06PURPLE\x10\x08\x12\r\n\tDARK_BLUE\x10\t\x12\x08\n\x04\x42LUE\x10\n\x12\x08\n\x04TEAL\x10\x0b\x12\t\n\x05GREEN\x10\x0c\x12\x0e\n\nDARK_GREEN\x10\r\x12\t\n\x05\x42ROWN\x10\x0e*.\n\tConstants\x12\n\n\x06Unused\x10\x00\x12\x15\n\x10\x44\x41TA_PAYLOAD_LEN\x10\xed\x01*\xe1\x01\n\x11\x43riticalErrorCode\x12\x08\n\x04None\x10\x00\x12\x0e\n\nTxWatchdog\x10\x01\x12\x12\n\x0eSleepEnterWait\x10\x02\x12\x0b\n\x07NoRadio\x10\x03\x12\x0f\n\x0bUnspecified\x10\x04\x12\x13\n\x0fUBloxInitFailed\x10\x05\x12\x0c\n\x08NoAXP192\x10\x06\x12\x17\n\x13InvalidRadioSetting\x10\x07\x12\x12\n\x0eTransmitFailed\x10\x08\x12\x0c\n\x08\x42rownout\x10\t\x12\x11\n\rSX1262Failure\x10\n\x12\x0f\n\x0bRadioSpiBug\x10\x0b\x42\x46\n\x13\x63om.geeksville.meshB\nMeshProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
-  ,
-  dependencies=[portnums__pb2.DESCRIPTOR,])
-
-_HARDWAREMODEL = _descriptor.EnumDescriptor(
-  name='HardwareModel',
-  full_name='HardwareModel',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNSET', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TLORA_V2', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TLORA_V1', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TLORA_V2_1_1p6', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TBEAM', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='HELTEC_V2_0', index=5, number=5,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TBEAM0p7', index=6, number=6,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='T_ECHO', index=7, number=7,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TLORA_V1_1p3', index=8, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='RAK4631', index=9, number=9,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='HELTEC_V2_1', index=10, number=10,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LORA_RELAY_V1', index=11, number=32,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NRF52840DK', index=12, number=33,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='PPR', index=13, number=34,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GENIEBLOCKS', index=14, number=35,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NRF52_UNKNOWN', index=15, number=36,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='PORTDUINO', index=16, number=37,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ANDROID_SIM', index=17, number=38,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DIY_V1', index=18, number=39,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3034,
-  serialized_end=3334,
-)
-_sym_db.RegisterEnumDescriptor(_HARDWAREMODEL)
-
-HardwareModel = enum_type_wrapper.EnumTypeWrapper(_HARDWAREMODEL)
-_TEAM = _descriptor.EnumDescriptor(
-  name='Team',
-  full_name='Team',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='CLEAR', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CYAN', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='WHITE', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='YELLOW', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ORANGE', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MAGENTA', index=5, number=5,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='RED', index=6, number=6,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MAROON', index=7, number=7,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='PURPLE', index=8, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DARK_BLUE', index=9, number=9,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='BLUE', index=10, number=10,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TEAL', index=11, number=11,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GREEN', index=12, number=12,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DARK_GREEN', index=13, number=13,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='BROWN', index=14, number=14,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3337,
-  serialized_end=3518,
-)
-_sym_db.RegisterEnumDescriptor(_TEAM)
-
-Team = enum_type_wrapper.EnumTypeWrapper(_TEAM)
-_CONSTANTS = _descriptor.EnumDescriptor(
-  name='Constants',
-  full_name='Constants',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='Unused', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DATA_PAYLOAD_LEN', index=1, number=237,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3520,
-  serialized_end=3566,
-)
-_sym_db.RegisterEnumDescriptor(_CONSTANTS)
-
-Constants = enum_type_wrapper.EnumTypeWrapper(_CONSTANTS)
-_CRITICALERRORCODE = _descriptor.EnumDescriptor(
-  name='CriticalErrorCode',
-  full_name='CriticalErrorCode',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='None', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TxWatchdog', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='SleepEnterWait', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NoRadio', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='Unspecified', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='UBloxInitFailed', index=5, number=5,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NoAXP192', index=6, number=6,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='InvalidRadioSetting', index=7, number=7,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TransmitFailed', index=8, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='Brownout', index=9, number=9,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='SX1262Failure', index=10, number=10,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='RadioSpiBug', index=11, number=11,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3569,
-  serialized_end=3794,
-)
-_sym_db.RegisterEnumDescriptor(_CRITICALERRORCODE)
-
-CriticalErrorCode = enum_type_wrapper.EnumTypeWrapper(_CRITICALERRORCODE)
-UNSET = 0
-TLORA_V2 = 1
-TLORA_V1 = 2
-TLORA_V2_1_1p6 = 3
-TBEAM = 4
-HELTEC_V2_0 = 5
-TBEAM0p7 = 6
-T_ECHO = 7
-TLORA_V1_1p3 = 8
-RAK4631 = 9
-HELTEC_V2_1 = 10
-LORA_RELAY_V1 = 32
-NRF52840DK = 33
-PPR = 34
-GENIEBLOCKS = 35
-NRF52_UNKNOWN = 36
-PORTDUINO = 37
-ANDROID_SIM = 38
-DIY_V1 = 39
-CLEAR = 0
-CYAN = 1
-WHITE = 2
-YELLOW = 3
-ORANGE = 4
-MAGENTA = 5
-RED = 6
-MAROON = 7
-PURPLE = 8
-DARK_BLUE = 9
-BLUE = 10
-TEAL = 11
-GREEN = 12
-DARK_GREEN = 13
-BROWN = 14
-Unused = 0
-DATA_PAYLOAD_LEN = 237
-globals()['None'] = 0
-TxWatchdog = 1
-SleepEnterWait = 2
-NoRadio = 3
-Unspecified = 4
-UBloxInitFailed = 5
-NoAXP192 = 6
-InvalidRadioSetting = 7
-TransmitFailed = 8
-Brownout = 9
-SX1262Failure = 10
-RadioSpiBug = 11
-
-
-_POSITION_LOCSOURCE = _descriptor.EnumDescriptor(
-  name='LocSource',
-  full_name='Position.LocSource',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='LOCSRC_UNSPECIFIED', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LOCSRC_MANUAL_ENTRY', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LOCSRC_GPS_INTERNAL', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LOCSRC_GPS_EXTERNAL', index=3, number=3,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=561,
-  serialized_end=671,
-)
-_sym_db.RegisterEnumDescriptor(_POSITION_LOCSOURCE)
-
-_POSITION_ALTSOURCE = _descriptor.EnumDescriptor(
-  name='AltSource',
-  full_name='Position.AltSource',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='ALTSRC_UNSPECIFIED', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ALTSRC_MANUAL_ENTRY', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ALTSRC_GPS_INTERNAL', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ALTSRC_GPS_EXTERNAL', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ALTSRC_BAROMETRIC', index=4, number=4,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=674,
-  serialized_end=807,
-)
-_sym_db.RegisterEnumDescriptor(_POSITION_ALTSOURCE)
-
-_ROUTING_ERROR = _descriptor.EnumDescriptor(
-  name='Error',
-  full_name='Routing.Error',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='NONE', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NO_ROUTE', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GOT_NAK', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TIMEOUT', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NO_INTERFACE', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MAX_RETRANSMIT', index=5, number=5,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NO_CHANNEL', index=6, number=6,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TOO_LARGE', index=7, number=7,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NO_RESPONSE', index=8, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='BAD_REQUEST', index=9, number=32,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='NOT_AUTHORIZED', index=10, number=33,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=1207,
-  serialized_end=1387,
-)
-_sym_db.RegisterEnumDescriptor(_ROUTING_ERROR)
-
-_MESHPACKET_PRIORITY = _descriptor.EnumDescriptor(
-  name='Priority',
-  full_name='MeshPacket.Priority',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNSET', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MIN', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='BACKGROUND', index=2, number=10,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DEFAULT', index=3, number=64,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='RELIABLE', index=4, number=70,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ACK', index=5, number=120,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MAX', index=6, number=127,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=1769,
-  serialized_end=1860,
-)
-_sym_db.RegisterEnumDescriptor(_MESHPACKET_PRIORITY)
-
-_LOGRECORD_LEVEL = _descriptor.EnumDescriptor(
-  name='Level',
-  full_name='LogRecord.Level',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNSET', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CRITICAL', index=1, number=50,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ERROR', index=2, number=40,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='WARNING', index=3, number=30,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='INFO', index=4, number=20,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DEBUG', index=5, number=10,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TRACE', index=6, number=5,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2479,
-  serialized_end=2567,
-)
-_sym_db.RegisterEnumDescriptor(_LOGRECORD_LEVEL)
-
-
-_POSITION = _descriptor.Descriptor(
-  name='Position',
-  full_name='Position',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='latitude_i', full_name='Position.latitude_i', index=0,
-      number=1, type=15, 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='longitude_i', full_name='Position.longitude_i', index=1,
-      number=2, type=15, 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='altitude', full_name='Position.altitude', index=2,
-      number=3, 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='battery_level', full_name='Position.battery_level', index=3,
-      number=4, 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='time', full_name='Position.time', index=4,
-      number=9, 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='location_source', full_name='Position.location_source', index=5,
-      number=10, 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='altitude_source', full_name='Position.altitude_source', index=6,
-      number=11, 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='pos_timestamp', full_name='Position.pos_timestamp', index=7,
-      number=12, 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='pos_time_millis', full_name='Position.pos_time_millis', index=8,
-      number=13, 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='altitude_hae', full_name='Position.altitude_hae', index=9,
-      number=14, type=17, 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='alt_geoid_sep', full_name='Position.alt_geoid_sep', index=10,
-      number=15, type=17, 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='PDOP', full_name='Position.PDOP', index=11,
-      number=16, 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='HDOP', full_name='Position.HDOP', index=12,
-      number=17, 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='VDOP', full_name='Position.VDOP', index=13,
-      number=18, 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='gps_accuracy', full_name='Position.gps_accuracy', index=14,
-      number=19, 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='ground_speed', full_name='Position.ground_speed', index=15,
-      number=20, 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='ground_track', full_name='Position.ground_track', index=16,
-      number=21, 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='fix_quality', full_name='Position.fix_quality', index=17,
-      number=22, 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='fix_type', full_name='Position.fix_type', index=18,
-      number=23, 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='sats_in_view', full_name='Position.sats_in_view', index=19,
-      number=24, 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='sensor_id', full_name='Position.sensor_id', index=20,
-      number=25, 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='pos_next_update', full_name='Position.pos_next_update', index=21,
-      number=40, 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='pos_seq_number', full_name='Position.pos_seq_number', index=22,
-      number=41, 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=[
-    _POSITION_LOCSOURCE,
-    _POSITION_ALTSOURCE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=31,
-  serialized_end=819,
-)
-
-
-_USER = _descriptor.Descriptor(
-  name='User',
-  full_name='User',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='id', full_name='User.id', index=0,
-      number=1, 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='long_name', full_name='User.long_name', 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='short_name', full_name='User.short_name', 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),
-    _descriptor.FieldDescriptor(
-      name='macaddr', full_name='User.macaddr', index=3,
-      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='hw_model', full_name='User.hw_model', index=4,
-      number=6, 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='is_licensed', full_name='User.is_licensed', index=5,
-      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='team', full_name='User.team', index=6,
-      number=8, 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='tx_power_dbm', full_name='User.tx_power_dbm', index=7,
-      number=10, 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='ant_gain_dbi', full_name='User.ant_gain_dbi', index=8,
-      number=11, 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='ant_azimuth', full_name='User.ant_azimuth', index=9,
-      number=12, 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=822,
-  serialized_end=1037,
-)
-
-
-_ROUTEDISCOVERY = _descriptor.Descriptor(
-  name='RouteDiscovery',
-  full_name='RouteDiscovery',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='route', full_name='RouteDiscovery.route', index=0,
-      number=2, type=7, cpp_type=3, 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=1039,
-  serialized_end=1070,
-)
-
-
-_ROUTING = _descriptor.Descriptor(
-  name='Routing',
-  full_name='Routing',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='route_request', full_name='Routing.route_request', 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='route_reply', full_name='Routing.route_reply', 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='error_reason', full_name='Routing.error_reason', 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=[
-    _ROUTING_ERROR,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-    _descriptor.OneofDescriptor(
-      name='variant', full_name='Routing.variant',
-      index=0, containing_type=None, fields=[]),
-  ],
-  serialized_start=1073,
-  serialized_end=1398,
-)
-
-
-_DATA = _descriptor.Descriptor(
-  name='Data',
-  full_name='Data',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='portnum', full_name='Data.portnum', 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='payload', full_name='Data.payload', index=1,
-      number=2, 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='want_response', full_name='Data.want_response', index=2,
-      number=3, 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='dest', full_name='Data.dest', index=3,
-      number=4, 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='source', full_name='Data.source', index=4,
-      number=5, 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='request_id', full_name='Data.request_id', index=5,
-      number=6, 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),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1400,
-  serialized_end=1523,
-)
-
-
-_MESHPACKET = _descriptor.Descriptor(
-  name='MeshPacket',
-  full_name='MeshPacket',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='from', full_name='MeshPacket.from', index=0,
-      number=1, 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='to', full_name='MeshPacket.to', index=1,
-      number=2, 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='channel', full_name='MeshPacket.channel', 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='decoded', full_name='MeshPacket.decoded', index=3,
-      number=4, 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='encrypted', full_name='MeshPacket.encrypted', index=4,
-      number=5, 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='id', full_name='MeshPacket.id', index=5,
-      number=6, 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='rx_time', full_name='MeshPacket.rx_time', index=6,
-      number=7, 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='rx_snr', full_name='MeshPacket.rx_snr', index=7,
-      number=8, 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='hop_limit', full_name='MeshPacket.hop_limit', index=8,
-      number=10, 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='want_ack', full_name='MeshPacket.want_ack', index=9,
-      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),
-    _descriptor.FieldDescriptor(
-      name='priority', full_name='MeshPacket.priority', index=10,
-      number=12, 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='rx_rssi', full_name='MeshPacket.rx_rssi', index=11,
-      number=13, 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=[
-    _MESHPACKET_PRIORITY,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-    _descriptor.OneofDescriptor(
-      name='payloadVariant', full_name='MeshPacket.payloadVariant',
-      index=0, containing_type=None, fields=[]),
-  ],
-  serialized_start=1526,
-  serialized_end=1878,
-)
-
-
-_NODEINFO = _descriptor.Descriptor(
-  name='NodeInfo',
-  full_name='NodeInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='num', full_name='NodeInfo.num', 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='user', full_name='NodeInfo.user', 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='position', full_name='NodeInfo.position', 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='snr', full_name='NodeInfo.snr', index=3,
-      number=7, 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='last_heard', full_name='NodeInfo.last_heard', index=4,
-      number=4, 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),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1880,
-  serialized_end=1986,
-)
-
-
-_MYNODEINFO = _descriptor.Descriptor(
-  name='MyNodeInfo',
-  full_name='MyNodeInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='my_node_num', full_name='MyNodeInfo.my_node_num', 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='has_gps', full_name='MyNodeInfo.has_gps', index=1,
-      number=2, 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='num_bands', full_name='MyNodeInfo.num_bands', 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='max_channels', full_name='MyNodeInfo.max_channels', index=3,
-      number=15, 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='region', full_name='MyNodeInfo.region', index=4,
-      number=4, 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=b'\030\001', file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='hw_model_deprecated', full_name='MyNodeInfo.hw_model_deprecated', index=5,
-      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=b'\030\001', file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='firmware_version', full_name='MyNodeInfo.firmware_version', index=6,
-      number=6, 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='error_code', full_name='MyNodeInfo.error_code', index=7,
-      number=7, 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='error_address', full_name='MyNodeInfo.error_address', index=8,
-      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='error_count', full_name='MyNodeInfo.error_count', index=9,
-      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='reboot_count', full_name='MyNodeInfo.reboot_count', index=10,
-      number=10, 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='bitrate', full_name='MyNodeInfo.bitrate', index=11,
-      number=11, 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='message_timeout_msec', full_name='MyNodeInfo.message_timeout_msec', index=12,
-      number=13, 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='min_app_version', full_name='MyNodeInfo.min_app_version', index=13,
-      number=14, 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='air_period_tx', full_name='MyNodeInfo.air_period_tx', index=14,
-      number=16, type=13, cpp_type=3, 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='air_period_rx', full_name='MyNodeInfo.air_period_rx', index=15,
-      number=17, type=13, cpp_type=3, 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=1989,
-  serialized_end=2383,
-)
-
-
-_LOGRECORD = _descriptor.Descriptor(
-  name='LogRecord',
-  full_name='LogRecord',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='message', full_name='LogRecord.message', index=0,
-      number=1, 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='time', full_name='LogRecord.time', index=1,
-      number=2, 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='source', full_name='LogRecord.source', 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),
-    _descriptor.FieldDescriptor(
-      name='level', full_name='LogRecord.level', index=3,
-      number=4, 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=[
-    _LOGRECORD_LEVEL,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2386,
-  serialized_end=2567,
-)
-
-
-_FROMRADIO = _descriptor.Descriptor(
-  name='FromRadio',
-  full_name='FromRadio',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='num', full_name='FromRadio.num', 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='packet', full_name='FromRadio.packet', index=1,
-      number=11, 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_info', full_name='FromRadio.my_info', 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_info', full_name='FromRadio.node_info', index=3,
-      number=4, 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='log_record', full_name='FromRadio.log_record', index=4,
-      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='config_complete_id', full_name='FromRadio.config_complete_id', 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='rebooted', full_name='FromRadio.rebooted', index=6,
-      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),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-    _descriptor.OneofDescriptor(
-      name='payloadVariant', full_name='FromRadio.payloadVariant',
-      index=0, containing_type=None, fields=[]),
-  ],
-  serialized_start=2570,
-  serialized_end=2803,
-)
-
-
-_TORADIO_PEERINFO = _descriptor.Descriptor(
-  name='PeerInfo',
-  full_name='ToRadio.PeerInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='app_version', full_name='ToRadio.PeerInfo.app_version', 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='mqtt_gateway', full_name='ToRadio.PeerInfo.mqtt_gateway', index=1,
-      number=2, 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=2936,
-  serialized_end=2989,
-)
-
-_TORADIO = _descriptor.Descriptor(
-  name='ToRadio',
-  full_name='ToRadio',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='packet', full_name='ToRadio.packet', index=0,
-      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='peer_info', full_name='ToRadio.peer_info', index=1,
-      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='want_config_id', full_name='ToRadio.want_config_id', index=2,
-      number=100, 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='disconnect', full_name='ToRadio.disconnect', index=3,
-      number=104, 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=[_TORADIO_PEERINFO, ],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-    _descriptor.OneofDescriptor(
-      name='payloadVariant', full_name='ToRadio.payloadVariant',
-      index=0, containing_type=None, fields=[]),
-  ],
-  serialized_start=2806,
-  serialized_end=3031,
-)
-
-_POSITION.fields_by_name['location_source'].enum_type = _POSITION_LOCSOURCE
-_POSITION.fields_by_name['altitude_source'].enum_type = _POSITION_ALTSOURCE
-_POSITION_LOCSOURCE.containing_type = _POSITION
-_POSITION_ALTSOURCE.containing_type = _POSITION
-_USER.fields_by_name['hw_model'].enum_type = _HARDWAREMODEL
-_USER.fields_by_name['team'].enum_type = _TEAM
-_ROUTING.fields_by_name['route_request'].message_type = _ROUTEDISCOVERY
-_ROUTING.fields_by_name['route_reply'].message_type = _ROUTEDISCOVERY
-_ROUTING.fields_by_name['error_reason'].enum_type = _ROUTING_ERROR
-_ROUTING_ERROR.containing_type = _ROUTING
-_ROUTING.oneofs_by_name['variant'].fields.append(
-  _ROUTING.fields_by_name['route_request'])
-_ROUTING.fields_by_name['route_request'].containing_oneof = _ROUTING.oneofs_by_name['variant']
-_ROUTING.oneofs_by_name['variant'].fields.append(
-  _ROUTING.fields_by_name['route_reply'])
-_ROUTING.fields_by_name['route_reply'].containing_oneof = _ROUTING.oneofs_by_name['variant']
-_ROUTING.oneofs_by_name['variant'].fields.append(
-  _ROUTING.fields_by_name['error_reason'])
-_ROUTING.fields_by_name['error_reason'].containing_oneof = _ROUTING.oneofs_by_name['variant']
-_DATA.fields_by_name['portnum'].enum_type = portnums__pb2._PORTNUM
-_MESHPACKET.fields_by_name['decoded'].message_type = _DATA
-_MESHPACKET.fields_by_name['priority'].enum_type = _MESHPACKET_PRIORITY
-_MESHPACKET_PRIORITY.containing_type = _MESHPACKET
-_MESHPACKET.oneofs_by_name['payloadVariant'].fields.append(
-  _MESHPACKET.fields_by_name['decoded'])
-_MESHPACKET.fields_by_name['decoded'].containing_oneof = _MESHPACKET.oneofs_by_name['payloadVariant']
-_MESHPACKET.oneofs_by_name['payloadVariant'].fields.append(
-  _MESHPACKET.fields_by_name['encrypted'])
-_MESHPACKET.fields_by_name['encrypted'].containing_oneof = _MESHPACKET.oneofs_by_name['payloadVariant']
-_NODEINFO.fields_by_name['user'].message_type = _USER
-_NODEINFO.fields_by_name['position'].message_type = _POSITION
-_MYNODEINFO.fields_by_name['error_code'].enum_type = _CRITICALERRORCODE
-_LOGRECORD.fields_by_name['level'].enum_type = _LOGRECORD_LEVEL
-_LOGRECORD_LEVEL.containing_type = _LOGRECORD
-_FROMRADIO.fields_by_name['packet'].message_type = _MESHPACKET
-_FROMRADIO.fields_by_name['my_info'].message_type = _MYNODEINFO
-_FROMRADIO.fields_by_name['node_info'].message_type = _NODEINFO
-_FROMRADIO.fields_by_name['log_record'].message_type = _LOGRECORD
-_FROMRADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _FROMRADIO.fields_by_name['packet'])
-_FROMRADIO.fields_by_name['packet'].containing_oneof = _FROMRADIO.oneofs_by_name['payloadVariant']
-_FROMRADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _FROMRADIO.fields_by_name['my_info'])
-_FROMRADIO.fields_by_name['my_info'].containing_oneof = _FROMRADIO.oneofs_by_name['payloadVariant']
-_FROMRADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _FROMRADIO.fields_by_name['node_info'])
-_FROMRADIO.fields_by_name['node_info'].containing_oneof = _FROMRADIO.oneofs_by_name['payloadVariant']
-_FROMRADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _FROMRADIO.fields_by_name['log_record'])
-_FROMRADIO.fields_by_name['log_record'].containing_oneof = _FROMRADIO.oneofs_by_name['payloadVariant']
-_FROMRADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _FROMRADIO.fields_by_name['config_complete_id'])
-_FROMRADIO.fields_by_name['config_complete_id'].containing_oneof = _FROMRADIO.oneofs_by_name['payloadVariant']
-_FROMRADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _FROMRADIO.fields_by_name['rebooted'])
-_FROMRADIO.fields_by_name['rebooted'].containing_oneof = _FROMRADIO.oneofs_by_name['payloadVariant']
-_TORADIO_PEERINFO.containing_type = _TORADIO
-_TORADIO.fields_by_name['packet'].message_type = _MESHPACKET
-_TORADIO.fields_by_name['peer_info'].message_type = _TORADIO_PEERINFO
-_TORADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _TORADIO.fields_by_name['packet'])
-_TORADIO.fields_by_name['packet'].containing_oneof = _TORADIO.oneofs_by_name['payloadVariant']
-_TORADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _TORADIO.fields_by_name['peer_info'])
-_TORADIO.fields_by_name['peer_info'].containing_oneof = _TORADIO.oneofs_by_name['payloadVariant']
-_TORADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _TORADIO.fields_by_name['want_config_id'])
-_TORADIO.fields_by_name['want_config_id'].containing_oneof = _TORADIO.oneofs_by_name['payloadVariant']
-_TORADIO.oneofs_by_name['payloadVariant'].fields.append(
-  _TORADIO.fields_by_name['disconnect'])
-_TORADIO.fields_by_name['disconnect'].containing_oneof = _TORADIO.oneofs_by_name['payloadVariant']
-DESCRIPTOR.message_types_by_name['Position'] = _POSITION
-DESCRIPTOR.message_types_by_name['User'] = _USER
-DESCRIPTOR.message_types_by_name['RouteDiscovery'] = _ROUTEDISCOVERY
-DESCRIPTOR.message_types_by_name['Routing'] = _ROUTING
-DESCRIPTOR.message_types_by_name['Data'] = _DATA
-DESCRIPTOR.message_types_by_name['MeshPacket'] = _MESHPACKET
-DESCRIPTOR.message_types_by_name['NodeInfo'] = _NODEINFO
-DESCRIPTOR.message_types_by_name['MyNodeInfo'] = _MYNODEINFO
-DESCRIPTOR.message_types_by_name['LogRecord'] = _LOGRECORD
-DESCRIPTOR.message_types_by_name['FromRadio'] = _FROMRADIO
-DESCRIPTOR.message_types_by_name['ToRadio'] = _TORADIO
-DESCRIPTOR.enum_types_by_name['HardwareModel'] = _HARDWAREMODEL
-DESCRIPTOR.enum_types_by_name['Team'] = _TEAM
-DESCRIPTOR.enum_types_by_name['Constants'] = _CONSTANTS
-DESCRIPTOR.enum_types_by_name['CriticalErrorCode'] = _CRITICALERRORCODE
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-Position = _reflection.GeneratedProtocolMessageType('Position', (_message.Message,), {
-  'DESCRIPTOR' : _POSITION,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:Position)
-  })
-_sym_db.RegisterMessage(Position)
-
-User = _reflection.GeneratedProtocolMessageType('User', (_message.Message,), {
-  'DESCRIPTOR' : _USER,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:User)
-  })
-_sym_db.RegisterMessage(User)
-
-RouteDiscovery = _reflection.GeneratedProtocolMessageType('RouteDiscovery', (_message.Message,), {
-  'DESCRIPTOR' : _ROUTEDISCOVERY,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:RouteDiscovery)
-  })
-_sym_db.RegisterMessage(RouteDiscovery)
-
-Routing = _reflection.GeneratedProtocolMessageType('Routing', (_message.Message,), {
-  'DESCRIPTOR' : _ROUTING,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:Routing)
-  })
-_sym_db.RegisterMessage(Routing)
-
-Data = _reflection.GeneratedProtocolMessageType('Data', (_message.Message,), {
-  'DESCRIPTOR' : _DATA,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:Data)
-  })
-_sym_db.RegisterMessage(Data)
-
-MeshPacket = _reflection.GeneratedProtocolMessageType('MeshPacket', (_message.Message,), {
-  'DESCRIPTOR' : _MESHPACKET,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:MeshPacket)
-  })
-_sym_db.RegisterMessage(MeshPacket)
-
-NodeInfo = _reflection.GeneratedProtocolMessageType('NodeInfo', (_message.Message,), {
-  'DESCRIPTOR' : _NODEINFO,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:NodeInfo)
-  })
-_sym_db.RegisterMessage(NodeInfo)
-
-MyNodeInfo = _reflection.GeneratedProtocolMessageType('MyNodeInfo', (_message.Message,), {
-  'DESCRIPTOR' : _MYNODEINFO,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:MyNodeInfo)
-  })
-_sym_db.RegisterMessage(MyNodeInfo)
-
-LogRecord = _reflection.GeneratedProtocolMessageType('LogRecord', (_message.Message,), {
-  'DESCRIPTOR' : _LOGRECORD,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:LogRecord)
-  })
-_sym_db.RegisterMessage(LogRecord)
-
-FromRadio = _reflection.GeneratedProtocolMessageType('FromRadio', (_message.Message,), {
-  'DESCRIPTOR' : _FROMRADIO,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:FromRadio)
-  })
-_sym_db.RegisterMessage(FromRadio)
-
-ToRadio = _reflection.GeneratedProtocolMessageType('ToRadio', (_message.Message,), {
-
-  'PeerInfo' : _reflection.GeneratedProtocolMessageType('PeerInfo', (_message.Message,), {
-    'DESCRIPTOR' : _TORADIO_PEERINFO,
-    '__module__' : 'mesh_pb2'
-    # @@protoc_insertion_point(class_scope:ToRadio.PeerInfo)
-    })
-  ,
-  'DESCRIPTOR' : _TORADIO,
-  '__module__' : 'mesh_pb2'
-  # @@protoc_insertion_point(class_scope:ToRadio)
-  })
-_sym_db.RegisterMessage(ToRadio)
-_sym_db.RegisterMessage(ToRadio.PeerInfo)
-
-
-DESCRIPTOR._options = None
-_MYNODEINFO.fields_by_name['region']._options = None
-_MYNODEINFO.fields_by_name['hw_model_deprecated']._options = None
-# @@protoc_insertion_point(module_scope)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Data -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var dest
-
-

Field Data.dest

-
-
var payload
-
-

Field Data.payload

-
-
var portnum
-
-

Field Data.portnum

-
-
var request_id
-
-

Field Data.request_id

-
-
var source
-
-

Field Data.source

-
-
var want_response
-
-

Field Data.want_response

-
-
-
-
-class FromRadio -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var config_complete_id
-
-

Field FromRadio.config_complete_id

-
-
var log_record
-
-

Field FromRadio.log_record

-
-
var my_info
-
-

Field FromRadio.my_info

-
-
var node_info
-
-

Field FromRadio.node_info

-
-
var num
-
-

Field FromRadio.num

-
-
var packet
-
-

Field FromRadio.packet

-
-
var rebooted
-
-

Field FromRadio.rebooted

-
-
-
-
-class LogRecord -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var CRITICAL
-
-
-
-
var DEBUG
-
-
-
-
var DESCRIPTOR
-
-
-
-
var ERROR
-
-
-
-
var INFO
-
-
-
-
var Level
-
-
-
-
var TRACE
-
-
-
-
var UNSET
-
-
-
-
var WARNING
-
-
-
-
-

Instance variables

-
-
var level
-
-

Field LogRecord.level

-
-
var message
-
-

Field LogRecord.message

-
-
var source
-
-

Field LogRecord.source

-
-
var time
-
-

Field LogRecord.time

-
-
-
-
-class MeshPacket -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var ACK
-
-
-
-
var BACKGROUND
-
-
-
-
var DEFAULT
-
-
-
-
var DESCRIPTOR
-
-
-
-
var MAX
-
-
-
-
var MIN
-
-
-
-
var Priority
-
-
-
-
var RELIABLE
-
-
-
-
var UNSET
-
-
-
-
-

Instance variables

-
-
var channel
-
-

Field MeshPacket.channel

-
-
var decoded
-
-

Field MeshPacket.decoded

-
-
var encrypted
-
-

Field MeshPacket.encrypted

-
-
var from
-
-

Field MeshPacket.from

-
-
var hop_limit
-
-

Field MeshPacket.hop_limit

-
-
var id
-
-

Field MeshPacket.id

-
-
var priority
-
-

Field MeshPacket.priority

-
-
var rx_rssi
-
-

Field MeshPacket.rx_rssi

-
-
var rx_snr
-
-

Field MeshPacket.rx_snr

-
-
var rx_time
-
-

Field MeshPacket.rx_time

-
-
var to
-
-

Field MeshPacket.to

-
-
var want_ack
-
-

Field MeshPacket.want_ack

-
-
-
-
-class MyNodeInfo -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var air_period_rx
-
-

Field MyNodeInfo.air_period_rx

-
-
var air_period_tx
-
-

Field MyNodeInfo.air_period_tx

-
-
var bitrate
-
-

Field MyNodeInfo.bitrate

-
-
var error_address
-
-

Field MyNodeInfo.error_address

-
-
var error_code
-
-

Field MyNodeInfo.error_code

-
-
var error_count
-
-

Field MyNodeInfo.error_count

-
-
var firmware_version
-
-

Field MyNodeInfo.firmware_version

-
-
var has_gps
-
-

Field MyNodeInfo.has_gps

-
-
var hw_model_deprecated
-
-

Field MyNodeInfo.hw_model_deprecated

-
-
var max_channels
-
-

Field MyNodeInfo.max_channels

-
-
var message_timeout_msec
-
-

Field MyNodeInfo.message_timeout_msec

-
-
var min_app_version
-
-

Field MyNodeInfo.min_app_version

-
-
var my_node_num
-
-

Field MyNodeInfo.my_node_num

-
-
var num_bands
-
-

Field MyNodeInfo.num_bands

-
-
var reboot_count
-
-

Field MyNodeInfo.reboot_count

-
-
var region
-
-

Field MyNodeInfo.region

-
-
-
-
-class NodeInfo -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var last_heard
-
-

Field NodeInfo.last_heard

-
-
var num
-
-

Field NodeInfo.num

-
-
var position
-
-

Field NodeInfo.position

-
-
var snr
-
-

Field NodeInfo.snr

-
-
var user
-
-

Field NodeInfo.user

-
-
-
-
-class Position -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var ALTSRC_BAROMETRIC
-
-
-
-
var ALTSRC_GPS_EXTERNAL
-
-
-
-
var ALTSRC_GPS_INTERNAL
-
-
-
-
var ALTSRC_MANUAL_ENTRY
-
-
-
-
var ALTSRC_UNSPECIFIED
-
-
-
-
var AltSource
-
-
-
-
var DESCRIPTOR
-
-
-
-
var LOCSRC_GPS_EXTERNAL
-
-
-
-
var LOCSRC_GPS_INTERNAL
-
-
-
-
var LOCSRC_MANUAL_ENTRY
-
-
-
-
var LOCSRC_UNSPECIFIED
-
-
-
-
var LocSource
-
-
-
-
-

Instance variables

-
-
var HDOP
-
-

Field Position.HDOP

-
-
var PDOP
-
-

Field Position.PDOP

-
-
var VDOP
-
-

Field Position.VDOP

-
-
var alt_geoid_sep
-
-

Field Position.alt_geoid_sep

-
-
var altitude
-
-

Field Position.altitude

-
-
var altitude_hae
-
-

Field Position.altitude_hae

-
-
var altitude_source
-
-

Field Position.altitude_source

-
-
var battery_level
-
-

Field Position.battery_level

-
-
var fix_quality
-
-

Field Position.fix_quality

-
-
var fix_type
-
-

Field Position.fix_type

-
-
var gps_accuracy
-
-

Field Position.gps_accuracy

-
-
var ground_speed
-
-

Field Position.ground_speed

-
-
var ground_track
-
-

Field Position.ground_track

-
-
var latitude_i
-
-

Field Position.latitude_i

-
-
var location_source
-
-

Field Position.location_source

-
-
var longitude_i
-
-

Field Position.longitude_i

-
-
var pos_next_update
-
-

Field Position.pos_next_update

-
-
var pos_seq_number
-
-

Field Position.pos_seq_number

-
-
var pos_time_millis
-
-

Field Position.pos_time_millis

-
-
var pos_timestamp
-
-

Field Position.pos_timestamp

-
-
var sats_in_view
-
-

Field Position.sats_in_view

-
-
var sensor_id
-
-

Field Position.sensor_id

-
-
var time
-
-

Field Position.time

-
-
-
-
-class RouteDiscovery -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var route
-
-

Field RouteDiscovery.route

-
-
-
-
-class Routing -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var BAD_REQUEST
-
-
-
-
var DESCRIPTOR
-
-
-
-
var Error
-
-
-
-
var GOT_NAK
-
-
-
-
var MAX_RETRANSMIT
-
-
-
-
var NONE
-
-
-
-
var NOT_AUTHORIZED
-
-
-
-
var NO_CHANNEL
-
-
-
-
var NO_INTERFACE
-
-
-
-
var NO_RESPONSE
-
-
-
-
var NO_ROUTE
-
-
-
-
var TIMEOUT
-
-
-
-
var TOO_LARGE
-
-
-
-
-

Instance variables

-
-
var error_reason
-
-

Field Routing.error_reason

-
-
var route_reply
-
-

Field Routing.route_reply

-
-
var route_request
-
-

Field Routing.route_request

-
-
-
-
-class ToRadio -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var PeerInfo
-
-

A ProtocolMessage

-
-
-

Instance variables

-
-
var disconnect
-
-

Field ToRadio.disconnect

-
-
var packet
-
-

Field ToRadio.packet

-
-
var peer_info
-
-

Field ToRadio.peer_info

-
-
var want_config_id
-
-

Field ToRadio.want_config_id

-
-
-
-
-class User -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var ant_azimuth
-
-

Field User.ant_azimuth

-
-
var ant_gain_dbi
-
-

Field User.ant_gain_dbi

-
-
var hw_model
-
-

Field User.hw_model

-
-
var id
-
-

Field User.id

-
-
var is_licensed
-
-

Field User.is_licensed

-
-
var long_name
-
-

Field User.long_name

-
-
var macaddr
-
-

Field User.macaddr

-
-
var short_name
-
-

Field User.short_name

-
-
var team
-
-

Field User.team

-
-
var tx_power_dbm
-
-

Field User.tx_power_dbm

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/mqtt_pb2.html b/docs/meshtastic/mqtt_pb2.html deleted file mode 100644 index d4062a2..0000000 --- a/docs/meshtastic/mqtt_pb2.html +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - -meshtastic.mqtt_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.mqtt_pb2

-
-
-
- -Expand source code - -
# -*- 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()
-
-
-from . 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)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class ServiceEnvelope -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var channel_id
-
-

Field ServiceEnvelope.channel_id

-
-
var gateway_id
-
-

Field ServiceEnvelope.gateway_id

-
-
var packet
-
-

Field ServiceEnvelope.packet

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/node.html b/docs/meshtastic/node.html deleted file mode 100644 index f0dd5e1..0000000 --- a/docs/meshtastic/node.html +++ /dev/null @@ -1,1429 +0,0 @@ - - - - - - -meshtastic.node API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.node

-
-
-

Node class

-
- -Expand source code - -
"""Node class
-"""
-
-import logging
-import base64
-from google.protobuf.json_format import MessageToJson
-from . import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
-from .util import pskToString, stripnl, Timeout, our_exit, fromPSK
-
-
-
-
-class Node:
-    """A model of a (local or remote) node in the mesh
-
-    Includes methods for radioConfig and channels
-    """
-
-    def __init__(self, iface, nodeNum, noProto=False):
-        """Constructor"""
-        self.iface = iface
-        self.nodeNum = nodeNum
-        self.radioConfig = None
-        self.channels = None
-        self._timeout = Timeout(maxSecs=300)
-        self.partialChannels = None
-        self.noProto = noProto
-
-    def showChannels(self):
-        """Show human readable description of our channels."""
-        print("Channels:")
-        if self.channels:
-            logging.debug(f'self.channels:{self.channels}')
-            for c in self.channels:
-                #print('c.settings.psk:', c.settings.psk)
-                cStr = stripnl(MessageToJson(c.settings))
-                # only show if there is no psk (meaning disabled channel)
-                if c.settings.psk:
-                    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"""
-        prefs = ""
-        if self.radioConfig and self.radioConfig.preferences:
-            prefs = stripnl(MessageToJson(self.radioConfig.preferences))
-        print(f"Preferences: {prefs}\n")
-        self.showChannels()
-
-    def requestConfig(self):
-        """Send regular MeshPackets to ask for settings and channels."""
-        logging.debug(f"requestConfig for nodeNum:{self.nodeNum}")
-        self.radioConfig = None
-        self.channels = None
-        self.partialChannels = []  # We keep our channels in a temp array until finished
-
-        self._requestSettings()
-
-    def turnOffEncryptionOnPrimaryChannel(self):
-        """Turn off encryption on primary channel."""
-        self.channels[0].settings.psk = fromPSK("none")
-        print("Writing modified channels to device")
-        self.writeChannel(0)
-
-    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 is None:
-            our_exit("Error: 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 getChannelByChannelIndex(self, channelIndex):
-        """Get channel by channelIndex
-            channelIndex: number, typically 0-7; based on max number channels
-            returns: None if there is no channel found
-        """
-        ch = None
-        if self.channels and 0 <= channelIndex < len(self.channels):
-            ch = self.channels[channelIndex]
-        return ch
-
-    def deleteChannel(self, channelIndex):
-        """Delete the specifed channelIndex and shift other channels up"""
-        ch = self.channels[channelIndex]
-        if ch.role not in (channel_pb2.Channel.Role.SECONDARY, channel_pb2.Channel.Role.DISABLED):
-            our_exit("Warning: 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 written 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=None, short_name=None, is_licensed=False, team=None):
-        """Set device owner name"""
-        logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
-        nChars = 3
-        minChars = 2
-        if long_name is not None:
-            long_name = long_name.strip()
-            if short_name is None:
-                words = long_name.split()
-                if len(long_name) <= nChars:
-                    short_name = long_name
-                elif len(words) >= minChars:
-                    short_name = ''.join(map(lambda word: word[0], words))
-                else:
-                    trans = str.maketrans(dict.fromkeys('aeiouAEIOU'))
-                    short_name = long_name[0] + long_name[1:].translate(trans)
-                    if len(short_name) < nChars:
-                        short_name = long_name[:nChars]
-
-        p = admin_pb2.AdminMessage()
-
-        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
-        if team is not None:
-            p.set_owner.team = team
-
-        # Note: These debug lines are used in unit tests
-        logging.debug(f'p.set_owner.long_name:{p.set_owner.long_name}:')
-        logging.debug(f'p.set_owner.short_name:{p.set_owner.short_name}:')
-        logging.debug(f'p.set_owner.is_licensed:{p.set_owner.is_licensed}')
-        logging.debug(f'p.set_owner.team:{p.set_owner.team}')
-        return self._sendAdmin(p)
-
-    def getURL(self, includeAll: bool = True):
-        """The sharable URL that describes the current channel"""
-        # Only keep the primary/secondary channels, assume primary is first
-        channelSet = apponly_pb2.ChannelSet()
-        if self.channels:
-            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)
-        some_bytes = channelSet.SerializeToString()
-        s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
-        return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
-
-    def setURL(self, url):
-        """Set mesh network URL"""
-        if self.radioConfig is None:
-            our_exit("Warning: 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)
-
-
-        if len(channelSet.settings) == 0:
-            our_exit("Warning: There were no settings.")
-
-        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
-            logging.debug(f'Channel i:{i} ch:{ch}')
-            self.writeChannel(ch.index)
-            i = i + 1
-
-
-    def onResponseRequestSettings(self, p):
-        """Handle the response packet for requesting settings _requestSettings()"""
-        logging.debug(f'onResponseRequestSetting() p:{p}')
-        errorFound = False
-        if 'routing' in p["decoded"]:
-            if p["decoded"]["routing"]["errorReason"] != "NONE":
-                errorFound = True
-                print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
-        if errorFound is False:
-            self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
-            logging.debug(f'self.radioConfig:{self.radioConfig}')
-            logging.debug("Received radio config, now fetching channels...")
-            self._timeout.reset()  # We made foreward progress
-            self._requestChannel(0)  # now start fetching channels
-
-
-    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
-
-        # Show progress message for super slow operations
-        if self != self.iface.localNode:
-            print("Requesting preferences from remote node.")
-            print("Be sure:")
-            print(" 1. There is a SECONDARY channel named 'admin'.")
-            print(" 2. The '--seturl' was used to configure.")
-            print(" 3. All devices have the same modem config. (i.e., '--ch-longfast')")
-            print(" 4. All devices have been rebooted after all of the above. (optional, but recommended)")
-            print("Note: This could take a while (it requests remote channel configs, then writes config)")
-
-        return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings)
-
-    def exitSimulator(self):
-        """Tell a simulator node to exit (this message
-           is ignored for other nodes)"""
-        p = admin_pb2.AdminMessage()
-        p.exit_simulator = True
-        logging.debug('in exitSimulator()')
-
-        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
-        # TODO: These 2 lines seem to not do anything.
-        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 onResponseRequestChannel(self, p):
-        """Handle the response packet for requesting a channel _requestChannel()"""
-        logging.debug(f'onResponseRequestChannel() p:{p}')
-        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 everyone else we are ready to go
-        else:
-            self._requestChannel(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:
-            print(f"Requesting channel {channelNum} info from remote node (this could take a while)")
-            logging.debug(f"Requesting channel {channelNum} info from remote node (this could take a while)")
-        else:
-            logging.debug(f"Requesting channel {channelNum}")
-
-        return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestChannel)
-
-
-    # pylint: disable=R1710
-    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 self.noProto:
-            logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-        else:
-            if adminIndex == 0:  # unless a special channel index was used, we want to use the admin index
-                adminIndex = self.iface.localNode._getAdminChannelIndex()
-            logging.debug(f'adminIndex:{adminIndex}')
-
-            return self.iface.sendData(p, self.nodeNum,
-                                       portNum=portnums_pb2.PortNum.ADMIN_APP,
-                                       wantAck=True,
-                                       wantResponse=wantResponse,
-                                       onResponse=onResponse,
-                                       channelIndex=adminIndex)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Node -(iface, nodeNum, noProto=False) -
-
-

A model of a (local or remote) node in the mesh

-

Includes methods for radioConfig and channels

-

Constructor

-
- -Expand source code - -
class Node:
-    """A model of a (local or remote) node in the mesh
-
-    Includes methods for radioConfig and channels
-    """
-
-    def __init__(self, iface, nodeNum, noProto=False):
-        """Constructor"""
-        self.iface = iface
-        self.nodeNum = nodeNum
-        self.radioConfig = None
-        self.channels = None
-        self._timeout = Timeout(maxSecs=300)
-        self.partialChannels = None
-        self.noProto = noProto
-
-    def showChannels(self):
-        """Show human readable description of our channels."""
-        print("Channels:")
-        if self.channels:
-            logging.debug(f'self.channels:{self.channels}')
-            for c in self.channels:
-                #print('c.settings.psk:', c.settings.psk)
-                cStr = stripnl(MessageToJson(c.settings))
-                # only show if there is no psk (meaning disabled channel)
-                if c.settings.psk:
-                    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"""
-        prefs = ""
-        if self.radioConfig and self.radioConfig.preferences:
-            prefs = stripnl(MessageToJson(self.radioConfig.preferences))
-        print(f"Preferences: {prefs}\n")
-        self.showChannels()
-
-    def requestConfig(self):
-        """Send regular MeshPackets to ask for settings and channels."""
-        logging.debug(f"requestConfig for nodeNum:{self.nodeNum}")
-        self.radioConfig = None
-        self.channels = None
-        self.partialChannels = []  # We keep our channels in a temp array until finished
-
-        self._requestSettings()
-
-    def turnOffEncryptionOnPrimaryChannel(self):
-        """Turn off encryption on primary channel."""
-        self.channels[0].settings.psk = fromPSK("none")
-        print("Writing modified channels to device")
-        self.writeChannel(0)
-
-    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 is None:
-            our_exit("Error: 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 getChannelByChannelIndex(self, channelIndex):
-        """Get channel by channelIndex
-            channelIndex: number, typically 0-7; based on max number channels
-            returns: None if there is no channel found
-        """
-        ch = None
-        if self.channels and 0 <= channelIndex < len(self.channels):
-            ch = self.channels[channelIndex]
-        return ch
-
-    def deleteChannel(self, channelIndex):
-        """Delete the specifed channelIndex and shift other channels up"""
-        ch = self.channels[channelIndex]
-        if ch.role not in (channel_pb2.Channel.Role.SECONDARY, channel_pb2.Channel.Role.DISABLED):
-            our_exit("Warning: 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 written 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=None, short_name=None, is_licensed=False, team=None):
-        """Set device owner name"""
-        logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
-        nChars = 3
-        minChars = 2
-        if long_name is not None:
-            long_name = long_name.strip()
-            if short_name is None:
-                words = long_name.split()
-                if len(long_name) <= nChars:
-                    short_name = long_name
-                elif len(words) >= minChars:
-                    short_name = ''.join(map(lambda word: word[0], words))
-                else:
-                    trans = str.maketrans(dict.fromkeys('aeiouAEIOU'))
-                    short_name = long_name[0] + long_name[1:].translate(trans)
-                    if len(short_name) < nChars:
-                        short_name = long_name[:nChars]
-
-        p = admin_pb2.AdminMessage()
-
-        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
-        if team is not None:
-            p.set_owner.team = team
-
-        # Note: These debug lines are used in unit tests
-        logging.debug(f'p.set_owner.long_name:{p.set_owner.long_name}:')
-        logging.debug(f'p.set_owner.short_name:{p.set_owner.short_name}:')
-        logging.debug(f'p.set_owner.is_licensed:{p.set_owner.is_licensed}')
-        logging.debug(f'p.set_owner.team:{p.set_owner.team}')
-        return self._sendAdmin(p)
-
-    def getURL(self, includeAll: bool = True):
-        """The sharable URL that describes the current channel"""
-        # Only keep the primary/secondary channels, assume primary is first
-        channelSet = apponly_pb2.ChannelSet()
-        if self.channels:
-            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)
-        some_bytes = channelSet.SerializeToString()
-        s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
-        return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
-
-    def setURL(self, url):
-        """Set mesh network URL"""
-        if self.radioConfig is None:
-            our_exit("Warning: 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)
-
-
-        if len(channelSet.settings) == 0:
-            our_exit("Warning: There were no settings.")
-
-        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
-            logging.debug(f'Channel i:{i} ch:{ch}')
-            self.writeChannel(ch.index)
-            i = i + 1
-
-
-    def onResponseRequestSettings(self, p):
-        """Handle the response packet for requesting settings _requestSettings()"""
-        logging.debug(f'onResponseRequestSetting() p:{p}')
-        errorFound = False
-        if 'routing' in p["decoded"]:
-            if p["decoded"]["routing"]["errorReason"] != "NONE":
-                errorFound = True
-                print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
-        if errorFound is False:
-            self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
-            logging.debug(f'self.radioConfig:{self.radioConfig}')
-            logging.debug("Received radio config, now fetching channels...")
-            self._timeout.reset()  # We made foreward progress
-            self._requestChannel(0)  # now start fetching channels
-
-
-    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
-
-        # Show progress message for super slow operations
-        if self != self.iface.localNode:
-            print("Requesting preferences from remote node.")
-            print("Be sure:")
-            print(" 1. There is a SECONDARY channel named 'admin'.")
-            print(" 2. The '--seturl' was used to configure.")
-            print(" 3. All devices have the same modem config. (i.e., '--ch-longfast')")
-            print(" 4. All devices have been rebooted after all of the above. (optional, but recommended)")
-            print("Note: This could take a while (it requests remote channel configs, then writes config)")
-
-        return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings)
-
-    def exitSimulator(self):
-        """Tell a simulator node to exit (this message
-           is ignored for other nodes)"""
-        p = admin_pb2.AdminMessage()
-        p.exit_simulator = True
-        logging.debug('in exitSimulator()')
-
-        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
-        # TODO: These 2 lines seem to not do anything.
-        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 onResponseRequestChannel(self, p):
-        """Handle the response packet for requesting a channel _requestChannel()"""
-        logging.debug(f'onResponseRequestChannel() p:{p}')
-        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 everyone else we are ready to go
-        else:
-            self._requestChannel(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:
-            print(f"Requesting channel {channelNum} info from remote node (this could take a while)")
-            logging.debug(f"Requesting channel {channelNum} info from remote node (this could take a while)")
-        else:
-            logging.debug(f"Requesting channel {channelNum}")
-
-        return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestChannel)
-
-
-    # pylint: disable=R1710
-    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 self.noProto:
-            logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-        else:
-            if adminIndex == 0:  # unless a special channel index was used, we want to use the admin index
-                adminIndex = self.iface.localNode._getAdminChannelIndex()
-            logging.debug(f'adminIndex:{adminIndex}')
-
-            return self.iface.sendData(p, self.nodeNum,
-                                       portNum=portnums_pb2.PortNum.ADMIN_APP,
-                                       wantAck=True,
-                                       wantResponse=wantResponse,
-                                       onResponse=onResponse,
-                                       channelIndex=adminIndex)
-
-

Methods

-
-
-def _fillChannels(self) -
-
-

Mark unused channels as disabled

-
- -Expand source code - -
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 _fixupChannels(self) -
-
-

Fixup indexes and add disabled channels as needed

-
- -Expand source code - -
def _fixupChannels(self):
-    """Fixup indexes and add disabled channels as needed"""
-
-    # Add extra disabled channels as needed
-    # TODO: These 2 lines seem to not do anything.
-    for index, ch in enumerate(self.channels):
-        ch.index = index  # fixup indexes
-
-    self._fillChannels()
-
-
-
-def _getAdminChannelIndex(self) -
-
-

Return the channel number of the admin channel, or 0 if no reserved channel

-
- -Expand source code - -
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 _requestChannel(self, channelNum: int) -
-
-

Done with initial config messages, now send regular -MeshPackets to ask for settings

-
- -Expand source code - -
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:
-        print(f"Requesting channel {channelNum} info from remote node (this could take a while)")
-        logging.debug(f"Requesting channel {channelNum} info from remote node (this could take a while)")
-    else:
-        logging.debug(f"Requesting channel {channelNum}")
-
-    return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestChannel)
-
-
-
-def _requestSettings(self) -
-
-

Done with initial config messages, now send regular -MeshPackets to ask for settings.

-
- -Expand source code - -
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
-
-    # Show progress message for super slow operations
-    if self != self.iface.localNode:
-        print("Requesting preferences from remote node.")
-        print("Be sure:")
-        print(" 1. There is a SECONDARY channel named 'admin'.")
-        print(" 2. The '--seturl' was used to configure.")
-        print(" 3. All devices have the same modem config. (i.e., '--ch-longfast')")
-        print(" 4. All devices have been rebooted after all of the above. (optional, but recommended)")
-        print("Note: This could take a while (it requests remote channel configs, then writes config)")
-
-    return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings)
-
-
-
-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)

-
- -Expand source code - -
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 self.noProto:
-        logging.warning(f"Not sending packet because protocol use is disabled by noProto")
-    else:
-        if adminIndex == 0:  # unless a special channel index was used, we want to use the admin index
-            adminIndex = self.iface.localNode._getAdminChannelIndex()
-        logging.debug(f'adminIndex:{adminIndex}')
-
-        return self.iface.sendData(p, self.nodeNum,
-                                   portNum=portnums_pb2.PortNum.ADMIN_APP,
-                                   wantAck=True,
-                                   wantResponse=wantResponse,
-                                   onResponse=onResponse,
-                                   channelIndex=adminIndex)
-
-
-
-def deleteChannel(self, channelIndex) -
-
-

Delete the specifed channelIndex and shift other channels up

-
- -Expand source code - -
def deleteChannel(self, channelIndex):
-    """Delete the specifed channelIndex and shift other channels up"""
-    ch = self.channels[channelIndex]
-    if ch.role not in (channel_pb2.Channel.Role.SECONDARY, channel_pb2.Channel.Role.DISABLED):
-        our_exit("Warning: 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 written it), so we can start finding it by name again
-            adminIndex = 0
-
-
-
-def exitSimulator(self) -
-
-

Tell a simulator node to exit (this message -is ignored for other nodes)

-
- -Expand source code - -
def exitSimulator(self):
-    """Tell a simulator node to exit (this message
-       is ignored for other nodes)"""
-    p = admin_pb2.AdminMessage()
-    p.exit_simulator = True
-    logging.debug('in exitSimulator()')
-
-    return self._sendAdmin(p)
-
-
-
-def getChannelByChannelIndex(self, channelIndex) -
-
-

Get channel by channelIndex -channelIndex: number, typically 0-7; based on max number channels -returns: None if there is no channel found

-
- -Expand source code - -
def getChannelByChannelIndex(self, channelIndex):
-    """Get channel by channelIndex
-        channelIndex: number, typically 0-7; based on max number channels
-        returns: None if there is no channel found
-    """
-    ch = None
-    if self.channels and 0 <= channelIndex < len(self.channels):
-        ch = self.channels[channelIndex]
-    return ch
-
-
-
-def getChannelByName(self, name) -
-
-

Try to find the named channel or return None

-
- -Expand source code - -
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)

-
- -Expand source code - -
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 getURL(self, includeAll: bool = True) -
-
-

The sharable URL that describes the current channel

-
- -Expand source code - -
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()
-    if self.channels:
-        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)
-    some_bytes = channelSet.SerializeToString()
-    s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
-    return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
-
-
-
-def onResponseRequestChannel(self, p) -
-
-

Handle the response packet for requesting a channel _requestChannel()

-
- -Expand source code - -
def onResponseRequestChannel(self, p):
-    """Handle the response packet for requesting a channel _requestChannel()"""
-    logging.debug(f'onResponseRequestChannel() p:{p}')
-    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 everyone else we are ready to go
-    else:
-        self._requestChannel(index + 1)
-
-
-
-def onResponseRequestSettings(self, p) -
-
-

Handle the response packet for requesting settings _requestSettings()

-
- -Expand source code - -
def onResponseRequestSettings(self, p):
-    """Handle the response packet for requesting settings _requestSettings()"""
-    logging.debug(f'onResponseRequestSetting() p:{p}')
-    errorFound = False
-    if 'routing' in p["decoded"]:
-        if p["decoded"]["routing"]["errorReason"] != "NONE":
-            errorFound = True
-            print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
-    if errorFound is False:
-        self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
-        logging.debug(f'self.radioConfig:{self.radioConfig}')
-        logging.debug("Received radio config, now fetching channels...")
-        self._timeout.reset()  # We made foreward progress
-        self._requestChannel(0)  # now start fetching channels
-
-
-
-def reboot(self, secs: int = 10) -
-
-

Tell the node to reboot.

-
- -Expand source code - -
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 requestConfig(self) -
-
-

Send regular MeshPackets to ask for settings and channels.

-
- -Expand source code - -
def requestConfig(self):
-    """Send regular MeshPackets to ask for settings and channels."""
-    logging.debug(f"requestConfig for nodeNum:{self.nodeNum}")
-    self.radioConfig = None
-    self.channels = None
-    self.partialChannels = []  # We keep our channels in a temp array until finished
-
-    self._requestSettings()
-
-
-
-def setOwner(self, long_name=None, short_name=None, is_licensed=False, team=None) -
-
-

Set device owner name

-
- -Expand source code - -
def setOwner(self, long_name=None, short_name=None, is_licensed=False, team=None):
-    """Set device owner name"""
-    logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
-    nChars = 3
-    minChars = 2
-    if long_name is not None:
-        long_name = long_name.strip()
-        if short_name is None:
-            words = long_name.split()
-            if len(long_name) <= nChars:
-                short_name = long_name
-            elif len(words) >= minChars:
-                short_name = ''.join(map(lambda word: word[0], words))
-            else:
-                trans = str.maketrans(dict.fromkeys('aeiouAEIOU'))
-                short_name = long_name[0] + long_name[1:].translate(trans)
-                if len(short_name) < nChars:
-                    short_name = long_name[:nChars]
-
-    p = admin_pb2.AdminMessage()
-
-    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
-    if team is not None:
-        p.set_owner.team = team
-
-    # Note: These debug lines are used in unit tests
-    logging.debug(f'p.set_owner.long_name:{p.set_owner.long_name}:')
-    logging.debug(f'p.set_owner.short_name:{p.set_owner.short_name}:')
-    logging.debug(f'p.set_owner.is_licensed:{p.set_owner.is_licensed}')
-    logging.debug(f'p.set_owner.team:{p.set_owner.team}')
-    return self._sendAdmin(p)
-
-
-
-def setURL(self, url) -
-
-

Set mesh network URL

-
- -Expand source code - -
def setURL(self, url):
-    """Set mesh network URL"""
-    if self.radioConfig is None:
-        our_exit("Warning: 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)
-
-
-    if len(channelSet.settings) == 0:
-        our_exit("Warning: There were no settings.")
-
-    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
-        logging.debug(f'Channel i:{i} ch:{ch}')
-        self.writeChannel(ch.index)
-        i = i + 1
-
-
-
-def showChannels(self) -
-
-

Show human readable description of our channels.

-
- -Expand source code - -
def showChannels(self):
-    """Show human readable description of our channels."""
-    print("Channels:")
-    if self.channels:
-        logging.debug(f'self.channels:{self.channels}')
-        for c in self.channels:
-            #print('c.settings.psk:', c.settings.psk)
-            cStr = stripnl(MessageToJson(c.settings))
-            # only show if there is no psk (meaning disabled channel)
-            if c.settings.psk:
-                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

-
- -Expand source code - -
def showInfo(self):
-    """Show human readable description of our node"""
-    prefs = ""
-    if self.radioConfig and self.radioConfig.preferences:
-        prefs = stripnl(MessageToJson(self.radioConfig.preferences))
-    print(f"Preferences: {prefs}\n")
-    self.showChannels()
-
-
-
-def turnOffEncryptionOnPrimaryChannel(self) -
-
-

Turn off encryption on primary channel.

-
- -Expand source code - -
def turnOffEncryptionOnPrimaryChannel(self):
-    """Turn off encryption on primary channel."""
-    self.channels[0].settings.psk = fromPSK("none")
-    print("Writing modified channels to device")
-    self.writeChannel(0)
-
-
-
-def waitForConfig(self) -
-
-

Block until radio config is received. Returns True if config has been received.

-
- -Expand source code - -
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 writeChannel(self, channelIndex, adminIndex=0) -
-
-

Write the current (edited) channel to the device

-
- -Expand source code - -
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 writeConfig(self) -
-
-

Write the current (edited) radioConfig to the device

-
- -Expand source code - -
def writeConfig(self):
-    """Write the current (edited) radioConfig to the device"""
-    if self.radioConfig is None:
-        our_exit("Error: No RadioConfig has been read")
-
-    p = admin_pb2.AdminMessage()
-    p.set_radio.CopyFrom(self.radioConfig)
-
-    self._sendAdmin(p)
-    logging.debug("Wrote config")
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/portnums_pb2.html b/docs/meshtastic/portnums_pb2.html deleted file mode 100644 index 1d705b6..0000000 --- a/docs/meshtastic/portnums_pb2.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - -meshtastic.portnums_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.portnums_pb2

-
-
-
- -Expand source code - -
# -*- 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)
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/radioconfig_pb2.html b/docs/meshtastic/radioconfig_pb2.html deleted file mode 100644 index 6efa03d..0000000 --- a/docs/meshtastic/radioconfig_pb2.html +++ /dev/null @@ -1,1042 +0,0 @@ - - - - - - -meshtastic.radioconfig_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.radioconfig_pb2

-
-
-
- -Expand source code - -
# -*- coding: utf-8 -*-
-# Generated by the protocol buffer compiler.  DO NOT EDIT!
-# source: radioconfig.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='radioconfig.proto',
-  package='',
-  syntax='proto3',
-  serialized_options=b'\n\023com.geeksville.meshB\021RadioConfigProtosH\003Z!github.com/meshtastic/gomeshproto',
-  serialized_pb=b'\n\x11radioconfig.proto\"\xa3\x13\n\x0bRadioConfig\x12\x31\n\x0bpreferences\x18\x01 \x01(\x0b\x32\x1c.RadioConfig.UserPreferences\x1a\xe0\x12\n\x0fUserPreferences\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12 \n\x18position_broadcast_smart\x18\x11 \x01(\x08\x12\x1b\n\x13send_owner_interval\x18\x02 \x01(\r\x12\x1b\n\x13wait_bluetooth_secs\x18\x04 \x01(\r\x12\x16\n\x0escreen_on_secs\x18\x05 \x01(\r\x12\x1a\n\x12phone_timeout_secs\x18\x06 \x01(\r\x12\x1d\n\x15phone_sds_timeout_sec\x18\x07 \x01(\r\x12\x1d\n\x15mesh_sds_timeout_secs\x18\x08 \x01(\r\x12\x10\n\x08sds_secs\x18\t \x01(\r\x12\x0f\n\x07ls_secs\x18\n \x01(\r\x12\x15\n\rmin_wake_secs\x18\x0b \x01(\r\x12\x11\n\twifi_ssid\x18\x0c \x01(\t\x12\x15\n\rwifi_password\x18\r \x01(\t\x12\x14\n\x0cwifi_ap_mode\x18\x0e \x01(\x08\x12\x1b\n\x06region\x18\x0f \x01(\x0e\x32\x0b.RegionCode\x12&\n\x0e\x63harge_current\x18\x10 \x01(\x0e\x32\x0e.ChargeCurrent\x12\x11\n\tis_router\x18% \x01(\x08\x12\x14\n\x0cis_low_power\x18& \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\' \x01(\x08\x12\x17\n\x0fserial_disabled\x18( \x01(\x08\x12(\n\x0elocation_share\x18  \x01(\x0e\x32\x10.LocationSharing\x12$\n\rgps_operation\x18! \x01(\x0e\x32\r.GpsOperation\x12\x1b\n\x13gps_update_interval\x18\" \x01(\r\x12\x18\n\x10gps_attempt_time\x18$ \x01(\r\x12\x15\n\rgps_accept_2d\x18- \x01(\x08\x12\x13\n\x0bgps_max_dop\x18. \x01(\r\x12\x18\n\x10\x66requency_offset\x18) \x01(\x02\x12\x13\n\x0bmqtt_server\x18* \x01(\t\x12\x15\n\rmqtt_disabled\x18+ \x01(\x08\x12(\n\ngps_format\x18, \x01(\x0e\x32\x14.GpsCoordinateFormat\x12\x15\n\rfactory_reset\x18\x64 \x01(\x08\x12\x19\n\x11\x64\x65\x62ug_log_enabled\x18\x65 \x01(\x08\x12\x17\n\x0fignore_incoming\x18g \x03(\r\x12\x1c\n\x14serialplugin_enabled\x18x \x01(\x08\x12\x19\n\x11serialplugin_echo\x18y \x01(\x08\x12\x18\n\x10serialplugin_rxd\x18z \x01(\r\x12\x18\n\x10serialplugin_txd\x18{ \x01(\r\x12\x1c\n\x14serialplugin_timeout\x18| \x01(\r\x12\x19\n\x11serialplugin_mode\x18} \x01(\r\x12\'\n\x1f\x65xt_notification_plugin_enabled\x18~ \x01(\x08\x12)\n!ext_notification_plugin_output_ms\x18\x7f \x01(\r\x12\'\n\x1e\x65xt_notification_plugin_output\x18\x80\x01 \x01(\r\x12\'\n\x1e\x65xt_notification_plugin_active\x18\x81\x01 \x01(\x08\x12.\n%ext_notification_plugin_alert_message\x18\x82\x01 \x01(\x08\x12+\n\"ext_notification_plugin_alert_bell\x18\x83\x01 \x01(\x08\x12\"\n\x19range_test_plugin_enabled\x18\x84\x01 \x01(\x08\x12!\n\x18range_test_plugin_sender\x18\x85\x01 \x01(\r\x12\x1f\n\x16range_test_plugin_save\x18\x86\x01 \x01(\x08\x12%\n\x1cstore_forward_plugin_enabled\x18\x94\x01 \x01(\x08\x12\'\n\x1estore_forward_plugin_heartbeat\x18\x95\x01 \x01(\x08\x12%\n\x1cstore_forward_plugin_records\x18\x89\x01 \x01(\r\x12\x30\n\'store_forward_plugin_history_return_max\x18\x8a\x01 \x01(\r\x12\x33\n*store_forward_plugin_history_return_window\x18\x8b\x01 \x01(\r\x12=\n4environmental_measurement_plugin_measurement_enabled\x18\x8c\x01 \x01(\x08\x12\x38\n/environmental_measurement_plugin_screen_enabled\x18\x8d\x01 \x01(\x08\x12\x44\n;environmental_measurement_plugin_read_error_count_threshold\x18\x8e\x01 \x01(\r\x12\x39\n0environmental_measurement_plugin_update_interval\x18\x8f\x01 \x01(\r\x12;\n2environmental_measurement_plugin_recovery_interval\x18\x90\x01 \x01(\r\x12;\n2environmental_measurement_plugin_display_farenheit\x18\x91\x01 \x01(\x08\x12v\n,environmental_measurement_plugin_sensor_type\x18\x92\x01 \x01(\x0e\x32?.RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType\x12\x34\n+environmental_measurement_plugin_sensor_pin\x18\x93\x01 \x01(\r\x12\x17\n\x0eposition_flags\x18\x96\x01 \x01(\r\x12\x1a\n\x11is_always_powered\x18\x97\x01 \x01(\x08\x12\"\n\x19\x61uto_screen_carousel_secs\x18\x98\x01 \x01(\r\x12\'\n\x1eon_battery_shutdown_after_secs\x18\x99\x01 \x01(\r\"<\n\"EnvironmentalMeasurementSensorType\x12\t\n\x05\x44HT11\x10\x00\x12\x0b\n\x07\x44S18B20\x10\x01J\x06\x08\x88\x01\x10\x89\x01*f\n\nRegionCode\x12\t\n\x05Unset\x10\x00\x12\x06\n\x02US\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x45U865\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08\x12\x06\n\x02RU\x10\t*\xd1\x01\n\rChargeCurrent\x12\x0b\n\x07MAUnset\x10\x00\x12\t\n\x05MA100\x10\x01\x12\t\n\x05MA190\x10\x02\x12\t\n\x05MA280\x10\x03\x12\t\n\x05MA360\x10\x04\x12\t\n\x05MA450\x10\x05\x12\t\n\x05MA550\x10\x06\x12\t\n\x05MA630\x10\x07\x12\t\n\x05MA700\x10\x08\x12\t\n\x05MA780\x10\t\x12\t\n\x05MA880\x10\n\x12\t\n\x05MA960\x10\x0b\x12\n\n\x06MA1000\x10\x0c\x12\n\n\x06MA1080\x10\r\x12\n\n\x06MA1160\x10\x0e\x12\n\n\x06MA1240\x10\x0f\x12\n\n\x06MA1320\x10\x10*j\n\x0cGpsOperation\x12\x0e\n\nGpsOpUnset\x10\x00\x12\x13\n\x0fGpsOpStationary\x10\x01\x12\x0f\n\x0bGpsOpMobile\x10\x02\x12\x11\n\rGpsOpTimeOnly\x10\x03\x12\x11\n\rGpsOpDisabled\x10\x04*\x83\x01\n\x13GpsCoordinateFormat\x12\x10\n\x0cGpsFormatDec\x10\x00\x12\x10\n\x0cGpsFormatDMS\x10\x01\x12\x10\n\x0cGpsFormatUTM\x10\x02\x12\x11\n\rGpsFormatMGRS\x10\x03\x12\x10\n\x0cGpsFormatOLC\x10\x04\x12\x11\n\rGpsFormatOSGR\x10\x05*@\n\x0fLocationSharing\x12\x0c\n\x08LocUnset\x10\x00\x12\x0e\n\nLocEnabled\x10\x01\x12\x0f\n\x0bLocDisabled\x10\x02*\xbc\x01\n\rPositionFlags\x12\x11\n\rPOS_UNDEFINED\x10\x00\x12\x10\n\x0cPOS_ALTITUDE\x10\x01\x12\x0f\n\x0bPOS_ALT_MSL\x10\x02\x12\x0f\n\x0bPOS_GEO_SEP\x10\x04\x12\x0b\n\x07POS_DOP\x10\x08\x12\r\n\tPOS_HVDOP\x10\x10\x12\x0f\n\x0bPOS_BATTERY\x10 \x12\x11\n\rPOS_SATINVIEW\x10@\x12\x10\n\x0bPOS_SEQ_NOS\x10\x80\x01\x12\x12\n\rPOS_TIMESTAMP\x10\x80\x02\x42M\n\x13\x63om.geeksville.meshB\x11RadioConfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
-)
-
-_REGIONCODE = _descriptor.EnumDescriptor(
-  name='RegionCode',
-  full_name='RegionCode',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='Unset', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='US', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='EU433', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='EU865', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CN', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='JP', index=5, number=5,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ANZ', index=6, number=6,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='KR', index=7, number=7,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TW', index=8, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='RU', index=9, number=9,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2491,
-  serialized_end=2593,
-)
-_sym_db.RegisterEnumDescriptor(_REGIONCODE)
-
-RegionCode = enum_type_wrapper.EnumTypeWrapper(_REGIONCODE)
-_CHARGECURRENT = _descriptor.EnumDescriptor(
-  name='ChargeCurrent',
-  full_name='ChargeCurrent',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='MAUnset', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA100', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA190', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA280', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA360', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA450', index=5, number=5,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA550', index=6, number=6,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA630', index=7, number=7,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA700', index=8, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA780', index=9, number=9,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA880', index=10, number=10,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA960', index=11, number=11,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA1000', index=12, number=12,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA1080', index=13, number=13,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA1160', index=14, number=14,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA1240', index=15, number=15,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='MA1320', index=16, number=16,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2596,
-  serialized_end=2805,
-)
-_sym_db.RegisterEnumDescriptor(_CHARGECURRENT)
-
-ChargeCurrent = enum_type_wrapper.EnumTypeWrapper(_CHARGECURRENT)
-_GPSOPERATION = _descriptor.EnumDescriptor(
-  name='GpsOperation',
-  full_name='GpsOperation',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='GpsOpUnset', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsOpStationary', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsOpMobile', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsOpTimeOnly', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsOpDisabled', index=4, number=4,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2807,
-  serialized_end=2913,
-)
-_sym_db.RegisterEnumDescriptor(_GPSOPERATION)
-
-GpsOperation = enum_type_wrapper.EnumTypeWrapper(_GPSOPERATION)
-_GPSCOORDINATEFORMAT = _descriptor.EnumDescriptor(
-  name='GpsCoordinateFormat',
-  full_name='GpsCoordinateFormat',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='GpsFormatDec', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsFormatDMS', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsFormatUTM', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsFormatMGRS', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsFormatOLC', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GpsFormatOSGR', index=5, number=5,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2916,
-  serialized_end=3047,
-)
-_sym_db.RegisterEnumDescriptor(_GPSCOORDINATEFORMAT)
-
-GpsCoordinateFormat = enum_type_wrapper.EnumTypeWrapper(_GPSCOORDINATEFORMAT)
-_LOCATIONSHARING = _descriptor.EnumDescriptor(
-  name='LocationSharing',
-  full_name='LocationSharing',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='LocUnset', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LocEnabled', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LocDisabled', index=2, number=2,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3049,
-  serialized_end=3113,
-)
-_sym_db.RegisterEnumDescriptor(_LOCATIONSHARING)
-
-LocationSharing = enum_type_wrapper.EnumTypeWrapper(_LOCATIONSHARING)
-_POSITIONFLAGS = _descriptor.EnumDescriptor(
-  name='PositionFlags',
-  full_name='PositionFlags',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='POS_UNDEFINED', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_ALTITUDE', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_ALT_MSL', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_GEO_SEP', index=3, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_DOP', index=4, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_HVDOP', index=5, number=16,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_BATTERY', index=6, number=32,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_SATINVIEW', index=7, number=64,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_SEQ_NOS', index=8, number=128,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='POS_TIMESTAMP', index=9, number=256,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3116,
-  serialized_end=3304,
-)
-_sym_db.RegisterEnumDescriptor(_POSITIONFLAGS)
-
-PositionFlags = enum_type_wrapper.EnumTypeWrapper(_POSITIONFLAGS)
-Unset = 0
-US = 1
-EU433 = 2
-EU865 = 3
-CN = 4
-JP = 5
-ANZ = 6
-KR = 7
-TW = 8
-RU = 9
-MAUnset = 0
-MA100 = 1
-MA190 = 2
-MA280 = 3
-MA360 = 4
-MA450 = 5
-MA550 = 6
-MA630 = 7
-MA700 = 8
-MA780 = 9
-MA880 = 10
-MA960 = 11
-MA1000 = 12
-MA1080 = 13
-MA1160 = 14
-MA1240 = 15
-MA1320 = 16
-GpsOpUnset = 0
-GpsOpStationary = 1
-GpsOpMobile = 2
-GpsOpTimeOnly = 3
-GpsOpDisabled = 4
-GpsFormatDec = 0
-GpsFormatDMS = 1
-GpsFormatUTM = 2
-GpsFormatMGRS = 3
-GpsFormatOLC = 4
-GpsFormatOSGR = 5
-LocUnset = 0
-LocEnabled = 1
-LocDisabled = 2
-POS_UNDEFINED = 0
-POS_ALTITUDE = 1
-POS_ALT_MSL = 2
-POS_GEO_SEP = 4
-POS_DOP = 8
-POS_HVDOP = 16
-POS_BATTERY = 32
-POS_SATINVIEW = 64
-POS_SEQ_NOS = 128
-POS_TIMESTAMP = 256
-
-
-_RADIOCONFIG_USERPREFERENCES_ENVIRONMENTALMEASUREMENTSENSORTYPE = _descriptor.EnumDescriptor(
-  name='EnvironmentalMeasurementSensorType',
-  full_name='RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='DHT11', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DS18B20', index=1, number=1,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2421,
-  serialized_end=2481,
-)
-_sym_db.RegisterEnumDescriptor(_RADIOCONFIG_USERPREFERENCES_ENVIRONMENTALMEASUREMENTSENSORTYPE)
-
-
-_RADIOCONFIG_USERPREFERENCES = _descriptor.Descriptor(
-  name='UserPreferences',
-  full_name='RadioConfig.UserPreferences',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='position_broadcast_secs', full_name='RadioConfig.UserPreferences.position_broadcast_secs', 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='position_broadcast_smart', full_name='RadioConfig.UserPreferences.position_broadcast_smart', index=1,
-      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),
-    _descriptor.FieldDescriptor(
-      name='send_owner_interval', full_name='RadioConfig.UserPreferences.send_owner_interval', index=2,
-      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='wait_bluetooth_secs', full_name='RadioConfig.UserPreferences.wait_bluetooth_secs', 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='screen_on_secs', full_name='RadioConfig.UserPreferences.screen_on_secs', 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='phone_timeout_secs', full_name='RadioConfig.UserPreferences.phone_timeout_secs', 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='phone_sds_timeout_sec', full_name='RadioConfig.UserPreferences.phone_sds_timeout_sec', index=6,
-      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='mesh_sds_timeout_secs', full_name='RadioConfig.UserPreferences.mesh_sds_timeout_secs', 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='sds_secs', full_name='RadioConfig.UserPreferences.sds_secs', 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),
-    _descriptor.FieldDescriptor(
-      name='ls_secs', full_name='RadioConfig.UserPreferences.ls_secs', index=9,
-      number=10, 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='min_wake_secs', full_name='RadioConfig.UserPreferences.min_wake_secs', index=10,
-      number=11, 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='wifi_ssid', full_name='RadioConfig.UserPreferences.wifi_ssid', index=11,
-      number=12, 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='wifi_password', full_name='RadioConfig.UserPreferences.wifi_password', index=12,
-      number=13, 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='wifi_ap_mode', full_name='RadioConfig.UserPreferences.wifi_ap_mode', index=13,
-      number=14, 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='region', full_name='RadioConfig.UserPreferences.region', index=14,
-      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),
-    _descriptor.FieldDescriptor(
-      name='charge_current', full_name='RadioConfig.UserPreferences.charge_current', index=15,
-      number=16, 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='is_router', full_name='RadioConfig.UserPreferences.is_router', index=16,
-      number=37, 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='is_low_power', full_name='RadioConfig.UserPreferences.is_low_power', index=17,
-      number=38, 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='fixed_position', full_name='RadioConfig.UserPreferences.fixed_position', index=18,
-      number=39, 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='serial_disabled', full_name='RadioConfig.UserPreferences.serial_disabled', index=19,
-      number=40, 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='location_share', full_name='RadioConfig.UserPreferences.location_share', index=20,
-      number=32, 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='gps_operation', full_name='RadioConfig.UserPreferences.gps_operation', index=21,
-      number=33, 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='gps_update_interval', full_name='RadioConfig.UserPreferences.gps_update_interval', index=22,
-      number=34, 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='gps_attempt_time', full_name='RadioConfig.UserPreferences.gps_attempt_time', index=23,
-      number=36, 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='gps_accept_2d', full_name='RadioConfig.UserPreferences.gps_accept_2d', index=24,
-      number=45, 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='gps_max_dop', full_name='RadioConfig.UserPreferences.gps_max_dop', index=25,
-      number=46, 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='frequency_offset', full_name='RadioConfig.UserPreferences.frequency_offset', index=26,
-      number=41, 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='mqtt_server', full_name='RadioConfig.UserPreferences.mqtt_server', index=27,
-      number=42, 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='mqtt_disabled', full_name='RadioConfig.UserPreferences.mqtt_disabled', index=28,
-      number=43, 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='gps_format', full_name='RadioConfig.UserPreferences.gps_format', index=29,
-      number=44, 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='factory_reset', full_name='RadioConfig.UserPreferences.factory_reset', index=30,
-      number=100, 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='debug_log_enabled', full_name='RadioConfig.UserPreferences.debug_log_enabled', index=31,
-      number=101, 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='ignore_incoming', full_name='RadioConfig.UserPreferences.ignore_incoming', index=32,
-      number=103, type=13, cpp_type=3, 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='serialplugin_enabled', full_name='RadioConfig.UserPreferences.serialplugin_enabled', index=33,
-      number=120, 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='serialplugin_echo', full_name='RadioConfig.UserPreferences.serialplugin_echo', index=34,
-      number=121, 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='serialplugin_rxd', full_name='RadioConfig.UserPreferences.serialplugin_rxd', index=35,
-      number=122, 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='serialplugin_txd', full_name='RadioConfig.UserPreferences.serialplugin_txd', index=36,
-      number=123, 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='serialplugin_timeout', full_name='RadioConfig.UserPreferences.serialplugin_timeout', index=37,
-      number=124, 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='serialplugin_mode', full_name='RadioConfig.UserPreferences.serialplugin_mode', index=38,
-      number=125, 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='ext_notification_plugin_enabled', full_name='RadioConfig.UserPreferences.ext_notification_plugin_enabled', index=39,
-      number=126, 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='ext_notification_plugin_output_ms', full_name='RadioConfig.UserPreferences.ext_notification_plugin_output_ms', index=40,
-      number=127, 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='ext_notification_plugin_output', full_name='RadioConfig.UserPreferences.ext_notification_plugin_output', index=41,
-      number=128, 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='ext_notification_plugin_active', full_name='RadioConfig.UserPreferences.ext_notification_plugin_active', index=42,
-      number=129, 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='ext_notification_plugin_alert_message', full_name='RadioConfig.UserPreferences.ext_notification_plugin_alert_message', index=43,
-      number=130, 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='ext_notification_plugin_alert_bell', full_name='RadioConfig.UserPreferences.ext_notification_plugin_alert_bell', index=44,
-      number=131, 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='range_test_plugin_enabled', full_name='RadioConfig.UserPreferences.range_test_plugin_enabled', index=45,
-      number=132, 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='range_test_plugin_sender', full_name='RadioConfig.UserPreferences.range_test_plugin_sender', index=46,
-      number=133, 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='range_test_plugin_save', full_name='RadioConfig.UserPreferences.range_test_plugin_save', index=47,
-      number=134, 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='store_forward_plugin_enabled', full_name='RadioConfig.UserPreferences.store_forward_plugin_enabled', index=48,
-      number=148, 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='store_forward_plugin_heartbeat', full_name='RadioConfig.UserPreferences.store_forward_plugin_heartbeat', index=49,
-      number=149, 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='store_forward_plugin_records', full_name='RadioConfig.UserPreferences.store_forward_plugin_records', index=50,
-      number=137, 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='store_forward_plugin_history_return_max', full_name='RadioConfig.UserPreferences.store_forward_plugin_history_return_max', index=51,
-      number=138, 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='store_forward_plugin_history_return_window', full_name='RadioConfig.UserPreferences.store_forward_plugin_history_return_window', index=52,
-      number=139, 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='environmental_measurement_plugin_measurement_enabled', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_measurement_enabled', index=53,
-      number=140, 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='environmental_measurement_plugin_screen_enabled', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_screen_enabled', index=54,
-      number=141, 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='environmental_measurement_plugin_read_error_count_threshold', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_read_error_count_threshold', index=55,
-      number=142, 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='environmental_measurement_plugin_update_interval', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_update_interval', index=56,
-      number=143, 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='environmental_measurement_plugin_recovery_interval', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_recovery_interval', index=57,
-      number=144, 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='environmental_measurement_plugin_display_farenheit', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_display_farenheit', index=58,
-      number=145, 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='environmental_measurement_plugin_sensor_type', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_sensor_type', index=59,
-      number=146, 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='environmental_measurement_plugin_sensor_pin', full_name='RadioConfig.UserPreferences.environmental_measurement_plugin_sensor_pin', index=60,
-      number=147, 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='position_flags', full_name='RadioConfig.UserPreferences.position_flags', index=61,
-      number=150, 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='is_always_powered', full_name='RadioConfig.UserPreferences.is_always_powered', index=62,
-      number=151, 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='auto_screen_carousel_secs', full_name='RadioConfig.UserPreferences.auto_screen_carousel_secs', index=63,
-      number=152, 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='on_battery_shutdown_after_secs', full_name='RadioConfig.UserPreferences.on_battery_shutdown_after_secs', index=64,
-      number=153, 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=[
-    _RADIOCONFIG_USERPREFERENCES_ENVIRONMENTALMEASUREMENTSENSORTYPE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=89,
-  serialized_end=2489,
-)
-
-_RADIOCONFIG = _descriptor.Descriptor(
-  name='RadioConfig',
-  full_name='RadioConfig',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='preferences', full_name='RadioConfig.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=[_RADIOCONFIG_USERPREFERENCES, ],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=22,
-  serialized_end=2489,
-)
-
-_RADIOCONFIG_USERPREFERENCES.fields_by_name['region'].enum_type = _REGIONCODE
-_RADIOCONFIG_USERPREFERENCES.fields_by_name['charge_current'].enum_type = _CHARGECURRENT
-_RADIOCONFIG_USERPREFERENCES.fields_by_name['location_share'].enum_type = _LOCATIONSHARING
-_RADIOCONFIG_USERPREFERENCES.fields_by_name['gps_operation'].enum_type = _GPSOPERATION
-_RADIOCONFIG_USERPREFERENCES.fields_by_name['gps_format'].enum_type = _GPSCOORDINATEFORMAT
-_RADIOCONFIG_USERPREFERENCES.fields_by_name['environmental_measurement_plugin_sensor_type'].enum_type = _RADIOCONFIG_USERPREFERENCES_ENVIRONMENTALMEASUREMENTSENSORTYPE
-_RADIOCONFIG_USERPREFERENCES.containing_type = _RADIOCONFIG
-_RADIOCONFIG_USERPREFERENCES_ENVIRONMENTALMEASUREMENTSENSORTYPE.containing_type = _RADIOCONFIG_USERPREFERENCES
-_RADIOCONFIG.fields_by_name['preferences'].message_type = _RADIOCONFIG_USERPREFERENCES
-DESCRIPTOR.message_types_by_name['RadioConfig'] = _RADIOCONFIG
-DESCRIPTOR.enum_types_by_name['RegionCode'] = _REGIONCODE
-DESCRIPTOR.enum_types_by_name['ChargeCurrent'] = _CHARGECURRENT
-DESCRIPTOR.enum_types_by_name['GpsOperation'] = _GPSOPERATION
-DESCRIPTOR.enum_types_by_name['GpsCoordinateFormat'] = _GPSCOORDINATEFORMAT
-DESCRIPTOR.enum_types_by_name['LocationSharing'] = _LOCATIONSHARING
-DESCRIPTOR.enum_types_by_name['PositionFlags'] = _POSITIONFLAGS
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-RadioConfig = _reflection.GeneratedProtocolMessageType('RadioConfig', (_message.Message,), {
-
-  'UserPreferences' : _reflection.GeneratedProtocolMessageType('UserPreferences', (_message.Message,), {
-    'DESCRIPTOR' : _RADIOCONFIG_USERPREFERENCES,
-    '__module__' : 'radioconfig_pb2'
-    # @@protoc_insertion_point(class_scope:RadioConfig.UserPreferences)
-    })
-  ,
-  'DESCRIPTOR' : _RADIOCONFIG,
-  '__module__' : 'radioconfig_pb2'
-  # @@protoc_insertion_point(class_scope:RadioConfig)
-  })
-_sym_db.RegisterMessage(RadioConfig)
-_sym_db.RegisterMessage(RadioConfig.UserPreferences)
-
-
-DESCRIPTOR._options = None
-# @@protoc_insertion_point(module_scope)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class RadioConfig -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var UserPreferences
-
-

A ProtocolMessage

-
-
-

Instance variables

-
-
var preferences
-
-

Field RadioConfig.preferences

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/remote_hardware.html b/docs/meshtastic/remote_hardware.html deleted file mode 100644 index ffe3be4..0000000 --- a/docs/meshtastic/remote_hardware.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - -meshtastic.remote_hardware API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.remote_hardware

-
-
-

Remote hardware

-
- -Expand source code - -
"""Remote hardware
-"""
-import logging
-from pubsub import pub
-from . import portnums_pb2, remote_hardware_pb2
-from .util import our_exit
-
-
-def onGPIOreceive(packet, interface):
-    """Callback for received GPIO responses
-    """
-    logging.debug(f"packet:{packet} interface:{interface}")
-    hw = packet["decoded"]["remotehw"]
-    gpioValue = hw["gpioValue"]
-    #print(f'mask:{interface.mask}')
-    value = int(gpioValue) & int(interface.mask)
-    print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')
-    interface.gotResponse = True
-
-
-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:
-            our_exit(
-                "Warning: No channel named 'gpio' was found.\n"\
-                "On the sending and receive nodes create a channel named 'gpio'.\n"\
-                "For example, run '--ch-add gpio' on one device, then '--seturl' on\n"\
-                "the other devices using the url from the device where the channel was added.")
-        self.channelIndex = ch.index
-
-        pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw")
-
-    def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
-        if not nodeid:
-            our_exit(r"Warning: Must use 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
-        """
-        logging.debug(f'writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}')
-        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"""
-        logging.debug(f'readGPIOs nodeid:{nodeid} mask:{mask}')
-        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"""
-        logging.debug(f'watchGPIOs nodeid:{nodeid} mask:{mask}')
-        r = remote_hardware_pb2.HardwareMessage()
-        r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
-        r.gpio_mask = mask
-        self.iface.mask = mask
-        return self._sendHardware(nodeid, r)
-
-
-
-
-
-
-
-

Functions

-
-
-def onGPIOreceive(packet, interface) -
-
-

Callback for received GPIO responses

-
- -Expand source code - -
def onGPIOreceive(packet, interface):
-    """Callback for received GPIO responses
-    """
-    logging.debug(f"packet:{packet} interface:{interface}")
-    hw = packet["decoded"]["remotehw"]
-    gpioValue = hw["gpioValue"]
-    #print(f'mask:{interface.mask}')
-    value = int(gpioValue) & int(interface.mask)
-    print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')
-    interface.gotResponse = True
-
-
-
-
-
-

Classes

-
-
-class RemoteHardwareClient -(iface) -
-
-

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

-

Constructor

-

iface is the already open MeshInterface instance

-
- -Expand source code - -
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:
-            our_exit(
-                "Warning: No channel named 'gpio' was found.\n"\
-                "On the sending and receive nodes create a channel named 'gpio'.\n"\
-                "For example, run '--ch-add gpio' on one device, then '--seturl' on\n"\
-                "the other devices using the url from the device where the channel was added.")
-        self.channelIndex = ch.index
-
-        pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw")
-
-    def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
-        if not nodeid:
-            our_exit(r"Warning: Must use 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
-        """
-        logging.debug(f'writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}')
-        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"""
-        logging.debug(f'readGPIOs nodeid:{nodeid} mask:{mask}')
-        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"""
-        logging.debug(f'watchGPIOs nodeid:{nodeid} mask:{mask}')
-        r = remote_hardware_pb2.HardwareMessage()
-        r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
-        r.gpio_mask = mask
-        self.iface.mask = mask
-        return self._sendHardware(nodeid, r)
-
-

Methods

-
-
-def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None) -
-
-
-
- -Expand source code - -
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
-    if not nodeid:
-        our_exit(r"Warning: Must use 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 readGPIOs(self, nodeid, mask, onResponse=None) -
-
-

Read the specified bits from GPIO inputs on the device

-
- -Expand source code - -
def readGPIOs(self, nodeid, mask, onResponse = None):
-    """Read the specified bits from GPIO inputs on the device"""
-    logging.debug(f'readGPIOs nodeid:{nodeid} mask:{mask}')
-    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

-
- -Expand source code - -
def watchGPIOs(self, nodeid, mask):
-    """Watch the specified bits from GPIO inputs on the device for changes"""
-    logging.debug(f'watchGPIOs nodeid:{nodeid} mask:{mask}')
-    r = remote_hardware_pb2.HardwareMessage()
-    r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
-    r.gpio_mask = mask
-    self.iface.mask = mask
-    return self._sendHardware(nodeid, r)
-
-
-
-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

-
- -Expand source code - -
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
-    """
-    logging.debug(f'writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}')
-    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)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/remote_hardware_pb2.html b/docs/meshtastic/remote_hardware_pb2.html deleted file mode 100644 index 48bd4f0..0000000 --- a/docs/meshtastic/remote_hardware_pb2.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - -meshtastic.remote_hardware_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.remote_hardware_pb2

-
-
-
- -Expand source code - -
# -*- 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)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class HardwareMessage -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var GPIOS_CHANGED
-
-
-
-
var READ_GPIOS
-
-
-
-
var READ_GPIOS_REPLY
-
-
-
-
var Type
-
-
-
-
var UNSET
-
-
-
-
var WATCH_GPIOS
-
-
-
-
var WRITE_GPIOS
-
-
-
-
-

Instance variables

-
-
var gpio_mask
-
-

Field HardwareMessage.gpio_mask

-
-
var gpio_value
-
-

Field HardwareMessage.gpio_value

-
-
var typ
-
-

Field HardwareMessage.typ

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/serial_interface.html b/docs/meshtastic/serial_interface.html deleted file mode 100644 index 101094d..0000000 --- a/docs/meshtastic/serial_interface.html +++ /dev/null @@ -1,323 +0,0 @@ - - - - - - -meshtastic.serial_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.serial_interface

-
-
-

Serial interface class

-
- -Expand source code - -
""" Serial interface class
-"""
-import logging
-import platform
-import os
-import stat
-import serial
-
-import meshtastic.util
-from .stream_interface import StreamInterface
-
-class SerialInterface(StreamInterface):
-    """Interface class for meshtastic devices over a serial link"""
-
-    def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
-        """Constructor, opens a connection to a specified serial port, or if unspecified try to
-        find one Meshtastic device by probing
-
-        Keyword Arguments:
-            devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
-            debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
-        """
-
-        if devPath is None:
-            ports = meshtastic.util.findPorts()
-            logging.debug(f"ports:{ports}")
-            if len(ports) == 0:
-                meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
-            elif len(ports) > 1:
-                message = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
-                message += f"  Ports detected:{ports}"
-                meshtastic.util.our_exit(message)
-            else:
-                devPath = ports[0]
-
-        logging.debug(f"Connecting to {devPath}")
-
-        # Note: we provide None for port here, because we will be opening it later
-        self.stream = serial.Serial(
-            None, 921600, exclusive=True, timeout=0.5, write_timeout=0)
-
-        # rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
-        self.stream.port = devPath
-
-        # HACK: If the platform driving the serial port is unable to leave the RTS pin in high-impedance
-        # mode, set RTS to false so that the device platform won't be reset spuriously.
-        # Linux does this properly, so don't apply this hack on Linux (because it makes the reset button not work).
-        if self._hostPlatformAlwaysDrivesUartRts():
-            self.stream.rts = False
-        self.stream.open()
-
-        StreamInterface.__init__(
-            self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
-
-    """true if platform driving the serial port is Windows Subsystem for Linux 1."""
-    def _isWsl1(self):
-        # WSL1 identifies itself as Linux, but has a special char device at /dev/lxss for use with session control,
-        # e.g. /init.  We should treat WSL1 as Windows for the RTS-driving hack because the underlying platfrom
-        # serial driver for the CP21xx still exhibits the buggy behavior.
-        # WSL2 is not covered here, as it does not (as of 2021-May-25) support the appropriate functionality to
-        # share or pass-through serial ports.
-        try:
-            # Claims to be Linux, but has /dev/lxss; must be WSL 1
-            return platform.system() == 'Linux' and stat.S_ISCHR(os.stat('/dev/lxss').st_mode)
-        except:
-            # Couldn't stat /dev/lxss special device; not WSL1
-            return False
-
-    def _hostPlatformAlwaysDrivesUartRts(self):
-        # OS-X/Windows seems to have a bug in its CP21xx serial drivers.  It ignores that we asked for no RTSCTS
-        # control and will always drive RTS either high or low (rather than letting the CP102 leave
-        # it as an open-collector floating pin).
-        # TODO: When WSL2 supports USB passthrough, this will get messier.  If/when WSL2 gets virtual serial
-        # ports that "share" the Windows serial port (and thus the Windows drivers), this code will need to be
-        # updated to reflect that as well -- or if T-Beams get made with an alternate USB to UART bridge that has
-        # a less buggy driver.
-        return platform.system() != 'Linux' or self._isWsl1()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class SerialInterface -(devPath=None, debugOut=None, noProto=False, connectNow=True) -
-
-

Interface class for meshtastic devices over a serial link

-

Constructor, opens a connection to a specified serial port, or if unspecified try to -find one Meshtastic device by probing

-

Keyword Arguments: -devPath {string} – A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) -debugOut {stream} – If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})

-
- -Expand source code - -
class SerialInterface(StreamInterface):
-    """Interface class for meshtastic devices over a serial link"""
-
-    def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
-        """Constructor, opens a connection to a specified serial port, or if unspecified try to
-        find one Meshtastic device by probing
-
-        Keyword Arguments:
-            devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
-            debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
-        """
-
-        if devPath is None:
-            ports = meshtastic.util.findPorts()
-            logging.debug(f"ports:{ports}")
-            if len(ports) == 0:
-                meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
-            elif len(ports) > 1:
-                message = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
-                message += f"  Ports detected:{ports}"
-                meshtastic.util.our_exit(message)
-            else:
-                devPath = ports[0]
-
-        logging.debug(f"Connecting to {devPath}")
-
-        # Note: we provide None for port here, because we will be opening it later
-        self.stream = serial.Serial(
-            None, 921600, exclusive=True, timeout=0.5, write_timeout=0)
-
-        # rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
-        self.stream.port = devPath
-
-        # HACK: If the platform driving the serial port is unable to leave the RTS pin in high-impedance
-        # mode, set RTS to false so that the device platform won't be reset spuriously.
-        # Linux does this properly, so don't apply this hack on Linux (because it makes the reset button not work).
-        if self._hostPlatformAlwaysDrivesUartRts():
-            self.stream.rts = False
-        self.stream.open()
-
-        StreamInterface.__init__(
-            self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
-
-    """true if platform driving the serial port is Windows Subsystem for Linux 1."""
-    def _isWsl1(self):
-        # WSL1 identifies itself as Linux, but has a special char device at /dev/lxss for use with session control,
-        # e.g. /init.  We should treat WSL1 as Windows for the RTS-driving hack because the underlying platfrom
-        # serial driver for the CP21xx still exhibits the buggy behavior.
-        # WSL2 is not covered here, as it does not (as of 2021-May-25) support the appropriate functionality to
-        # share or pass-through serial ports.
-        try:
-            # Claims to be Linux, but has /dev/lxss; must be WSL 1
-            return platform.system() == 'Linux' and stat.S_ISCHR(os.stat('/dev/lxss').st_mode)
-        except:
-            # Couldn't stat /dev/lxss special device; not WSL1
-            return False
-
-    def _hostPlatformAlwaysDrivesUartRts(self):
-        # OS-X/Windows seems to have a bug in its CP21xx serial drivers.  It ignores that we asked for no RTSCTS
-        # control and will always drive RTS either high or low (rather than letting the CP102 leave
-        # it as an open-collector floating pin).
-        # TODO: When WSL2 supports USB passthrough, this will get messier.  If/when WSL2 gets virtual serial
-        # ports that "share" the Windows serial port (and thus the Windows drivers), this code will need to be
-        # updated to reflect that as well -- or if T-Beams get made with an alternate USB to UART bridge that has
-        # a less buggy driver.
-        return platform.system() != 'Linux' or self._isWsl1()
-
-

Ancestors

- -

Methods

-
-
-def _hostPlatformAlwaysDrivesUartRts(self) -
-
-
-
- -Expand source code - -
def _hostPlatformAlwaysDrivesUartRts(self):
-    # OS-X/Windows seems to have a bug in its CP21xx serial drivers.  It ignores that we asked for no RTSCTS
-    # control and will always drive RTS either high or low (rather than letting the CP102 leave
-    # it as an open-collector floating pin).
-    # TODO: When WSL2 supports USB passthrough, this will get messier.  If/when WSL2 gets virtual serial
-    # ports that "share" the Windows serial port (and thus the Windows drivers), this code will need to be
-    # updated to reflect that as well -- or if T-Beams get made with an alternate USB to UART bridge that has
-    # a less buggy driver.
-    return platform.system() != 'Linux' or self._isWsl1()
-
-
-
-def _isWsl1(self) -
-
-
-
- -Expand source code - -
def _isWsl1(self):
-    # WSL1 identifies itself as Linux, but has a special char device at /dev/lxss for use with session control,
-    # e.g. /init.  We should treat WSL1 as Windows for the RTS-driving hack because the underlying platfrom
-    # serial driver for the CP21xx still exhibits the buggy behavior.
-    # WSL2 is not covered here, as it does not (as of 2021-May-25) support the appropriate functionality to
-    # share or pass-through serial ports.
-    try:
-        # Claims to be Linux, but has /dev/lxss; must be WSL 1
-        return platform.system() == 'Linux' and stat.S_ISCHR(os.stat('/dev/lxss').st_mode)
-    except:
-        # Couldn't stat /dev/lxss special device; not WSL1
-        return False
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/storeforward_pb2.html b/docs/meshtastic/storeforward_pb2.html deleted file mode 100644 index b895c26..0000000 --- a/docs/meshtastic/storeforward_pb2.html +++ /dev/null @@ -1,465 +0,0 @@ - - - - - - -meshtastic.storeforward_pb2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.storeforward_pb2

-
-
-
- -Expand source code - -
# -*- 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\025StoreAndForwardProtosH\003Z!github.com/meshtastic/gomeshproto',
-  serialized_pb=b'\n\x12storeforward.proto\"\xe7\x04\n\x0fStoreAndForward\x12,\n\x02rr\x18\x01 \x01(\x0e\x32 .StoreAndForward.RequestResponse\x12*\n\x05stats\x18\x02 \x01(\x0b\x32\x1b.StoreAndForward.Statistics\x12)\n\x07history\x18\x03 \x01(\x0b\x32\x18.StoreAndForward.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\"\xd1\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\x10iBQ\n\x13\x63om.geeksville.meshB\x15StoreAndForwardProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
-)
-
-
-
-_STOREANDFORWARD_REQUESTRESPONSE = _descriptor.EnumDescriptor(
-  name='RequestResponse',
-  full_name='StoreAndForward.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),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=429,
-  serialized_end=638,
-)
-_sym_db.RegisterEnumDescriptor(_STOREANDFORWARD_REQUESTRESPONSE)
-
-
-_STOREANDFORWARD_STATISTICS = _descriptor.Descriptor(
-  name='Statistics',
-  full_name='StoreAndForward.Statistics',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='MessagesTotal', full_name='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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=176,
-  serialized_end=374,
-)
-
-_STOREANDFORWARD_HISTORY = _descriptor.Descriptor(
-  name='History',
-  full_name='StoreAndForward.History',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='HistoryMessages', full_name='StoreAndForward.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='StoreAndForward.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=376,
-  serialized_end=426,
-)
-
-_STOREANDFORWARD = _descriptor.Descriptor(
-  name='StoreAndForward',
-  full_name='StoreAndForward',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='rr', full_name='StoreAndForward.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='StoreAndForward.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='StoreAndForward.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=[_STOREANDFORWARD_STATISTICS, _STOREANDFORWARD_HISTORY, ],
-  enum_types=[
-    _STOREANDFORWARD_REQUESTRESPONSE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=23,
-  serialized_end=638,
-)
-
-_STOREANDFORWARD_STATISTICS.containing_type = _STOREANDFORWARD
-_STOREANDFORWARD_HISTORY.containing_type = _STOREANDFORWARD
-_STOREANDFORWARD.fields_by_name['rr'].enum_type = _STOREANDFORWARD_REQUESTRESPONSE
-_STOREANDFORWARD.fields_by_name['stats'].message_type = _STOREANDFORWARD_STATISTICS
-_STOREANDFORWARD.fields_by_name['history'].message_type = _STOREANDFORWARD_HISTORY
-_STOREANDFORWARD_REQUESTRESPONSE.containing_type = _STOREANDFORWARD
-DESCRIPTOR.message_types_by_name['StoreAndForward'] = _STOREANDFORWARD
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-StoreAndForward = _reflection.GeneratedProtocolMessageType('StoreAndForward', (_message.Message,), {
-
-  'Statistics' : _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), {
-    'DESCRIPTOR' : _STOREANDFORWARD_STATISTICS,
-    '__module__' : 'storeforward_pb2'
-    # @@protoc_insertion_point(class_scope:StoreAndForward.Statistics)
-    })
-  ,
-
-  'History' : _reflection.GeneratedProtocolMessageType('History', (_message.Message,), {
-    'DESCRIPTOR' : _STOREANDFORWARD_HISTORY,
-    '__module__' : 'storeforward_pb2'
-    # @@protoc_insertion_point(class_scope:StoreAndForward.History)
-    })
-  ,
-  'DESCRIPTOR' : _STOREANDFORWARD,
-  '__module__' : 'storeforward_pb2'
-  # @@protoc_insertion_point(class_scope:StoreAndForward)
-  })
-_sym_db.RegisterMessage(StoreAndForward)
-_sym_db.RegisterMessage(StoreAndForward.Statistics)
-_sym_db.RegisterMessage(StoreAndForward.History)
-
-
-DESCRIPTOR._options = None
-# @@protoc_insertion_point(module_scope)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class StoreAndForward -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var CLIENT_ERROR
-
-
-
-
var CLIENT_HISTORY
-
-
-
-
var CLIENT_PING
-
-
-
-
var CLIENT_PONG
-
-
-
-
var CLIENT_STATS
-
-
-
-
var DESCRIPTOR
-
-
-
-
var History
-
-

A ProtocolMessage

-
-
var ROUTER_BUSY
-
-
-
-
var ROUTER_ERROR
-
-
-
-
var ROUTER_HEARTBEAT
-
-
-
-
var ROUTER_PING
-
-
-
-
var ROUTER_PONG
-
-
-
-
var RequestResponse
-
-
-
-
var Statistics
-
-

A ProtocolMessage

-
-
var UNSET
-
-
-
-
-

Instance variables

-
-
var history
-
-

Field StoreAndForward.history

-
-
var rr
-
-

Field StoreAndForward.rr

-
-
var stats
-
-

Field StoreAndForward.stats

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/stream_interface.html b/docs/meshtastic/stream_interface.html deleted file mode 100644 index 520430c..0000000 --- a/docs/meshtastic/stream_interface.html +++ /dev/null @@ -1,665 +0,0 @@ - - - - - - -meshtastic.stream_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.stream_interface

-
-
-

Stream Interface base class

-
- -Expand source code - -
"""Stream Interface base class
-"""
-import logging
-import threading
-import time
-import traceback
-import serial
-
-
-from .mesh_interface import MeshInterface
-from .util import stripnl
-
-
-START1 = 0x94
-START2 = 0xc3
-HEADER_LEN = 4
-MAX_TO_FROM_RADIO_SIZE = 512
-
-
-class StreamInterface(MeshInterface):
-    """Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
-
-    def __init__(self, debugOut=None, noProto=False, connectNow=True):
-        """Constructor, opens a connection to self.stream
-
-        Keyword Arguments:
-            debugOut {stream} -- If a stream is provided, any debug serial output from the
-                                 device will be emitted to that stream. (default: {None})
-
-        Raises:
-            Exception: [description]
-            Exception: [description]
-        """
-
-        if not hasattr(self, 'stream') and not noProto:
-            raise Exception(
-                "StreamInterface is now abstract (to update existing code create SerialInterface instead)")
-        self._rxBuf = bytes()  # empty
-        self._wantExit = False
-
-        # FIXME, figure out why daemon=True causes reader thread to exit too early
-        self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
-
-        MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
-
-        # Start the reader thread after superclass constructor completes init
-        if connectNow:
-            self.connect()
-            if not noProto:
-                self.waitForConfig()
-
-    def connect(self):
-        """Connect to our radio
-
-        Normally this is called automatically by the constructor, but if you
-        passed in connectNow=False you can manually start the reading thread later.
-        """
-
-        # Send some bogus UART characters to force a sleeping device to wake, and
-        # if the reading statemachine was parsing a bad packet make sure
-        # we write enought start bytes to force it to resync (we don't use START1
-        # because we want to ensure it is looking for START1)
-        p = bytearray([START2] * 32)
-        self._writeBytes(p)
-        time.sleep(0.1)  # wait 100ms to give device time to start running
-
-        self._rxThread.start()
-
-        self._startConfig()
-
-        if not self.noProto:  # Wait for the db download if using the protocol
-            self._waitConnected()
-
-    def _disconnected(self):
-        """We override the superclass implementation to close our port"""
-        MeshInterface._disconnected(self)
-
-        logging.debug("Closing our port")
-        # pylint: disable=E0203
-        if not self.stream is None:
-            # pylint: disable=E0203
-            self.stream.close()
-            # pylint: disable=W0201
-            self.stream = None
-
-    def _writeBytes(self, b):
-        """Write an array of bytes to our stream and flush"""
-        if self.stream:  # ignore writes when stream is closed
-            self.stream.write(b)
-            self.stream.flush()
-
-    def _readBytes(self, length):
-        """Read an array of bytes from our stream"""
-        if self.stream:
-            return self.stream.read(length)
-        else:
-            return None
-
-    def _sendToRadioImpl(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        logging.debug(f"Sending: {stripnl(toRadio)}")
-        b = toRadio.SerializeToString()
-        bufLen = len(b)
-        # We convert into a string, because the TCP code doesn't work with byte arrays
-        header = bytes([START1, START2, (bufLen >> 8) & 0xff,  bufLen & 0xff])
-        logging.debug(f'sending header:{header} b:{b}')
-        self._writeBytes(header + b)
-
-    def close(self):
-        """Close a connection to the device"""
-        logging.debug("Closing stream")
-        MeshInterface.close(self)
-        # pyserial cancel_read doesn't seem to work, therefore we ask the
-        # reader thread to close things for us
-        self._wantExit = True
-        if self._rxThread != threading.current_thread():
-            self._rxThread.join()  # wait for it to exit
-
-    def __reader(self):
-        """The reader thread that reads bytes from our stream"""
-        logging.debug('in __reader()')
-        empty = bytes()
-
-        try:
-            while not self._wantExit:
-                logging.debug("reading character")
-                b = self._readBytes(1)
-                logging.debug("In reader loop")
-                #logging.debug(f"read returned {b}")
-                if len(b) > 0:
-                    c = b[0]
-                    #logging.debug(f'c:{c}')
-                    ptr = len(self._rxBuf)
-
-                    # Assume we want to append this byte, fixme use bytearray instead
-                    self._rxBuf = self._rxBuf + b
-
-                    if ptr == 0:  # looking for START1
-                        if c != START1:
-                            self._rxBuf = empty  # failed to find start
-                            if self.debugOut is not None:
-                                try:
-                                    self.debugOut.write(b.decode("utf-8"))
-                                except:
-                                    self.debugOut.write('?')
-
-                    elif ptr == 1:  # looking for START2
-                        if c != START2:
-                            self._rxBuf = empty  # failed to find start2
-                    elif ptr >= HEADER_LEN - 1:  # we've at least got a header
-                        #logging.debug('at least we received a header')
-                        # big endian length follows header
-                        packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
-
-                        if ptr == HEADER_LEN - 1:  # we _just_ finished reading the header, validate length
-                            if packetlen > MAX_TO_FROM_RADIO_SIZE:
-                                self._rxBuf = empty  # length was out out bounds, restart
-
-                        if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
-                            try:
-                                self._handleFromRadio(self._rxBuf[HEADER_LEN:])
-                            except Exception as ex:
-                                logging.error(f"Error while handling message from radio {ex}")
-                                traceback.print_exc()
-                            self._rxBuf = empty
-                else:
-                    # logging.debug(f"timeout")
-                    pass
-        except serial.SerialException as ex:
-            if not self._wantExit:  # We might intentionally get an exception during shutdown
-                logging.warning(f"Meshtastic serial port disconnected, disconnecting... {ex}")
-        except OSError as ex:
-            if not self._wantExit:  # We might intentionally get an exception during shutdown
-                logging.error(f"Unexpected OSError, terminating meshtastic reader... {ex}")
-        except Exception as ex:
-            logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
-        finally:
-            logging.debug("reader is exiting")
-            self._disconnected()
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class StreamInterface -(debugOut=None, noProto=False, connectNow=True) -
-
-

Interface class for meshtastic devices over a stream link (serial, TCP, etc)

-

Constructor, opens a connection to self.stream

-

Keyword Arguments: -debugOut {stream} – If a stream is provided, any debug serial output from the -device will be emitted to that stream. (default: {None})

-

Raises

-
-
Exception
-
[description]
-
Exception
-
[description]
-
-
- -Expand source code - -
class StreamInterface(MeshInterface):
-    """Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
-
-    def __init__(self, debugOut=None, noProto=False, connectNow=True):
-        """Constructor, opens a connection to self.stream
-
-        Keyword Arguments:
-            debugOut {stream} -- If a stream is provided, any debug serial output from the
-                                 device will be emitted to that stream. (default: {None})
-
-        Raises:
-            Exception: [description]
-            Exception: [description]
-        """
-
-        if not hasattr(self, 'stream') and not noProto:
-            raise Exception(
-                "StreamInterface is now abstract (to update existing code create SerialInterface instead)")
-        self._rxBuf = bytes()  # empty
-        self._wantExit = False
-
-        # FIXME, figure out why daemon=True causes reader thread to exit too early
-        self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
-
-        MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
-
-        # Start the reader thread after superclass constructor completes init
-        if connectNow:
-            self.connect()
-            if not noProto:
-                self.waitForConfig()
-
-    def connect(self):
-        """Connect to our radio
-
-        Normally this is called automatically by the constructor, but if you
-        passed in connectNow=False you can manually start the reading thread later.
-        """
-
-        # Send some bogus UART characters to force a sleeping device to wake, and
-        # if the reading statemachine was parsing a bad packet make sure
-        # we write enought start bytes to force it to resync (we don't use START1
-        # because we want to ensure it is looking for START1)
-        p = bytearray([START2] * 32)
-        self._writeBytes(p)
-        time.sleep(0.1)  # wait 100ms to give device time to start running
-
-        self._rxThread.start()
-
-        self._startConfig()
-
-        if not self.noProto:  # Wait for the db download if using the protocol
-            self._waitConnected()
-
-    def _disconnected(self):
-        """We override the superclass implementation to close our port"""
-        MeshInterface._disconnected(self)
-
-        logging.debug("Closing our port")
-        # pylint: disable=E0203
-        if not self.stream is None:
-            # pylint: disable=E0203
-            self.stream.close()
-            # pylint: disable=W0201
-            self.stream = None
-
-    def _writeBytes(self, b):
-        """Write an array of bytes to our stream and flush"""
-        if self.stream:  # ignore writes when stream is closed
-            self.stream.write(b)
-            self.stream.flush()
-
-    def _readBytes(self, length):
-        """Read an array of bytes from our stream"""
-        if self.stream:
-            return self.stream.read(length)
-        else:
-            return None
-
-    def _sendToRadioImpl(self, toRadio):
-        """Send a ToRadio protobuf to the device"""
-        logging.debug(f"Sending: {stripnl(toRadio)}")
-        b = toRadio.SerializeToString()
-        bufLen = len(b)
-        # We convert into a string, because the TCP code doesn't work with byte arrays
-        header = bytes([START1, START2, (bufLen >> 8) & 0xff,  bufLen & 0xff])
-        logging.debug(f'sending header:{header} b:{b}')
-        self._writeBytes(header + b)
-
-    def close(self):
-        """Close a connection to the device"""
-        logging.debug("Closing stream")
-        MeshInterface.close(self)
-        # pyserial cancel_read doesn't seem to work, therefore we ask the
-        # reader thread to close things for us
-        self._wantExit = True
-        if self._rxThread != threading.current_thread():
-            self._rxThread.join()  # wait for it to exit
-
-    def __reader(self):
-        """The reader thread that reads bytes from our stream"""
-        logging.debug('in __reader()')
-        empty = bytes()
-
-        try:
-            while not self._wantExit:
-                logging.debug("reading character")
-                b = self._readBytes(1)
-                logging.debug("In reader loop")
-                #logging.debug(f"read returned {b}")
-                if len(b) > 0:
-                    c = b[0]
-                    #logging.debug(f'c:{c}')
-                    ptr = len(self._rxBuf)
-
-                    # Assume we want to append this byte, fixme use bytearray instead
-                    self._rxBuf = self._rxBuf + b
-
-                    if ptr == 0:  # looking for START1
-                        if c != START1:
-                            self._rxBuf = empty  # failed to find start
-                            if self.debugOut is not None:
-                                try:
-                                    self.debugOut.write(b.decode("utf-8"))
-                                except:
-                                    self.debugOut.write('?')
-
-                    elif ptr == 1:  # looking for START2
-                        if c != START2:
-                            self._rxBuf = empty  # failed to find start2
-                    elif ptr >= HEADER_LEN - 1:  # we've at least got a header
-                        #logging.debug('at least we received a header')
-                        # big endian length follows header
-                        packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
-
-                        if ptr == HEADER_LEN - 1:  # we _just_ finished reading the header, validate length
-                            if packetlen > MAX_TO_FROM_RADIO_SIZE:
-                                self._rxBuf = empty  # length was out out bounds, restart
-
-                        if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
-                            try:
-                                self._handleFromRadio(self._rxBuf[HEADER_LEN:])
-                            except Exception as ex:
-                                logging.error(f"Error while handling message from radio {ex}")
-                                traceback.print_exc()
-                            self._rxBuf = empty
-                else:
-                    # logging.debug(f"timeout")
-                    pass
-        except serial.SerialException as ex:
-            if not self._wantExit:  # We might intentionally get an exception during shutdown
-                logging.warning(f"Meshtastic serial port disconnected, disconnecting... {ex}")
-        except OSError as ex:
-            if not self._wantExit:  # We might intentionally get an exception during shutdown
-                logging.error(f"Unexpected OSError, terminating meshtastic reader... {ex}")
-        except Exception as ex:
-            logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
-        finally:
-            logging.debug("reader is exiting")
-            self._disconnected()
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def _StreamInterface__reader(self) -
-
-

The reader thread that reads bytes from our stream

-
- -Expand source code - -
def __reader(self):
-    """The reader thread that reads bytes from our stream"""
-    logging.debug('in __reader()')
-    empty = bytes()
-
-    try:
-        while not self._wantExit:
-            logging.debug("reading character")
-            b = self._readBytes(1)
-            logging.debug("In reader loop")
-            #logging.debug(f"read returned {b}")
-            if len(b) > 0:
-                c = b[0]
-                #logging.debug(f'c:{c}')
-                ptr = len(self._rxBuf)
-
-                # Assume we want to append this byte, fixme use bytearray instead
-                self._rxBuf = self._rxBuf + b
-
-                if ptr == 0:  # looking for START1
-                    if c != START1:
-                        self._rxBuf = empty  # failed to find start
-                        if self.debugOut is not None:
-                            try:
-                                self.debugOut.write(b.decode("utf-8"))
-                            except:
-                                self.debugOut.write('?')
-
-                elif ptr == 1:  # looking for START2
-                    if c != START2:
-                        self._rxBuf = empty  # failed to find start2
-                elif ptr >= HEADER_LEN - 1:  # we've at least got a header
-                    #logging.debug('at least we received a header')
-                    # big endian length follows header
-                    packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
-
-                    if ptr == HEADER_LEN - 1:  # we _just_ finished reading the header, validate length
-                        if packetlen > MAX_TO_FROM_RADIO_SIZE:
-                            self._rxBuf = empty  # length was out out bounds, restart
-
-                    if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
-                        try:
-                            self._handleFromRadio(self._rxBuf[HEADER_LEN:])
-                        except Exception as ex:
-                            logging.error(f"Error while handling message from radio {ex}")
-                            traceback.print_exc()
-                        self._rxBuf = empty
-            else:
-                # logging.debug(f"timeout")
-                pass
-    except serial.SerialException as ex:
-        if not self._wantExit:  # We might intentionally get an exception during shutdown
-            logging.warning(f"Meshtastic serial port disconnected, disconnecting... {ex}")
-    except OSError as ex:
-        if not self._wantExit:  # We might intentionally get an exception during shutdown
-            logging.error(f"Unexpected OSError, terminating meshtastic reader... {ex}")
-    except Exception as ex:
-        logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
-    finally:
-        logging.debug("reader is exiting")
-        self._disconnected()
-
-
-
-def _disconnected(self) -
-
-

We override the superclass implementation to close our port

-
- -Expand source code - -
def _disconnected(self):
-    """We override the superclass implementation to close our port"""
-    MeshInterface._disconnected(self)
-
-    logging.debug("Closing our port")
-    # pylint: disable=E0203
-    if not self.stream is None:
-        # pylint: disable=E0203
-        self.stream.close()
-        # pylint: disable=W0201
-        self.stream = None
-
-
-
-def _readBytes(self, length) -
-
-

Read an array of bytes from our stream

-
- -Expand source code - -
def _readBytes(self, length):
-    """Read an array of bytes from our stream"""
-    if self.stream:
-        return self.stream.read(length)
-    else:
-        return None
-
-
-
-def _writeBytes(self, b) -
-
-

Write an array of bytes to our stream and flush

-
- -Expand source code - -
def _writeBytes(self, b):
-    """Write an array of bytes to our stream and flush"""
-    if self.stream:  # ignore writes when stream is closed
-        self.stream.write(b)
-        self.stream.flush()
-
-
-
-def close(self) -
-
-

Close a connection to the device

-
- -Expand source code - -
def close(self):
-    """Close a connection to the device"""
-    logging.debug("Closing stream")
-    MeshInterface.close(self)
-    # pyserial cancel_read doesn't seem to work, therefore we ask the
-    # reader thread to close things for us
-    self._wantExit = True
-    if self._rxThread != threading.current_thread():
-        self._rxThread.join()  # wait for it to exit
-
-
-
-def connect(self) -
-
-

Connect to our radio

-

Normally this is called automatically by the constructor, but if you -passed in connectNow=False you can manually start the reading thread later.

-
- -Expand source code - -
def connect(self):
-    """Connect to our radio
-
-    Normally this is called automatically by the constructor, but if you
-    passed in connectNow=False you can manually start the reading thread later.
-    """
-
-    # Send some bogus UART characters to force a sleeping device to wake, and
-    # if the reading statemachine was parsing a bad packet make sure
-    # we write enought start bytes to force it to resync (we don't use START1
-    # because we want to ensure it is looking for START1)
-    p = bytearray([START2] * 32)
-    self._writeBytes(p)
-    time.sleep(0.1)  # wait 100ms to give device time to start running
-
-    self._rxThread.start()
-
-    self._startConfig()
-
-    if not self.noProto:  # Wait for the db download if using the protocol
-        self._waitConnected()
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tcp_interface.html b/docs/meshtastic/tcp_interface.html deleted file mode 100644 index e8b149d..0000000 --- a/docs/meshtastic/tcp_interface.html +++ /dev/null @@ -1,270 +0,0 @@ - - - - - - -meshtastic.tcp_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tcp_interface

-
-
-

TCPInterface class for interfacing with http endpoint

-
- -Expand source code - -
"""TCPInterface class for interfacing with http endpoint
-"""
-import logging
-import socket
-from typing import AnyStr
-
-from .stream_interface import StreamInterface
-
-class TCPInterface(StreamInterface):
-    """Interface class for meshtastic devices over a TCP link"""
-
-    def __init__(self, hostname: AnyStr, debugOut=None, noProto=False,
-                 connectNow=True, portNumber=4403):
-        """Constructor, opens a connection to a specified IP address/hostname
-
-        Keyword Arguments:
-            hostname {string} -- Hostname/IP address of the device to connect to
-        """
-
-        # Instead of wrapping as a stream, we use the native socket API
-        # self.stream = sock.makefile('rw')
-        self.stream = None
-
-        self.hostname = hostname
-        self.portNumber = portNumber
-
-        if connectNow:
-            logging.debug(f"Connecting to {hostname}")
-            server_address = (hostname, portNumber)
-            sock = socket.create_connection(server_address)
-            self.socket = sock
-        else:
-            self.socket = None
-
-        StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
-                                 connectNow=connectNow)
-
-    def myConnect(self):
-        """Connect to socket"""
-        server_address = (self.hostname, self.portNumber)
-        sock = socket.create_connection(server_address)
-        self.socket = sock
-
-    def close(self):
-        """Close a connection to the device"""
-        logging.debug("Closing TCP stream")
-        StreamInterface.close(self)
-        # Sometimes the socket read might be blocked in the reader thread.
-        # Therefore we force the shutdown by closing the socket here
-        self._wantExit = True
-        if not self.socket is None:
-            try:
-                self.socket.shutdown(socket.SHUT_RDWR)
-            except:
-                pass  # Ignore errors in shutdown, because we might have a race with the server
-            self.socket.close()
-
-    def _writeBytes(self, b):
-        """Write an array of bytes to our stream and flush"""
-        self.socket.send(b)
-
-    def _readBytes(self, length):
-        """Read an array of bytes from our stream"""
-        return self.socket.recv(length)
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class TCPInterface -(hostname: ~AnyStr, debugOut=None, noProto=False, connectNow=True, portNumber=4403) -
-
-

Interface class for meshtastic devices over a TCP link

-

Constructor, opens a connection to a specified IP address/hostname

-

Keyword Arguments: -hostname {string} – Hostname/IP address of the device to connect to

-
- -Expand source code - -
class TCPInterface(StreamInterface):
-    """Interface class for meshtastic devices over a TCP link"""
-
-    def __init__(self, hostname: AnyStr, debugOut=None, noProto=False,
-                 connectNow=True, portNumber=4403):
-        """Constructor, opens a connection to a specified IP address/hostname
-
-        Keyword Arguments:
-            hostname {string} -- Hostname/IP address of the device to connect to
-        """
-
-        # Instead of wrapping as a stream, we use the native socket API
-        # self.stream = sock.makefile('rw')
-        self.stream = None
-
-        self.hostname = hostname
-        self.portNumber = portNumber
-
-        if connectNow:
-            logging.debug(f"Connecting to {hostname}")
-            server_address = (hostname, portNumber)
-            sock = socket.create_connection(server_address)
-            self.socket = sock
-        else:
-            self.socket = None
-
-        StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
-                                 connectNow=connectNow)
-
-    def myConnect(self):
-        """Connect to socket"""
-        server_address = (self.hostname, self.portNumber)
-        sock = socket.create_connection(server_address)
-        self.socket = sock
-
-    def close(self):
-        """Close a connection to the device"""
-        logging.debug("Closing TCP stream")
-        StreamInterface.close(self)
-        # Sometimes the socket read might be blocked in the reader thread.
-        # Therefore we force the shutdown by closing the socket here
-        self._wantExit = True
-        if not self.socket is None:
-            try:
-                self.socket.shutdown(socket.SHUT_RDWR)
-            except:
-                pass  # Ignore errors in shutdown, because we might have a race with the server
-            self.socket.close()
-
-    def _writeBytes(self, b):
-        """Write an array of bytes to our stream and flush"""
-        self.socket.send(b)
-
-    def _readBytes(self, length):
-        """Read an array of bytes from our stream"""
-        return self.socket.recv(length)
-
-

Ancestors

- -

Methods

-
-
-def myConnect(self) -
-
-

Connect to socket

-
- -Expand source code - -
def myConnect(self):
-    """Connect to socket"""
-    server_address = (self.hostname, self.portNumber)
-    sock = socket.create_connection(server_address)
-    self.socket = sock
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/test.html b/docs/meshtastic/test.html deleted file mode 100644 index df49a92..0000000 --- a/docs/meshtastic/test.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - -meshtastic.test API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.test

-
-
-

With two radios connected serially, send and receive test -messages and report back if successful.

-
- -Expand source code - -
"""With two radios connected serially, send and receive test
-   messages and report back if successful.
-"""
-import logging
-import time
-import sys
-import traceback
-from dotmap import DotMap
-from pubsub import pub
-import meshtastic.util
-from .__init__ import BROADCAST_NUM
-from .serial_interface import SerialInterface
-from .tcp_interface import TCPInterface
-
-
-"""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 is not 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 _ 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):
-    """Run the tests."""
-    logging.info(f"Running {numTests} tests with wantAck={wantAck}")
-    numFail = 0
-    numSuccess = 0
-    for _ 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")
-
-        time.sleep(1)
-
-    if numFail > maxFailures:
-        logging.error("Too many failures! Test failed!")
-        return False
-    return True
-
-
-def testThread(numTests=50):
-    """Test thread"""
-    logging.info("Found devices, starting tests...")
-    result = runTests(numTests, wantAck=True)
-    if result:
-        # Run another test
-        # Allow a few dropped packets
-        result = runTests(numTests, wantAck=False, maxFailures=1)
-    return result
-
-
-def onConnection(topic=pub.AUTO_TOPIC):
-    """Callback invoked when we connect/disconnect from a radio"""
-    print(f"Connection changed: {topic.getName()}")
-
-
-def openDebugLog(portName):
-    """Open the debug log file"""
-    debugname = "log" + portName.replace("/", "_")
-    logging.info(f"Writing serial debugging to {debugname}")
-    return open(debugname, 'w+', buffering=1, encoding='utf8')
-
-
-def testAll(numTests=5):
-    """
-    Run a series of tests using devices we can find.
-    This is called from the cli with the "--test" option.
-
-    """
-    ports = meshtastic.util.findPorts()
-    if len(ports) < 2:
-        meshtastic.util.our_exit("Warning: 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")
-    result = testThread(numTests)
-
-    for i in interfaces:
-        i.close()
-
-    return result
-
-
-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)
-    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)
-
-
-
-
-
-

Global variables

-
-
var interfaces
-
-

A list of all packets we received while the current test was running

-
-
-
-
-

Functions

-
-
-def onConnection(topic=pubsub.core.callables.AUTO_TOPIC) -
-
-

Callback invoked when we connect/disconnect from a radio

-
- -Expand source code - -
def onConnection(topic=pub.AUTO_TOPIC):
-    """Callback invoked when we connect/disconnect from a radio"""
-    print(f"Connection changed: {topic.getName()}")
-
-
-
-def onNode(node) -
-
-

Callback invoked when the node DB changes

-
- -Expand source code - -
def onNode(node):
-    """Callback invoked when the node DB changes"""
-    print(f"Node changed: {node}")
-
-
-
-def onReceive(packet, interface) -
-
-

Callback invoked when a packet arrives

-
- -Expand source code - -
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 is not None:
-                receivedPackets.append(p)
-
-
-
-def openDebugLog(portName) -
-
-

Open the debug log file

-
- -Expand source code - -
def openDebugLog(portName):
-    """Open the debug log file"""
-    debugname = "log" + portName.replace("/", "_")
-    logging.info(f"Writing serial debugging to {debugname}")
-    return open(debugname, 'w+', buffering=1, encoding='utf8')
-
-
-
-def runTests(numTests=50, wantAck=False, maxFailures=0) -
-
-

Run the tests.

-
- -Expand source code - -
def runTests(numTests=50, wantAck=False, maxFailures=0):
-    """Run the tests."""
-    logging.info(f"Running {numTests} tests with wantAck={wantAck}")
-    numFail = 0
-    numSuccess = 0
-    for _ 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")
-
-        time.sleep(1)
-
-    if numFail > maxFailures:
-        logging.error("Too many failures! Test failed!")
-        return False
-    return True
-
-
-
-def subscribe() -
-
-

Subscribe to the topics the user probably wants to see, prints output to stdout

-
- -Expand source code - -
def subscribe():
-    """Subscribe to the topics the user probably wants to see, prints output to stdout"""
-
-    pub.subscribe(onNode, "meshtastic.node")
-
-
-
-def testAll(numTests=5) -
-
-

Run a series of tests using devices we can find. -This is called from the cli with the "–test" option.

-
- -Expand source code - -
def testAll(numTests=5):
-    """
-    Run a series of tests using devices we can find.
-    This is called from the cli with the "--test" option.
-
-    """
-    ports = meshtastic.util.findPorts()
-    if len(ports) < 2:
-        meshtastic.util.our_exit("Warning: 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")
-    result = testThread(numTests)
-
-    for i in interfaces:
-        i.close()
-
-    return result
-
-
-
-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

-
- -Expand source code - -
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 _ 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 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()'

-
- -Expand source code - -
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)
-    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)
-
-
-
-def testThread(numTests=50) -
-
-

Test thread

-
- -Expand source code - -
def testThread(numTests=50):
-    """Test thread"""
-    logging.info("Found devices, starting tests...")
-    result = runTests(numTests, wantAck=True)
-    if result:
-        # Run another test
-        # Allow a few dropped packets
-        result = runTests(numTests, wantAck=False, maxFailures=1)
-    return result
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/test/index.html b/docs/meshtastic/test/index.html deleted file mode 100644 index af6298c..0000000 --- a/docs/meshtastic/test/index.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - -meshtastic.test API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.test

-
-
-
-
-

Sub-modules

-
-
meshtastic.test.test_int
-
-

Meshtastic integration tests

-
-
meshtastic.test.test_mesh_interface
-
-

Meshtastic unit tests for node.py

-
-
meshtastic.test.test_smoke1
-
-

Meshtastic smoke tests with a single device

-
-
meshtastic.test.test_util
-
-

Meshtastic unit tests for node.py

-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/test/test_int.html b/docs/meshtastic/test/test_int.html deleted file mode 100644 index 714007a..0000000 --- a/docs/meshtastic/test/test_int.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - -meshtastic.test.test_int API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.test.test_int

-
-
-

Meshtastic integration tests

-
- -Expand source code - -
"""Meshtastic integration tests"""
-import re
-import subprocess
-
-import pytest
-
-
-@pytest.mark.int
-def test_int_no_args():
-    """Test without any args"""
-    return_value, out = subprocess.getstatusoutput('meshtastic')
-    assert re.match(r'usage: meshtastic', out)
-    assert return_value == 1
-
-
-@pytest.mark.int
-def test_int_version():
-    """Test '--version'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --version')
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert return_value == 0
-
-
-@pytest.mark.int
-def test_int_help():
-    """Test '--help'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --help')
-    assert re.match(r'usage: meshtastic ', out)
-    assert return_value == 0
-
-
-@pytest.mark.int
-def test_int_support():
-    """Test '--support'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --support')
-    assert re.search(r'System', out)
-    assert re.search(r'Python', out)
-    assert return_value == 0
-
-
-
-
-
-
-
-

Functions

-
-
-def test_int_help() -
-
-

Test '–help'.

-
- -Expand source code - -
@pytest.mark.int
-def test_int_help():
-    """Test '--help'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --help')
-    assert re.match(r'usage: meshtastic ', out)
-    assert return_value == 0
-
-
-
-def test_int_no_args() -
-
-

Test without any args

-
- -Expand source code - -
@pytest.mark.int
-def test_int_no_args():
-    """Test without any args"""
-    return_value, out = subprocess.getstatusoutput('meshtastic')
-    assert re.match(r'usage: meshtastic', out)
-    assert return_value == 1
-
-
-
-def test_int_support() -
-
-

Test '–support'.

-
- -Expand source code - -
@pytest.mark.int
-def test_int_support():
-    """Test '--support'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --support')
-    assert re.search(r'System', out)
-    assert re.search(r'Python', out)
-    assert return_value == 0
-
-
-
-def test_int_version() -
-
-

Test '–version'.

-
- -Expand source code - -
@pytest.mark.int
-def test_int_version():
-    """Test '--version'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --version')
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert return_value == 0
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/test/test_mesh_interface.html b/docs/meshtastic/test/test_mesh_interface.html deleted file mode 100644 index 180b817..0000000 --- a/docs/meshtastic/test/test_mesh_interface.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -meshtastic.test.test_mesh_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.test.test_mesh_interface

-
-
-

Meshtastic unit tests for node.py

-
- -Expand source code - -
"""Meshtastic unit tests for node.py"""
-
-import pytest
-
-from meshtastic.mesh_interface import MeshInterface
-
-
-@pytest.mark.unit
-def test_MeshInterface():
-    """Test that we instantiate a MeshInterface"""
-    iface = MeshInterface(noProto=True)
-    iface.showInfo()
-    iface.localNode.showInfo()
-    iface.close()
-
-
-
-
-
-
-
-

Functions

-
-
-def test_MeshInterface() -
-
-

Test that we instantiate a MeshInterface

-
- -Expand source code - -
@pytest.mark.unit
-def test_MeshInterface():
-    """Test that we instantiate a MeshInterface"""
-    iface = MeshInterface(noProto=True)
-    iface.showInfo()
-    iface.localNode.showInfo()
-    iface.close()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/test/test_smoke1.html b/docs/meshtastic/test/test_smoke1.html deleted file mode 100644 index 3024282..0000000 --- a/docs/meshtastic/test/test_smoke1.html +++ /dev/null @@ -1,982 +0,0 @@ - - - - - - -meshtastic.test.test_smoke1 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.test.test_smoke1

-
-
-

Meshtastic smoke tests with a single device

-
- -Expand source code - -
"""Meshtastic smoke tests with a single device"""
-import re
-import subprocess
-import time
-import os
-
-# Do not like using hard coded sleeps, but it probably makes
-# sense to pause for the radio at apprpriate times
-import pytest
-
-import meshtastic
-
-# seconds to pause after running a meshtastic command
-PAUSE_AFTER_COMMAND = 2
-PAUSE_AFTER_REBOOT = 7
-
-
-@pytest.mark.smoke1
-def test_smoke1_reboot():
-    """Test reboot"""
-    return_value, _ = subprocess.getstatusoutput('meshtastic --reboot')
-    assert return_value == 0
-    # pause for the radio to reset (10 seconds for the pause, and a few more seconds to be back up)
-    time.sleep(18)
-
-
-@pytest.mark.smoke1
-def test_smoke1_info():
-    """Test --info"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^My info', out, re.MULTILINE)
-    assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
-    assert re.search(r'^Preferences', out, re.MULTILINE)
-    assert re.search(r'^Channels', out, re.MULTILINE)
-    assert re.search(r'^  PRIMARY', out, re.MULTILINE)
-    assert re.search(r'^Primary channel URL', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_debug():
-    """Test --debug"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info --debug')
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^DEBUG:root', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_seriallog_to_file():
-    """Test --seriallog to a file creates a file"""
-    filename = 'tmpoutput.txt'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --info --seriallog {filename}')
-    assert os.path.exists(f"{filename}")
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-@pytest.mark.smoke1
-def test_smoke1_qr():
-    """Test --qr"""
-    filename = 'tmpqr'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --qr > {filename}')
-    assert os.path.exists(f"{filename}")
-    # not really testing that a valid qr code is created, just that the file size
-    # is reasonably big enough for a qr code
-    assert os.stat(f"{filename}").st_size > 20000
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-@pytest.mark.smoke1
-def test_smoke1_nodes():
-    """Test --nodes"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --nodes')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^│   N │ User', out, re.MULTILINE)
-    assert re.search(r'^│   1 │', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_send_hello():
-    """Test --sendtext hello"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --sendtext hello')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Sending text message hello to \^all', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_port():
-    """Test --port"""
-    # first, get the ports
-    ports = meshtastic.util.findPorts()
-    # hopefully there is just one
-    assert len(ports) == 1
-    port = ports[0]
-    return_value, out = subprocess.getstatusoutput(f'meshtastic --port {port} --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_is_router_true():
-    """Test --set is_router true"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to true', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: True', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_location_info():
-    """Test --setlat, --setlon and --setalt """
-    return_value, out = subprocess.getstatusoutput('meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Fixing altitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing latitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing longitude', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out2 = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'1337', out2, re.MULTILINE)
-    assert re.search(r'32.7767', out2, re.MULTILINE)
-    assert re.search(r'-96.797', out2, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_is_router_false():
-    """Test --set is_router false"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router false')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to false', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: False', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_owner():
-    """Test --set-owner name"""
-    # make sure the owner is not Joe
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Bob')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Bob', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Joe')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_team():
-    """Test --set-team """
-    # unset the team
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CLEAR')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CYAN')
-    assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'CYAN', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_values():
-    """Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
-       --ch-shortslow, and --ch-shortfast arguments
-    """
-    exp = {
-            '--ch-longslow': 'Bw125Cr48Sf4096',
-            # TODO: not sure why these fail thru tests, but ok manually
-            #'--ch-longfast': 'Bw31_25Cr48Sf512',
-            #'--ch-mediumslow': 'Bw250Cr46Sf2048',
-            #'--ch-mediumfast': 'Bw250Cr47Sf1024',
-            # TODO '--ch-shortslow': '?',
-            '--ch-shortfast': 'Bw500Cr45Sf128'
-          }
-
-    for key, val in exp.items():
-        print(key, val)
-        return_value, out = subprocess.getstatusoutput(f'meshtastic {key}')
-        assert re.match(r'Connected to radio', out)
-        assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio (might reboot)
-        time.sleep(PAUSE_AFTER_REBOOT)
-        return_value, out = subprocess.getstatusoutput('meshtastic --info')
-        assert re.search(val, out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio
-        time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_set_name():
-    """Test --ch-set name"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set name to MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_add_and_ch_del():
-    """Test --ch-add"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-index 1 --ch-del')
-    assert re.search(r'Deleting channel 1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    # make sure the secondar channel is not there
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert not re.search(r'SECONDARY', out, re.MULTILINE)
-    assert not re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_set_modem_config():
-    """Test --ch-set modem_config"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_seturl_default():
-    """Test --seturl with default value"""
-    # set some channel value so we no longer have a default channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name foo')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure we no longer have a default primary channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-    url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_seturl_invalid_url():
-    """Test --seturl with invalid url"""
-    # Note: This url is no longer a valid url.
-    url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ="
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('Warning: There were no settings', out, re.MULTILINE)
-    assert return_value == 1
-
-
-@pytest.mark.smoke1
-def test_smoke1_configure():
-    """Test --configure"""
-    _ , out = subprocess.getstatusoutput(f"meshtastic --configure example_config.yaml")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('^Setting device owner to Bob TBeam', out, re.MULTILINE)
-    assert re.search('^Fixing altitude at 304 meters', out, re.MULTILINE)
-    assert re.search('^Fixing latitude at 35.8', out, re.MULTILINE)
-    assert re.search('^Fixing longitude at -93.8', out, re.MULTILINE)
-    assert re.search('^Setting device position', out, re.MULTILINE)
-    assert re.search('^Set region to 1', out, re.MULTILINE)
-    assert re.search('^Set is_always_powered to true', out, re.MULTILINE)
-    assert re.search('^Set send_owner_interval to 2', out, re.MULTILINE)
-    assert re.search('^Set screen_on_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Set wait_bluetooth_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Writing modified preferences to device', out, re.MULTILINE)
-
-
-@pytest.mark.smoke1
-def test_smoke1_factory_reset():
-    """Test factory reset"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set factory_reset true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set factory_reset to true', out, re.MULTILINE)
-    assert re.search(r'^Writing modified preferences to device', out, re.MULTILINE)
-    assert return_value == 0
-    # NOTE: The radio may not be responsive after this, may need to do a manual reboot
-    # by pressing the button
-
-
-
-
-
-
-
-

Functions

-
-
-def test_smoke1_ch_add_and_ch_del() -
-
-

Test –ch-add

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_add_and_ch_del():
-    """Test --ch-add"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-index 1 --ch-del')
-    assert re.search(r'Deleting channel 1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    # make sure the secondar channel is not there
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert not re.search(r'SECONDARY', out, re.MULTILINE)
-    assert not re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ch_set_modem_config() -
-
-

Test –ch-set modem_config

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_set_modem_config():
-    """Test --ch-set modem_config"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ch_set_name() -
-
-

Test –ch-set name

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_set_name():
-    """Test --ch-set name"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set name to MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ch_values() -
-
-

Test –ch-longslow, –ch-longfast, –ch-mediumslow, –ch-mediumsfast, -–ch-shortslow, and –ch-shortfast arguments

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_values():
-    """Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
-       --ch-shortslow, and --ch-shortfast arguments
-    """
-    exp = {
-            '--ch-longslow': 'Bw125Cr48Sf4096',
-            # TODO: not sure why these fail thru tests, but ok manually
-            #'--ch-longfast': 'Bw31_25Cr48Sf512',
-            #'--ch-mediumslow': 'Bw250Cr46Sf2048',
-            #'--ch-mediumfast': 'Bw250Cr47Sf1024',
-            # TODO '--ch-shortslow': '?',
-            '--ch-shortfast': 'Bw500Cr45Sf128'
-          }
-
-    for key, val in exp.items():
-        print(key, val)
-        return_value, out = subprocess.getstatusoutput(f'meshtastic {key}')
-        assert re.match(r'Connected to radio', out)
-        assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio (might reboot)
-        time.sleep(PAUSE_AFTER_REBOOT)
-        return_value, out = subprocess.getstatusoutput('meshtastic --info')
-        assert re.search(val, out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio
-        time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_configure() -
-
-

Test –configure

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_configure():
-    """Test --configure"""
-    _ , out = subprocess.getstatusoutput(f"meshtastic --configure example_config.yaml")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('^Setting device owner to Bob TBeam', out, re.MULTILINE)
-    assert re.search('^Fixing altitude at 304 meters', out, re.MULTILINE)
-    assert re.search('^Fixing latitude at 35.8', out, re.MULTILINE)
-    assert re.search('^Fixing longitude at -93.8', out, re.MULTILINE)
-    assert re.search('^Setting device position', out, re.MULTILINE)
-    assert re.search('^Set region to 1', out, re.MULTILINE)
-    assert re.search('^Set is_always_powered to true', out, re.MULTILINE)
-    assert re.search('^Set send_owner_interval to 2', out, re.MULTILINE)
-    assert re.search('^Set screen_on_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Set wait_bluetooth_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Writing modified preferences to device', out, re.MULTILINE)
-
-
-
-def test_smoke1_debug() -
-
-

Test –debug

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_debug():
-    """Test --debug"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info --debug')
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^DEBUG:root', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_factory_reset() -
-
-

Test factory reset

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_factory_reset():
-    """Test factory reset"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set factory_reset true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set factory_reset to true', out, re.MULTILINE)
-    assert re.search(r'^Writing modified preferences to device', out, re.MULTILINE)
-    assert return_value == 0
-    # NOTE: The radio may not be responsive after this, may need to do a manual reboot
-    # by pressing the button
-
-
-
-def test_smoke1_info() -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_info():
-    """Test --info"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^My info', out, re.MULTILINE)
-    assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
-    assert re.search(r'^Preferences', out, re.MULTILINE)
-    assert re.search(r'^Channels', out, re.MULTILINE)
-    assert re.search(r'^  PRIMARY', out, re.MULTILINE)
-    assert re.search(r'^Primary channel URL', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_nodes() -
-
-

Test –nodes

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_nodes():
-    """Test --nodes"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --nodes')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^│   N │ User', out, re.MULTILINE)
-    assert re.search(r'^│   1 │', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_port() -
-
-

Test –port

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_port():
-    """Test --port"""
-    # first, get the ports
-    ports = meshtastic.util.findPorts()
-    # hopefully there is just one
-    assert len(ports) == 1
-    port = ports[0]
-    return_value, out = subprocess.getstatusoutput(f'meshtastic --port {port} --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_qr() -
-
-

Test –qr

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_qr():
-    """Test --qr"""
-    filename = 'tmpqr'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --qr > {filename}')
-    assert os.path.exists(f"{filename}")
-    # not really testing that a valid qr code is created, just that the file size
-    # is reasonably big enough for a qr code
-    assert os.stat(f"{filename}").st_size > 20000
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-
-def test_smoke1_reboot() -
-
-

Test reboot

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_reboot():
-    """Test reboot"""
-    return_value, _ = subprocess.getstatusoutput('meshtastic --reboot')
-    assert return_value == 0
-    # pause for the radio to reset (10 seconds for the pause, and a few more seconds to be back up)
-    time.sleep(18)
-
-
-
-def test_smoke1_send_hello() -
-
-

Test –sendtext hello

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_send_hello():
-    """Test --sendtext hello"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --sendtext hello')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Sending text message hello to \^all', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_seriallog_to_file() -
-
-

Test –seriallog to a file creates a file

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_seriallog_to_file():
-    """Test --seriallog to a file creates a file"""
-    filename = 'tmpoutput.txt'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --info --seriallog {filename}')
-    assert os.path.exists(f"{filename}")
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-
-def test_smoke1_set_is_router_false() -
-
-

Test –set is_router false

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_is_router_false():
-    """Test --set is_router false"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router false')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to false', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: False', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_is_router_true() -
-
-

Test –set is_router true

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_is_router_true():
-    """Test --set is_router true"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to true', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: True', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_location_info() -
-
-

Test –setlat, –setlon and –setalt

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_location_info():
-    """Test --setlat, --setlon and --setalt """
-    return_value, out = subprocess.getstatusoutput('meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Fixing altitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing latitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing longitude', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out2 = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'1337', out2, re.MULTILINE)
-    assert re.search(r'32.7767', out2, re.MULTILINE)
-    assert re.search(r'-96.797', out2, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_owner() -
-
-

Test –set-owner name

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_owner():
-    """Test --set-owner name"""
-    # make sure the owner is not Joe
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Bob')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Bob', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Joe')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_team() -
-
-

Test –set-team

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_team():
-    """Test --set-team """
-    # unset the team
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CLEAR')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CYAN')
-    assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'CYAN', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_seturl_default() -
-
-

Test –seturl with default value

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_seturl_default():
-    """Test --seturl with default value"""
-    # set some channel value so we no longer have a default channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name foo')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure we no longer have a default primary channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-    url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_seturl_invalid_url() -
-
-

Test –seturl with invalid url

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_seturl_invalid_url():
-    """Test --seturl with invalid url"""
-    # Note: This url is no longer a valid url.
-    url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ="
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('Warning: There were no settings', out, re.MULTILINE)
-    assert return_value == 1
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/test/test_util.html b/docs/meshtastic/test/test_util.html deleted file mode 100644 index 9425f37..0000000 --- a/docs/meshtastic/test/test_util.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - - - -meshtastic.test.test_util API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.test.test_util

-
-
-

Meshtastic unit tests for node.py

-
- -Expand source code - -
"""Meshtastic unit tests for node.py"""
-
-import pytest
-
-from meshtastic.util import pskToString, our_exit
-
-@pytest.mark.unit
-def test_pskToString_empty_string():
-    """Test pskToString empty string"""
-    assert pskToString('') == 'unencrypted'
-
-
-@pytest.mark.unit
-def test_pskToString_string():
-    """Test pskToString string"""
-    assert pskToString('hunter123') == 'secret'
-
-
-@pytest.mark.unit
-def test_pskToString_one_byte_zero_value():
-    """Test pskToString one byte that is value of 0"""
-    assert pskToString(bytes([0x00])) == 'unencrypted'
-
-
-@pytest.mark.unit
-def test_pskToString_one_byte_non_zero_value():
-    """Test pskToString one byte that is non-zero"""
-    assert pskToString(bytes([0x01])) == 'default'
-
-
-@pytest.mark.unit
-def test_pskToString_many_bytes():
-    """Test pskToString many bytes"""
-    assert pskToString(bytes([0x02, 0x01])) == 'secret'
-
-
-@pytest.mark.unit
-def test_our_exit_zero_return_value():
-    """Test our_exit with a zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Warning: Some message", 0)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-
-
-@pytest.mark.unit
-def test_our_exit_non_zero_return_value():
-    """Test our_exit with a non-zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Error: Some message", 1)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-
-
-
-
-

Functions

-
-
-def test_our_exit_non_zero_return_value() -
-
-

Test our_exit with a non-zero return value

-
- -Expand source code - -
@pytest.mark.unit
-def test_our_exit_non_zero_return_value():
-    """Test our_exit with a non-zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Error: Some message", 1)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_our_exit_zero_return_value() -
-
-

Test our_exit with a zero return value

-
- -Expand source code - -
@pytest.mark.unit
-def test_our_exit_zero_return_value():
-    """Test our_exit with a zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Warning: Some message", 0)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-
-
-
-def test_pskToString_empty_string() -
-
-

Test pskToString empty string

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_empty_string():
-    """Test pskToString empty string"""
-    assert pskToString('') == 'unencrypted'
-
-
-
-def test_pskToString_many_bytes() -
-
-

Test pskToString many bytes

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_many_bytes():
-    """Test pskToString many bytes"""
-    assert pskToString(bytes([0x02, 0x01])) == 'secret'
-
-
-
-def test_pskToString_one_byte_non_zero_value() -
-
-

Test pskToString one byte that is non-zero

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_one_byte_non_zero_value():
-    """Test pskToString one byte that is non-zero"""
-    assert pskToString(bytes([0x01])) == 'default'
-
-
-
-def test_pskToString_one_byte_zero_value() -
-
-

Test pskToString one byte that is value of 0

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_one_byte_zero_value():
-    """Test pskToString one byte that is value of 0"""
-    assert pskToString(bytes([0x00])) == 'unencrypted'
-
-
-
-def test_pskToString_string() -
-
-

Test pskToString string

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_string():
-    """Test pskToString string"""
-    assert pskToString('hunter123') == 'secret'
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/conftest.html b/docs/meshtastic/tests/conftest.html deleted file mode 100644 index 44956ff..0000000 --- a/docs/meshtastic/tests/conftest.html +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - -meshtastic.tests.conftest API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.conftest

-
-
-

Common pytest code (place for fixtures).

-
- -Expand source code - -
"""Common pytest code (place for fixtures)."""
-
-import argparse
-
-from unittest.mock import MagicMock
-import pytest
-
-from meshtastic.__main__ import Globals
-from ..mesh_interface import MeshInterface
-
-
-@pytest.fixture
-def reset_globals():
-    """Fixture to reset globals."""
-    parser = None
-    parser = argparse.ArgumentParser()
-    Globals.getInstance().reset()
-    Globals.getInstance().set_parser(parser)
-
-
-@pytest.fixture
-def iface_with_nodes():
-    """Fixture to setup some nodes."""
-    nodesById = {
-            '!9388f81c': {
-                'num': 2475227164,
-                'user': {
-                    'id': '!9388f81c',
-                    'longName': 'Unknown f81c',
-                    'shortName': '?1C',
-                    'macaddr': 'RBeTiPgc',
-                    'hwModel': 'TBEAM'
-                    },
-                'position': {},
-                'lastHeard': 1640204888
-                }
-            }
-
-    nodesByNum = {
-            2475227164: {
-                'num': 2475227164,
-                'user': {
-                    'id': '!9388f81c',
-                    'longName': 'Unknown f81c',
-                    'shortName': '?1C',
-                    'macaddr': 'RBeTiPgc',
-                    'hwModel': 'TBEAM'
-                    },
-                'position': {
-                    'time': 1640206266
-                    },
-                'lastHeard': 1640206266
-                }
-            }
-    iface = MeshInterface(noProto=True)
-    iface.nodes = nodesById
-    iface.nodesByNum = nodesByNum
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-    iface.myInfo.my_node_num = 2475227164
-    return iface
-
-
-
-
-
-
-
-

Functions

-
-
-def iface_with_nodes() -
-
-

Fixture to setup some nodes.

-
- -Expand source code - -
@pytest.fixture
-def iface_with_nodes():
-    """Fixture to setup some nodes."""
-    nodesById = {
-            '!9388f81c': {
-                'num': 2475227164,
-                'user': {
-                    'id': '!9388f81c',
-                    'longName': 'Unknown f81c',
-                    'shortName': '?1C',
-                    'macaddr': 'RBeTiPgc',
-                    'hwModel': 'TBEAM'
-                    },
-                'position': {},
-                'lastHeard': 1640204888
-                }
-            }
-
-    nodesByNum = {
-            2475227164: {
-                'num': 2475227164,
-                'user': {
-                    'id': '!9388f81c',
-                    'longName': 'Unknown f81c',
-                    'shortName': '?1C',
-                    'macaddr': 'RBeTiPgc',
-                    'hwModel': 'TBEAM'
-                    },
-                'position': {
-                    'time': 1640206266
-                    },
-                'lastHeard': 1640206266
-                }
-            }
-    iface = MeshInterface(noProto=True)
-    iface.nodes = nodesById
-    iface.nodesByNum = nodesByNum
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-    iface.myInfo.my_node_num = 2475227164
-    return iface
-
-
-
-def reset_globals() -
-
-

Fixture to reset globals.

-
- -Expand source code - -
@pytest.fixture
-def reset_globals():
-    """Fixture to reset globals."""
-    parser = None
-    parser = argparse.ArgumentParser()
-    Globals.getInstance().reset()
-    Globals.getInstance().set_parser(parser)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/index.html b/docs/meshtastic/tests/index.html deleted file mode 100644 index 1e30938..0000000 --- a/docs/meshtastic/tests/index.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - -meshtastic.tests API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests

-
-
-
-
-

Sub-modules

-
-
meshtastic.tests.conftest
-
-

Common pytest code (place for fixtures).

-
-
meshtastic.tests.test_ble_interface
-
-

Meshtastic unit tests for ble_interface.py

-
-
meshtastic.tests.test_examples
-
-

Meshtastic test that the examples run as expected. -We assume you have a python virtual environment in current directory. -If not, you need to run: …

-
-
meshtastic.tests.test_globals
-
-

Meshtastic unit tests for globals.py

-
-
meshtastic.tests.test_int
-
-

Meshtastic integration tests

-
-
meshtastic.tests.test_main
-
-

Meshtastic unit tests for main.py

-
-
meshtastic.tests.test_mesh_interface
-
-

Meshtastic unit tests for mesh_interface.py

-
-
meshtastic.tests.test_node
-
-

Meshtastic unit tests for node.py

-
-
meshtastic.tests.test_remote_hardware
-
-

Meshtastic unit tests for remote_hardware.py

-
-
meshtastic.tests.test_serial_interface
-
-

Meshtastic unit tests for serial_interface.py

-
-
meshtastic.tests.test_smoke1
-
-

Meshtastic smoke tests with a single device via USB

-
-
meshtastic.tests.test_smoke2
-
-

Meshtastic smoke tests with 2 devices connected via USB

-
-
meshtastic.tests.test_smoke_wifi
-
-

Meshtastic smoke tests a device setup with wifi …

-
-
meshtastic.tests.test_stream_interface
-
-

Meshtastic unit tests for stream_interface.py

-
-
meshtastic.tests.test_tcp_interface
-
-

Meshtastic unit tests for tcp_interface.py

-
-
meshtastic.tests.test_util
-
-

Meshtastic unit tests for util.py

-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_ble_interface.html b/docs/meshtastic/tests/test_ble_interface.html deleted file mode 100644 index 8faaf55..0000000 --- a/docs/meshtastic/tests/test_ble_interface.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -meshtastic.tests.test_ble_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_ble_interface

-
-
-

Meshtastic unit tests for ble_interface.py

-
- -Expand source code - -
"""Meshtastic unit tests for ble_interface.py"""
-
-
-import pytest
-
-from ..ble_interface import BLEInterface
-
-@pytest.mark.unit
-def test_BLEInterface():
-    """Test that we can instantiate a BLEInterface"""
-    iface = BLEInterface('foo', debugOut=True, noProto=True)
-    iface.close()
-
-
-
-
-
-
-
-

Functions

-
-
-def test_BLEInterface() -
-
-

Test that we can instantiate a BLEInterface

-
- -Expand source code - -
@pytest.mark.unit
-def test_BLEInterface():
-    """Test that we can instantiate a BLEInterface"""
-    iface = BLEInterface('foo', debugOut=True, noProto=True)
-    iface.close()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_examples.html b/docs/meshtastic/tests/test_examples.html deleted file mode 100644 index 3ae618d..0000000 --- a/docs/meshtastic/tests/test_examples.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - -meshtastic.tests.test_examples API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_examples

-
-
-

Meshtastic test that the examples run as expected. -We assume you have a python virtual environment in current directory. -If not, you need to run: "python3 -m venv venv", "source venv/bin/activate", "pip install ."

-
- -Expand source code - -
"""Meshtastic test that the examples run as expected.
-   We assume you have a python virtual environment in current directory.
-   If not, you need to run: "python3 -m venv venv", "source venv/bin/activate", "pip install ."
-"""
-import subprocess
-
-import pytest
-
-@pytest.mark.examples
-def test_examples_hello_world_serial_no_arg():
-    """Test hello_world_serial without any args"""
-    return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py')
-    assert return_value == 3
-
-
-@pytest.mark.examples
-def test_examples_hello_world_serial_with_arg(capsys):
-    """Test hello_world_serial with arg"""
-    return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py hello')
-    assert return_value == 1
-    _, err = capsys.readouterr()
-    assert err == ''
-    # TODO: Why does this not work?
-    # assert out == 'Warning: No Meshtastic devices detected.'
-
-
-
-
-
-
-
-

Functions

-
-
-def test_examples_hello_world_serial_no_arg() -
-
-

Test hello_world_serial without any args

-
- -Expand source code - -
@pytest.mark.examples
-def test_examples_hello_world_serial_no_arg():
-    """Test hello_world_serial without any args"""
-    return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py')
-    assert return_value == 3
-
-
-
-def test_examples_hello_world_serial_with_arg(capsys) -
-
-

Test hello_world_serial with arg

-
- -Expand source code - -
@pytest.mark.examples
-def test_examples_hello_world_serial_with_arg(capsys):
-    """Test hello_world_serial with arg"""
-    return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py hello')
-    assert return_value == 1
-    _, err = capsys.readouterr()
-    assert err == ''
-    # TODO: Why does this not work?
-    # assert out == 'Warning: No Meshtastic devices detected.'
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_globals.html b/docs/meshtastic/tests/test_globals.html deleted file mode 100644 index 766e975..0000000 --- a/docs/meshtastic/tests/test_globals.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - -meshtastic.tests.test_globals API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_globals

-
-
-

Meshtastic unit tests for globals.py

-
- -Expand source code - -
"""Meshtastic unit tests for globals.py
-"""
-
-import pytest
-
-from ..globals import Globals
-
-
-@pytest.mark.unit
-def test_globals_get_instaance():
-    """Test that we can instantiate a Globals instance"""
-    ourglobals = Globals.getInstance()
-    ourglobals2 = Globals.getInstance()
-    assert ourglobals == ourglobals2
-
-
-@pytest.mark.unit
-def test_globals_there_can_be_only_one():
-    """Test that we can cannot create two Globals instances"""
-    # if we have an instance, delete it
-    Globals.getInstance()
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        # try to create another instance
-        Globals()
-    assert pytest_wrapped_e.type == Exception
-
-
-
-
-
-
-
-

Functions

-
-
-def test_globals_get_instaance() -
-
-

Test that we can instantiate a Globals instance

-
- -Expand source code - -
@pytest.mark.unit
-def test_globals_get_instaance():
-    """Test that we can instantiate a Globals instance"""
-    ourglobals = Globals.getInstance()
-    ourglobals2 = Globals.getInstance()
-    assert ourglobals == ourglobals2
-
-
-
-def test_globals_there_can_be_only_one() -
-
-

Test that we can cannot create two Globals instances

-
- -Expand source code - -
@pytest.mark.unit
-def test_globals_there_can_be_only_one():
-    """Test that we can cannot create two Globals instances"""
-    # if we have an instance, delete it
-    Globals.getInstance()
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        # try to create another instance
-        Globals()
-    assert pytest_wrapped_e.type == Exception
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_int.html b/docs/meshtastic/tests/test_int.html deleted file mode 100644 index 0abfd06..0000000 --- a/docs/meshtastic/tests/test_int.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - -meshtastic.tests.test_int API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_int

-
-
-

Meshtastic integration tests

-
- -Expand source code - -
"""Meshtastic integration tests"""
-import re
-import subprocess
-
-import pytest
-
-
-@pytest.mark.int
-def test_int_no_args():
-    """Test without any args"""
-    return_value, out = subprocess.getstatusoutput('meshtastic')
-    assert re.match(r'usage: meshtastic', out)
-    assert return_value == 1
-
-
-@pytest.mark.int
-def test_int_version():
-    """Test '--version'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --version')
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert return_value == 0
-
-
-@pytest.mark.int
-def test_int_help():
-    """Test '--help'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --help')
-    assert re.match(r'usage: meshtastic ', out)
-    assert return_value == 0
-
-
-@pytest.mark.int
-def test_int_support():
-    """Test '--support'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --support')
-    assert re.search(r'System', out)
-    assert re.search(r'Python', out)
-    assert return_value == 0
-
-
-
-
-
-
-
-

Functions

-
-
-def test_int_help() -
-
-

Test '–help'.

-
- -Expand source code - -
@pytest.mark.int
-def test_int_help():
-    """Test '--help'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --help')
-    assert re.match(r'usage: meshtastic ', out)
-    assert return_value == 0
-
-
-
-def test_int_no_args() -
-
-

Test without any args

-
- -Expand source code - -
@pytest.mark.int
-def test_int_no_args():
-    """Test without any args"""
-    return_value, out = subprocess.getstatusoutput('meshtastic')
-    assert re.match(r'usage: meshtastic', out)
-    assert return_value == 1
-
-
-
-def test_int_support() -
-
-

Test '–support'.

-
- -Expand source code - -
@pytest.mark.int
-def test_int_support():
-    """Test '--support'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --support')
-    assert re.search(r'System', out)
-    assert re.search(r'Python', out)
-    assert return_value == 0
-
-
-
-def test_int_version() -
-
-

Test '–version'.

-
- -Expand source code - -
@pytest.mark.int
-def test_int_version():
-    """Test '--version'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --version')
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert return_value == 0
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_main.html b/docs/meshtastic/tests/test_main.html deleted file mode 100644 index b40d624..0000000 --- a/docs/meshtastic/tests/test_main.html +++ /dev/null @@ -1,3987 +0,0 @@ - - - - - - -meshtastic.tests.test_main API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_main

-
-
-

Meshtastic unit tests for main.py

-
- -Expand source code - -
"""Meshtastic unit tests for __main__.py"""
-# pylint: disable=C0302
-
-import sys
-import os
-import re
-import logging
-
-from unittest.mock import patch, MagicMock
-import pytest
-
-from meshtastic.__main__ import initParser, main, Globals, onReceive, onConnection, export_config, getPref, setPref
-#from ..radioconfig_pb2 import UserPreferences
-import meshtastic.radioconfig_pb2
-from ..serial_interface import SerialInterface
-from ..tcp_interface import TCPInterface
-from ..ble_interface import BLEInterface
-from ..node import Node
-from ..channel_pb2 import Channel
-from ..remote_hardware import onGPIOreceive
-
-
-@pytest.mark.unit
-def test_main_init_parser_no_args(capsys, reset_globals):
-    """Test no arguments"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-    initParser()
-    out, err = capsys.readouterr()
-    assert out == ''
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_init_parser_version(capsys, reset_globals):
-    """Test --version"""
-    sys.argv = ['', '--version']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        initParser()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    out, err = capsys.readouterr()
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_main_version(capsys, reset_globals):
-    """Test --version"""
-    sys.argv = ['', '--version']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    out, err = capsys.readouterr()
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_main_no_args(reset_globals):
-    """Test with no args"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_main_support(capsys, reset_globals):
-    """Test --support"""
-    sys.argv = ['', '--support']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    out, err = capsys.readouterr()
-    assert re.search(r'System', out, re.MULTILINE)
-    assert re.search(r'Platform', out, re.MULTILINE)
-    assert re.search(r'Machine', out, re.MULTILINE)
-    assert re.search(r'Executable', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=[])
-def test_main_ch_index_no_devices(patched_find_ports, capsys, reset_globals):
-    """Test --ch-index 1"""
-    sys.argv = ['', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert Globals.getInstance().get_channel_index() == 1
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: No Meshtastic devices detected', out, re.MULTILINE)
-    assert err == ''
-    patched_find_ports.assert_called()
-
-
-@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=[])
-def test_main_test_no_ports(patched_find_ports, reset_globals):
-    """Test --test with no hardware"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    assert Globals.getInstance().get_target_node() is None
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    patched_find_ports.assert_called()
-
-
-@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1'])
-def test_main_test_one_port(patched_find_ports, reset_globals):
-    """Test --test with one fake port"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    assert Globals.getInstance().get_target_node() is None
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    patched_find_ports.assert_called()
-
-
-@pytest.mark.unit
-@patch('meshtastic.test.testAll', return_value=True)
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1', '/dev/ttyFake2'])
-def test_main_test_two_ports_success(patched_find_ports, patched_test_all, reset_globals):
-    """Test --test two fake ports and testAll() is a simulated success"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    # TODO: why does this fail? patched_find_ports.assert_called()
-    patched_test_all.assert_called()
-
-
-@pytest.mark.unit
-@patch('meshtastic.test.testAll', return_value=False)
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1', '/dev/ttyFake2'])
-def test_main_test_two_ports_fails(patched_find_ports, patched_test_all, reset_globals):
-    """Test --test two fake ports and testAll() is a simulated failure"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    # TODO: why does this fail? patched_find_ports.assert_called()
-    patched_test_all.assert_called()
-
-
-@pytest.mark.unit
-def test_main_info(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_info_with_tcp_interface(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--host', 'meshtastic.local']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=TCPInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.tcp_interface.TCPInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_info_with_ble_interface(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--ble', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=BLEInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.ble_interface.BLEInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_no_proto(capsys, reset_globals):
-    """Test --noproto (using --info for output)"""
-    sys.argv = ['', '--info', '--noproto']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-
-    # Override the time.sleep so there is no loop
-    def my_sleep(amount):
-        sys.exit(0)
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with patch('time.sleep', side_effect=my_sleep):
-            with pytest.raises(SystemExit) as pytest_wrapped_e:
-                main()
-            assert pytest_wrapped_e.type == SystemExit
-            assert pytest_wrapped_e.value.code == 0
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-            assert err == ''
-
-
-@pytest.mark.unit
-def test_main_info_with_seriallog_stdout(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--seriallog', 'stdout']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_info_with_seriallog_output_txt(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--seriallog', 'output.txt']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-    # do some cleanup
-    os.remove('output.txt')
-
-
-@pytest.mark.unit
-def test_main_qr(capsys, reset_globals):
-    """Test --qr"""
-    sys.argv = ['', '--qr']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    # TODO: could mock/check url
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Primary channel URL', out, re.MULTILINE)
-        # if a qr code is generated it will have lots of these
-        assert re.search(r'\[7m', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_nodes(capsys, reset_globals):
-    """Test --nodes"""
-    sys.argv = ['', '--nodes']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showNodes():
-        print('inside mocked showNodes')
-    iface.showNodes.side_effect = mock_showNodes
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showNodes', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_set_owner_to_bob(capsys, reset_globals):
-    """Test --set-owner bob"""
-    sys.argv = ['', '--set-owner', 'bob']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Setting device owner to bob', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_set_ham_to_KI123(capsys, reset_globals):
-    """Test --set-ham KI123"""
-    sys.argv = ['', '--set-ham', 'KI123']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_turnOffEncryptionOnPrimaryChannel():
-        print('inside mocked turnOffEncryptionOnPrimaryChannel')
-    def mock_setOwner(name, is_licensed):
-        print('inside mocked setOwner')
-    mocked_node.turnOffEncryptionOnPrimaryChannel.side_effect = mock_turnOffEncryptionOnPrimaryChannel
-    mocked_node.setOwner.side_effect = mock_setOwner
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Setting Ham ID to KI123', out, re.MULTILINE)
-        assert re.search(r'inside mocked setOwner', out, re.MULTILINE)
-        assert re.search(r'inside mocked turnOffEncryptionOnPrimaryChannel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_reboot(capsys, reset_globals):
-    """Test --reboot"""
-    sys.argv = ['', '--reboot']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_reboot():
-        print('inside mocked reboot')
-    mocked_node.reboot.side_effect = mock_reboot
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked reboot', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_sendtext(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendText(text, dest, wantAck, channelIndex):
-        print('inside mocked sendText')
-    iface.sendText.side_effect = mock_sendText
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending text message', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendText', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_sendtext_with_channel(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendText(text, dest, wantAck, channelIndex):
-        print('inside mocked sendText')
-    iface.sendText.side_effect = mock_sendText
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending text message', out, re.MULTILINE)
-        assert re.search(r'on channelIndex:1', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendText', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_sendtext_with_invalid_channel(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello', '--ch-index', '-1']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getChannelByChannelIndex.return_value = None
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        iface.getNode.return_value.getChannelByChannelIndex.return_value = None
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'is not a valid channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_sendtext_with_invalid_channel_nine(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello', '--ch-index', '9']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        iface.getNode.return_value.getChannelByChannelIndex.return_value = None
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'is not a valid channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_sendtext_with_dest(capsys, reset_globals):
-    """Test --sendtext with --dest"""
-    sys.argv = ['', '--sendtext', 'hello', '--dest', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendText(text, dest, wantAck, channelIndex):
-        print('inside mocked sendText')
-    iface.sendText.side_effect = mock_sendText
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending text message', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendText', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_sendping(capsys, reset_globals):
-    """Test --sendping"""
-    sys.argv = ['', '--sendping']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendData(payload, dest, portNum, wantAck, wantResponse):
-        print('inside mocked sendData')
-    iface.sendData.side_effect = mock_sendData
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending ping message', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendData', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_setlat(capsys, reset_globals):
-    """Test --sendlat"""
-    sys.argv = ['', '--setlat', '37.5']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_writeConfig():
-        print('inside mocked writeConfig')
-    mocked_node.writeConfig.side_effect = mock_writeConfig
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendPosition(lat, lon, alt):
-        print('inside mocked sendPosition')
-    iface.sendPosition.side_effect = mock_sendPosition
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Fixing latitude', out, re.MULTILINE)
-        assert re.search(r'Setting device position', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendPosition', out, re.MULTILINE)
-        # TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_setlon(capsys, reset_globals):
-    """Test --setlon"""
-    sys.argv = ['', '--setlon', '-122.1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_writeConfig():
-        print('inside mocked writeConfig')
-    mocked_node.writeConfig.side_effect = mock_writeConfig
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendPosition(lat, lon, alt):
-        print('inside mocked sendPosition')
-    iface.sendPosition.side_effect = mock_sendPosition
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Fixing longitude', out, re.MULTILINE)
-        assert re.search(r'Setting device position', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendPosition', out, re.MULTILINE)
-        # TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_setalt(capsys, reset_globals):
-    """Test --setalt"""
-    sys.argv = ['', '--setalt', '51']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_writeConfig():
-        print('inside mocked writeConfig')
-    mocked_node.writeConfig.side_effect = mock_writeConfig
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendPosition(lat, lon, alt):
-        print('inside mocked sendPosition')
-    iface.sendPosition.side_effect = mock_sendPosition
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Fixing altitude', out, re.MULTILINE)
-        assert re.search(r'Setting device position', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendPosition', out, re.MULTILINE)
-        # TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_set_team_valid(capsys, reset_globals):
-    """Test --set-team"""
-    sys.argv = ['', '--set-team', 'CYAN']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_setOwner(team):
-        print('inside mocked setOwner')
-    mocked_node.setOwner.side_effect = mock_setOwner
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with patch('meshtastic.mesh_pb2.Team') as mm:
-            mm.Name.return_value = 'FAKENAME'
-            mm.Value.return_value = 'FAKEVAL'
-            main()
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'Setting team to', out, re.MULTILINE)
-            assert err == ''
-            mo.assert_called()
-            mm.Name.assert_called()
-            mm.Value.assert_called()
-
-
-@pytest.mark.unit
-def test_main_set_team_invalid(capsys, reset_globals):
-    """Test --set-team using an invalid team name"""
-    sys.argv = ['', '--set-team', 'NOTCYAN']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-
-    def throw_an_exception(exc):
-        raise ValueError("Fake exception.")
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with patch('meshtastic.mesh_pb2.Team') as mm:
-            mm.Value.side_effect = throw_an_exception
-            main()
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'ERROR: Team', out, re.MULTILINE)
-            assert err == ''
-            mo.assert_called()
-            mm.Value.assert_called()
-
-
-@pytest.mark.unit
-def test_main_seturl(capsys, reset_globals):
-    """Test --seturl (url used below is what is generated after a factory_reset)"""
-    sys.argv = ['', '--seturl', 'https://www.meshtastic.org/d/#CgUYAyIBAQ']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_set_valid(capsys, reset_globals):
-    """Test --set with valid field"""
-    sys.argv = ['', '--set', 'wifi_ssid', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Set wifi_ssid to foo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_set_with_invalid(capsys, reset_globals):
-    """Test --set with invalid field"""
-    sys.argv = ['', '--set', 'foo', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_user_prefs = MagicMock()
-    mocked_user_prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    mocked_node = MagicMock(autospec=Node)
-    mocked_node.radioConfig.preferences = ( mocked_user_prefs )
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-# TODO: write some negative --configure tests
-@pytest.mark.unit
-def test_main_configure(capsys, reset_globals):
-    """Test --configure with valid file"""
-    sys.argv = ['', '--configure', 'example_config.yaml']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Setting device owner', out, re.MULTILINE)
-        assert re.search(r'Setting channel url', out, re.MULTILINE)
-        assert re.search(r'Fixing altitude', out, re.MULTILINE)
-        assert re.search(r'Fixing latitude', out, re.MULTILINE)
-        assert re.search(r'Fixing longitude', out, re.MULTILINE)
-        assert re.search(r'Writing modified preferences', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_add_valid(capsys, reset_globals):
-    """Test --ch-add with valid channel name, and that channel name does not already exist"""
-    sys.argv = ['', '--ch-add', 'testing']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_channel = MagicMock(autospec=Channel)
-    # TODO: figure out how to get it to print the channel name instead of MagicMock
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = False
-    # set it up so we have free channels
-    mocked_node.getDisabledChannel.return_value = mocked_channel
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_add_invalid_name_too_long(capsys, reset_globals):
-    """Test --ch-add with invalid channel name, name too long"""
-    sys.argv = ['', '--ch-add', 'testingtestingtesting']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_channel = MagicMock(autospec=Channel)
-    # TODO: figure out how to get it to print the channel name instead of MagicMock
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = False
-    # set it up so we have free channels
-    mocked_node.getDisabledChannel.return_value = mocked_channel
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Channel name must be shorter', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_add_but_name_already_exists(capsys, reset_globals):
-    """Test --ch-add with a channel name that already exists"""
-    sys.argv = ['', '--ch-add', 'testing']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = True
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: This node already has', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_add_but_no_more_channels(capsys, reset_globals):
-    """Test --ch-add with but there are no more channels"""
-    sys.argv = ['', '--ch-add', 'testing']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = False
-    # set it up so we have free channels
-    mocked_node.getDisabledChannel.return_value = None
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: No free channels were found', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_del(capsys, reset_globals):
-    """Test --ch-del with valid secondary channel to be deleted"""
-    sys.argv = ['', '--ch-del', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Deleting channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_del_no_ch_index_specified(capsys, reset_globals):
-    """Test --ch-del without a valid ch-index"""
-    sys.argv = ['', '--ch-del']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_del_primary_channel(capsys, reset_globals):
-    """Test --ch-del on ch-index=0"""
-    sys.argv = ['', '--ch-del', '--ch-index', '0']
-    Globals.getInstance().set_args(sys.argv)
-    Globals.getInstance().set_channel_index(1)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Cannot delete primary channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_enable_valid_secondary_channel(capsys, reset_globals):
-    """Test --ch-enable with --ch-index"""
-    sys.argv = ['', '--ch-enable', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Writing modified channels', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() == 1
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_disable_valid_secondary_channel(capsys, reset_globals):
-    """Test --ch-disable with --ch-index"""
-    sys.argv = ['', '--ch-disable', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Writing modified channels', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() == 1
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_enable_without_a_ch_index(capsys, reset_globals):
-    """Test --ch-enable without --ch-index"""
-    sys.argv = ['', '--ch-enable']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() is None
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_enable_primary_channel(capsys, reset_globals):
-    """Test --ch-enable with --ch-index = 0"""
-    sys.argv = ['', '--ch-enable', '--ch-index', '0']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Cannot enable/disable PRIMARY', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() == 0
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_range_options(capsys, reset_globals):
-    """Test changing the various range options."""
-    range_options = ['--ch-longslow', '--ch-longfast', '--ch-mediumslow',
-                     '--ch-mediumfast', '--ch-shortslow', '--ch-shortfast']
-    for range_option in range_options:
-        sys.argv = ['', f"{range_option}" ]
-        Globals.getInstance().set_args(sys.argv)
-
-        mocked_node = MagicMock(autospec=Node)
-
-        iface = MagicMock(autospec=SerialInterface)
-        iface.getNode.return_value = mocked_node
-
-        with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-            main()
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'Writing modified channels', out, re.MULTILINE)
-            assert err == ''
-            mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_ch_longsfast_on_non_primary_channel(capsys, reset_globals):
-    """Test --ch-longfast --ch-index 1"""
-    sys.argv = ['', '--ch-longfast', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Standard channel settings', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-# PositionFlags:
-# Misc info that might be helpful (this info will grow stale, just
-# a snapshot of the values.) The radioconfig_pb2.PositionFlags.Name and bit values are:
-# POS_UNDEFINED 0
-# POS_ALTITUDE 1
-# POS_ALT_MSL 2
-# POS_GEO_SEP 4
-# POS_DOP 8
-# POS_HVDOP 16
-# POS_BATTERY 32
-# POS_SATINVIEW 64
-# POS_SEQ_NOS 128
-# POS_TIMESTAMP 256
-
-@pytest.mark.unit
-def test_main_pos_fields_no_args(capsys, reset_globals):
-    """Test --pos-fields no args (which shows settings)"""
-    sys.argv = ['', '--pos-fields']
-    Globals.getInstance().set_args(sys.argv)
-
-    pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-        with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc:
-            # kind of cheating here, we are setting up the node
-            mocked_node = MagicMock(autospec=Node)
-            anode = mocked_node()
-            anode.radioConfig.preferences.position_flags = 35
-            Globals.getInstance().set_target_node(anode)
-
-            mrc.values.return_value = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256]
-            # Note: When you use side_effect and a list, each call will use a value from the front of the list then
-            # remove that value from the list. If there are three values in the list, we expect it to be called
-            # three times.
-            mrc.Name.side_effect = [ 'POS_ALTITUDE', 'POS_ALT_MSL', 'POS_BATTERY' ]
-
-            main()
-
-            mrc.Name.assert_called()
-            mrc.values.assert_called()
-            mo.assert_called()
-
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'POS_ALTITUDE POS_ALT_MSL POS_BATTERY', out, re.MULTILINE)
-            assert err == ''
-
-
-@pytest.mark.unit
-def test_main_pos_fields_arg_of_zero(capsys, reset_globals):
-    """Test --pos-fields an arg of 0 (which shows list)"""
-    sys.argv = ['', '--pos-fields', '0']
-    Globals.getInstance().set_args(sys.argv)
-
-    pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-        with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc:
-
-            def throw_value_error_exception(exc):
-                raise ValueError()
-            mrc.Value.side_effect = throw_value_error_exception
-            mrc.keys.return_value = [ 'POS_UNDEFINED', 'POS_ALTITUDE', 'POS_ALT_MSL',
-                                      'POS_GEO_SEP', 'POS_DOP', 'POS_HVDOP', 'POS_BATTERY',
-                                      'POS_SATINVIEW', 'POS_SEQ_NOS', 'POS_TIMESTAMP']
-
-            main()
-
-            mrc.Value.assert_called()
-            mrc.keys.assert_called()
-            mo.assert_called()
-
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'ERROR: supported position fields are:', out, re.MULTILINE)
-            assert re.search(r"['POS_UNDEFINED', 'POS_ALTITUDE', 'POS_ALT_MSL', 'POS_GEO_SEP',"\
-                              "'POS_DOP', 'POS_HVDOP', 'POS_BATTERY', 'POS_SATINVIEW', 'POS_SEQ_NOS',"\
-                              "'POS_TIMESTAMP']", out, re.MULTILINE)
-            assert err == ''
-
-
-@pytest.mark.unit
-def test_main_pos_fields_valid_values(capsys, reset_globals):
-    """Test --pos-fields with valid values"""
-    sys.argv = ['', '--pos-fields', 'POS_GEO_SEP', 'POS_ALT_MSL']
-    Globals.getInstance().set_args(sys.argv)
-
-    pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-        with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc:
-
-            mrc.Value.side_effect = [ 4, 2 ]
-
-            main()
-
-            mrc.Value.assert_called()
-            mo.assert_called()
-
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'Setting position fields to 6', out, re.MULTILINE)
-            assert re.search(r'Set position_flags to 6', out, re.MULTILINE)
-            assert re.search(r'Writing modified preferences to device', out, re.MULTILINE)
-            assert err == ''
-
-
-@pytest.mark.unit
-def test_main_get_with_valid_values(capsys, reset_globals):
-    """Test --get with valid values (with string, number, boolean)"""
-    sys.argv = ['', '--get', 'ls_secs', '--get', 'wifi_ssid', '--get', 'fixed_position']
-    Globals.getInstance().set_args(sys.argv)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-
-        # kind of cheating here, we are setting up the node
-        mocked_node = MagicMock(autospec=Node)
-        anode = mocked_node()
-        anode.radioConfig.preferences.wifi_ssid = 'foo'
-        anode.radioConfig.preferences.ls_secs = 300
-        anode.radioConfig.preferences.fixed_position = False
-        Globals.getInstance().set_target_node(anode)
-
-        main()
-
-        mo.assert_called()
-
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'ls_secs: 300', out, re.MULTILINE)
-        assert re.search(r'wifi_ssid: foo', out, re.MULTILINE)
-        assert re.search(r'fixed_position: False', out, re.MULTILINE)
-        assert err == ''
-
-
-@pytest.mark.unit
-def test_main_get_with_invalid(capsys, reset_globals):
-    """Test --get with invalid field"""
-    sys.argv = ['', '--get', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_user_prefs = MagicMock()
-    mocked_user_prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    mocked_node = MagicMock(autospec=Node)
-    mocked_node.radioConfig.preferences = ( mocked_user_prefs )
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-        assert re.search(r'Choices in sorted order are', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_setchan(capsys, reset_globals):
-    """Test --setchan (deprecated)"""
-    sys.argv = ['', '--setchan', 'a', 'b']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_main_onReceive_empty(caplog, reset_globals):
-    """Test onReceive"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-    iface = MagicMock(autospec=SerialInterface)
-    packet = {'decoded': 'foo'}
-    with caplog.at_level(logging.DEBUG):
-        onReceive(packet, iface)
-    assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
-
-
-#    TODO: use this captured position app message (might want/need in the future)
-#    packet = {
-#            'to': 4294967295,
-#            'decoded': {
-#                'portnum': 'POSITION_APP',
-#                'payload': "M69\306a"
-#                },
-#            'id': 334776976,
-#            'hop_limit': 3
-#            }
-
-@pytest.mark.unit
-def test_main_onReceive_with_sendtext(caplog, reset_globals):
-    """Test onReceive with sendtext
-       The entire point of this test is to make sure the interface.close() call
-       is made in onReceive().
-    """
-    sys.argv = ['', '--sendtext', 'hello']
-    Globals.getInstance().set_args(sys.argv)
-
-    # Note: 'TEXT_MESSAGE_APP' value is 1
-    packet = {
-            'to': 4294967295,
-            'decoded': {
-                'portnum': 1,
-                'payload': "hello"
-                },
-            'id': 334776977,
-            'hop_limit': 3,
-            'want_ack': True
-            }
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.myInfo.my_node_num = 4294967295
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with caplog.at_level(logging.DEBUG):
-            main()
-            onReceive(packet, iface)
-        assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_onReceive_with_reply(caplog, capsys, reset_globals):
-    """Test onReceive with a reply
-       To capture: on one device run '--sendtext aaa --reply' and on another
-       device run '--sendtext bbb --reply', then back to the first device and
-       run '--sendtext aaa2 --reply'. You should now see a "Sending reply" message.
-    """
-    sys.argv = ['', '--sendtext', 'hello', '--reply']
-    Globals.getInstance().set_args(sys.argv)
-
-    # Note: 'TEXT_MESSAGE_APP' value is 1
-
-    send_packet = {
-            'to': 4294967295,
-            'decoded': {
-                'portnum': 1,
-                'payload': "hello"
-                },
-            'id': 334776977,
-            'hop_limit': 3,
-            'want_ack': True
-            }
-
-    reply_packet = {
-            'from': 682968668,
-            'to': 4294967295,
-            'decoded': {
-                'portnum': 'TEXT_MESSAGE_APP',
-                'payload': b'bbb',
-                'text': 'bbb'
-                },
-            'id': 1709936182,
-            'rxTime': 1640381999,
-            'rxSnr': 6.0,
-            'hopLimit': 3,
-            'raw': 'faked',
-            'fromId': '!28b5465c',
-            'toId': '^all'
-            }
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.myInfo.my_node_num = 4294967295
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with caplog.at_level(logging.DEBUG):
-            main()
-            onReceive(send_packet, iface)
-            onReceive(reply_packet, iface)
-        assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
-        out, err = capsys.readouterr()
-        assert re.search(r'got msg ', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_onConnection(reset_globals, capsys):
-    """Test onConnection"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-    iface = MagicMock(autospec=SerialInterface)
-    class TempTopic:
-        """ temp class for topic """
-        def getName(self):
-            """ return the fake name of a topic"""
-            return 'foo'
-    mytopic = TempTopic()
-    onConnection(iface, mytopic)
-    out, err = capsys.readouterr()
-    assert re.search(r'Connection changed: foo', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_export_config(reset_globals, capsys):
-    """Test export_config() function directly"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.getLongName.return_value = 'foo'
-        mo.localNode.getURL.return_value = 'bar'
-        mo.getMyNodeInfo().get.return_value = { 'latitudeI': 1100000000, 'longitudeI': 1200000000,
-                                                'altitude': 100, 'batteryLevel': 34, 'latitude': 110.0,
-                                                'longitude': 120.0}
-        mo.localNode.radioConfig.preferences = """phone_timeout_secs: 900
-ls_secs: 300
-position_broadcast_smart: true
-fixed_position: true
-position_flags: 35"""
-        export_config(mo)
-    out, err = capsys.readouterr()
-    assert re.search(r'owner: foo', out, re.MULTILINE)
-    assert re.search(r'channel_url: bar', out, re.MULTILINE)
-    assert re.search(r'location:', out, re.MULTILINE)
-    assert re.search(r'lat: 110.0', out, re.MULTILINE)
-    assert re.search(r'lon: 120.0', out, re.MULTILINE)
-    assert re.search(r'alt: 100', out, re.MULTILINE)
-    assert re.search(r'user_prefs:', out, re.MULTILINE)
-    assert re.search(r'phone_timeout_secs: 900', out, re.MULTILINE)
-    assert re.search(r'ls_secs: 300', out, re.MULTILINE)
-    assert re.search(r"position_broadcast_smart: 'true'", out, re.MULTILINE)
-    assert re.search(r"fixed_position: 'true'", out, re.MULTILINE)
-    assert re.search(r"position_flags: 35", out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_export_config_called_from_main(capsys, reset_globals):
-    """Test --export-config"""
-    sys.argv = ['', '--export-config']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'# start of Meshtastic configure yaml', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-@pytest.mark.unit
-def test_main_gpio_rd_no_gpio_channel(capsys, reset_globals):
-    """Test --gpio_rd with no named gpio channel"""
-    sys.argv = ['', '--gpio-rd', '0x10']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.getChannelByName.return_value = None
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Warning: No channel named', out)
-        assert err == ''
-
-
-@pytest.mark.unit
-def test_main_gpio_rd_no_dest(capsys, reset_globals):
-    """Test --gpio_rd with a named gpio channel but no dest was specified"""
-    sys.argv = ['', '--gpio-rd', '0x2000']
-    Globals.getInstance().set_args(sys.argv)
-
-    channel = Channel(index=1, role=1)
-    channel.settings.modem_config = 3
-    channel.settings.psk = b'\x01'
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.getChannelByName.return_value = channel
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Warning: Must use a destination node ID', out)
-        assert err == ''
-
-
-@pytest.mark.unit
-def test_main_gpio_rd(caplog, capsys, reset_globals):
-    """Test --gpio_rd with a named gpio channel"""
-    # Note: On the Heltec v2.1, there is a GPIO pin GPIO 13 that does not have a
-    # red arrow (meaning ok to use for our purposes)
-    # See https://resource.heltec.cn/download/WiFi_LoRa_32/WIFI_LoRa_32_V2.pdf
-    # To find out the mask for GPIO 13, let us assign n as 13.
-    # 1. Subtract 1 from n (n is now 12)
-    # 2. Find the 2^n or 2^12 (4096)
-    # 3. Convert 4096 decimal to hex (0x1000)
-    # You can use python:
-    # >>> print(hex(2**12))
-    # 0x1000
-    sys.argv = ['', '--gpio-rd', '0x1000', '--dest', '!1234']
-    Globals.getInstance().set_args(sys.argv)
-
-    channel = Channel(index=1, role=1)
-    channel.settings.modem_config = 3
-    channel.settings.psk = b'\x01'
-
-    packet = {
-
-            'from': 682968668,
-            'to': 682968612,
-            'channel': 1,
-            'decoded': {
-                'portnum': 'REMOTE_HARDWARE_APP',
-                'payload': b'\x08\x05\x18\x80 ',
-                'requestId': 1629980484,
-                'remotehw': {
-                    'typ': 'READ_GPIOS_REPLY',
-                    'gpioValue': '4096',
-                    'raw': 'faked',
-                    'id': 1693085229,
-                    'rxTime': 1640294262,
-                    'rxSnr': 4.75,
-                    'hopLimit': 3,
-                    'wantAck': True,
-                    }
-                }
-            }
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.getChannelByName.return_value = channel
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with caplog.at_level(logging.DEBUG):
-            main()
-            onGPIOreceive(packet, mo)
-    assert re.search(r'readGPIOs nodeid:!1234 mask:4096', caplog.text, re.MULTILINE)
-    out, err = capsys.readouterr()
-    assert re.search(r'Connected to radio', out, re.MULTILINE)
-    assert re.search(r'Reading GPIO mask 0x1000 ', out, re.MULTILINE)
-    assert re.search(r'Received RemoteHardware typ=READ_GPIOS_REPLY, gpio_value=4096', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_getPref_valid_field(capsys, reset_globals):
-    """Test getPref() with a valid field"""
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = 'ls_secs'
-    prefs.wifi_ssid = 'foo'
-    prefs.ls_secs = 300
-    prefs.fixed_position = False
-
-    getPref(prefs, 'ls_secs')
-    out, err = capsys.readouterr()
-    assert re.search(r'ls_secs: 300', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_getPref_invalid_field(capsys, reset_globals):
-    """Test getPref() with an invalid field"""
-
-    class Field:
-        """Simple class for testing."""
-
-        def __init__(self, name):
-            """constructor"""
-            self.name = name
-
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    # Note: This is a subset of the real fields
-    ls_secs_field = Field('ls_secs')
-    is_router = Field('is_router')
-    fixed_position = Field('fixed_position')
-
-    fields = [ ls_secs_field, is_router, fixed_position ]
-    prefs.DESCRIPTOR.fields = fields
-
-    getPref(prefs, 'foo')
-
-    out, err = capsys.readouterr()
-    assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-    # ensure they are sorted
-    assert re.search(r'fixed_position\s+is_router\s+ls_secs', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_setPref_valid_field(capsys, reset_globals):
-    """Test setPref() with a valid field"""
-
-    class Field:
-        """Simple class for testing."""
-
-        def __init__(self, name, enum_type):
-            """constructor"""
-            self.name = name
-            self.enum_type = enum_type
-
-    ls_secs_field = Field('ls_secs', 'int')
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = ls_secs_field
-
-    setPref(prefs, 'ls_secs', '300')
-    out, err = capsys.readouterr()
-    assert re.search(r'Set ls_secs to 300', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_main_setPref_invalid_field(capsys, reset_globals):
-    """Test setPref() with a invalid field"""
-
-
-    class Field:
-        """Simple class for testing."""
-
-        def __init__(self, name):
-            """constructor"""
-            self.name = name
-
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    # Note: This is a subset of the real fields
-    ls_secs_field = Field('ls_secs')
-    is_router = Field('is_router')
-    fixed_position = Field('fixed_position')
-
-    fields = [ ls_secs_field, is_router, fixed_position ]
-    prefs.DESCRIPTOR.fields = fields
-
-    setPref(prefs, 'foo', '300')
-    out, err = capsys.readouterr()
-    assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-    # ensure they are sorted
-    assert re.search(r'fixed_position\s+is_router\s+ls_secs', out, re.MULTILINE)
-    assert err == ''
-
-
-
-
-
-
-
-

Functions

-
-
-def test_main_ch_add_but_name_already_exists(capsys, reset_globals) -
-
-

Test –ch-add with a channel name that already exists

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_add_but_name_already_exists(capsys, reset_globals):
-    """Test --ch-add with a channel name that already exists"""
-    sys.argv = ['', '--ch-add', 'testing']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = True
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: This node already has', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_add_but_no_more_channels(capsys, reset_globals) -
-
-

Test –ch-add with but there are no more channels

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_add_but_no_more_channels(capsys, reset_globals):
-    """Test --ch-add with but there are no more channels"""
-    sys.argv = ['', '--ch-add', 'testing']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = False
-    # set it up so we have free channels
-    mocked_node.getDisabledChannel.return_value = None
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: No free channels were found', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_add_invalid_name_too_long(capsys, reset_globals) -
-
-

Test –ch-add with invalid channel name, name too long

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_add_invalid_name_too_long(capsys, reset_globals):
-    """Test --ch-add with invalid channel name, name too long"""
-    sys.argv = ['', '--ch-add', 'testingtestingtesting']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_channel = MagicMock(autospec=Channel)
-    # TODO: figure out how to get it to print the channel name instead of MagicMock
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = False
-    # set it up so we have free channels
-    mocked_node.getDisabledChannel.return_value = mocked_channel
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Channel name must be shorter', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_add_valid(capsys, reset_globals) -
-
-

Test –ch-add with valid channel name, and that channel name does not already exist

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_add_valid(capsys, reset_globals):
-    """Test --ch-add with valid channel name, and that channel name does not already exist"""
-    sys.argv = ['', '--ch-add', 'testing']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_channel = MagicMock(autospec=Channel)
-    # TODO: figure out how to get it to print the channel name instead of MagicMock
-
-    mocked_node = MagicMock(autospec=Node)
-    # set it up so we do not already have a channel named this
-    mocked_node.getChannelByName.return_value = False
-    # set it up so we have free channels
-    mocked_node.getDisabledChannel.return_value = mocked_channel
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_del(capsys, reset_globals) -
-
-

Test –ch-del with valid secondary channel to be deleted

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_del(capsys, reset_globals):
-    """Test --ch-del with valid secondary channel to be deleted"""
-    sys.argv = ['', '--ch-del', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Deleting channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_del_no_ch_index_specified(capsys, reset_globals) -
-
-

Test –ch-del without a valid ch-index

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_del_no_ch_index_specified(capsys, reset_globals):
-    """Test --ch-del without a valid ch-index"""
-    sys.argv = ['', '--ch-del']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_del_primary_channel(capsys, reset_globals) -
-
-

Test –ch-del on ch-index=0

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_del_primary_channel(capsys, reset_globals):
-    """Test --ch-del on ch-index=0"""
-    sys.argv = ['', '--ch-del', '--ch-index', '0']
-    Globals.getInstance().set_args(sys.argv)
-    Globals.getInstance().set_channel_index(1)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Cannot delete primary channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_disable_valid_secondary_channel(capsys, reset_globals) -
-
-

Test –ch-disable with –ch-index

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_disable_valid_secondary_channel(capsys, reset_globals):
-    """Test --ch-disable with --ch-index"""
-    sys.argv = ['', '--ch-disable', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Writing modified channels', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() == 1
-        mo.assert_called()
-
-
-
-def test_main_ch_enable_primary_channel(capsys, reset_globals) -
-
-

Test –ch-enable with –ch-index = 0

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_enable_primary_channel(capsys, reset_globals):
-    """Test --ch-enable with --ch-index = 0"""
-    sys.argv = ['', '--ch-enable', '--ch-index', '0']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Cannot enable/disable PRIMARY', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() == 0
-        mo.assert_called()
-
-
-
-def test_main_ch_enable_valid_secondary_channel(capsys, reset_globals) -
-
-

Test –ch-enable with –ch-index

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_enable_valid_secondary_channel(capsys, reset_globals):
-    """Test --ch-enable with --ch-index"""
-    sys.argv = ['', '--ch-enable', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Writing modified channels', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() == 1
-        mo.assert_called()
-
-
-
-def test_main_ch_enable_without_a_ch_index(capsys, reset_globals) -
-
-

Test –ch-enable without –ch-index

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_enable_without_a_ch_index(capsys, reset_globals):
-    """Test --ch-enable without --ch-index"""
-    sys.argv = ['', '--ch-enable']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-        assert err == ''
-        assert Globals.getInstance().get_channel_index() is None
-        mo.assert_called()
-
-
-
-def test_main_ch_index_no_devices(patched_find_ports, capsys, reset_globals) -
-
-

Test –ch-index 1

-
- -Expand source code - -
@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=[])
-def test_main_ch_index_no_devices(patched_find_ports, capsys, reset_globals):
-    """Test --ch-index 1"""
-    sys.argv = ['', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert Globals.getInstance().get_channel_index() == 1
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: No Meshtastic devices detected', out, re.MULTILINE)
-    assert err == ''
-    patched_find_ports.assert_called()
-
-
-
-def test_main_ch_longsfast_on_non_primary_channel(capsys, reset_globals) -
-
-

Test –ch-longfast –ch-index 1

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_longsfast_on_non_primary_channel(capsys, reset_globals):
-    """Test --ch-longfast --ch-index 1"""
-    sys.argv = ['', '--ch-longfast', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Warning: Standard channel settings', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_ch_range_options(capsys, reset_globals) -
-
-

Test changing the various range options.

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_ch_range_options(capsys, reset_globals):
-    """Test changing the various range options."""
-    range_options = ['--ch-longslow', '--ch-longfast', '--ch-mediumslow',
-                     '--ch-mediumfast', '--ch-shortslow', '--ch-shortfast']
-    for range_option in range_options:
-        sys.argv = ['', f"{range_option}" ]
-        Globals.getInstance().set_args(sys.argv)
-
-        mocked_node = MagicMock(autospec=Node)
-
-        iface = MagicMock(autospec=SerialInterface)
-        iface.getNode.return_value = mocked_node
-
-        with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-            main()
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'Writing modified channels', out, re.MULTILINE)
-            assert err == ''
-            mo.assert_called()
-
-
-
-def test_main_configure(capsys, reset_globals) -
-
-

Test –configure with valid file

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_configure(capsys, reset_globals):
-    """Test --configure with valid file"""
-    sys.argv = ['', '--configure', 'example_config.yaml']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Setting device owner', out, re.MULTILINE)
-        assert re.search(r'Setting channel url', out, re.MULTILINE)
-        assert re.search(r'Fixing altitude', out, re.MULTILINE)
-        assert re.search(r'Fixing latitude', out, re.MULTILINE)
-        assert re.search(r'Fixing longitude', out, re.MULTILINE)
-        assert re.search(r'Writing modified preferences', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_export_config(reset_globals, capsys) -
-
-

Test export_config() function directly

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_export_config(reset_globals, capsys):
-    """Test export_config() function directly"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.getLongName.return_value = 'foo'
-        mo.localNode.getURL.return_value = 'bar'
-        mo.getMyNodeInfo().get.return_value = { 'latitudeI': 1100000000, 'longitudeI': 1200000000,
-                                                'altitude': 100, 'batteryLevel': 34, 'latitude': 110.0,
-                                                'longitude': 120.0}
-        mo.localNode.radioConfig.preferences = """phone_timeout_secs: 900
-ls_secs: 300
-position_broadcast_smart: true
-fixed_position: true
-position_flags: 35"""
-        export_config(mo)
-    out, err = capsys.readouterr()
-    assert re.search(r'owner: foo', out, re.MULTILINE)
-    assert re.search(r'channel_url: bar', out, re.MULTILINE)
-    assert re.search(r'location:', out, re.MULTILINE)
-    assert re.search(r'lat: 110.0', out, re.MULTILINE)
-    assert re.search(r'lon: 120.0', out, re.MULTILINE)
-    assert re.search(r'alt: 100', out, re.MULTILINE)
-    assert re.search(r'user_prefs:', out, re.MULTILINE)
-    assert re.search(r'phone_timeout_secs: 900', out, re.MULTILINE)
-    assert re.search(r'ls_secs: 300', out, re.MULTILINE)
-    assert re.search(r"position_broadcast_smart: 'true'", out, re.MULTILINE)
-    assert re.search(r"fixed_position: 'true'", out, re.MULTILINE)
-    assert re.search(r"position_flags: 35", out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_export_config_called_from_main(capsys, reset_globals) -
-
-

Test –export-config

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_export_config_called_from_main(capsys, reset_globals):
-    """Test --export-config"""
-    sys.argv = ['', '--export-config']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'# start of Meshtastic configure yaml', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_getPref_invalid_field(capsys, reset_globals) -
-
-

Test getPref() with an invalid field

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_getPref_invalid_field(capsys, reset_globals):
-    """Test getPref() with an invalid field"""
-
-    class Field:
-        """Simple class for testing."""
-
-        def __init__(self, name):
-            """constructor"""
-            self.name = name
-
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    # Note: This is a subset of the real fields
-    ls_secs_field = Field('ls_secs')
-    is_router = Field('is_router')
-    fixed_position = Field('fixed_position')
-
-    fields = [ ls_secs_field, is_router, fixed_position ]
-    prefs.DESCRIPTOR.fields = fields
-
-    getPref(prefs, 'foo')
-
-    out, err = capsys.readouterr()
-    assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-    # ensure they are sorted
-    assert re.search(r'fixed_position\s+is_router\s+ls_secs', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_getPref_valid_field(capsys, reset_globals) -
-
-

Test getPref() with a valid field

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_getPref_valid_field(capsys, reset_globals):
-    """Test getPref() with a valid field"""
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = 'ls_secs'
-    prefs.wifi_ssid = 'foo'
-    prefs.ls_secs = 300
-    prefs.fixed_position = False
-
-    getPref(prefs, 'ls_secs')
-    out, err = capsys.readouterr()
-    assert re.search(r'ls_secs: 300', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_get_with_invalid(capsys, reset_globals) -
-
-

Test –get with invalid field

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_get_with_invalid(capsys, reset_globals):
-    """Test --get with invalid field"""
-    sys.argv = ['', '--get', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_user_prefs = MagicMock()
-    mocked_user_prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    mocked_node = MagicMock(autospec=Node)
-    mocked_node.radioConfig.preferences = ( mocked_user_prefs )
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-        assert re.search(r'Choices in sorted order are', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_get_with_valid_values(capsys, reset_globals) -
-
-

Test –get with valid values (with string, number, boolean)

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_get_with_valid_values(capsys, reset_globals):
-    """Test --get with valid values (with string, number, boolean)"""
-    sys.argv = ['', '--get', 'ls_secs', '--get', 'wifi_ssid', '--get', 'fixed_position']
-    Globals.getInstance().set_args(sys.argv)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-
-        # kind of cheating here, we are setting up the node
-        mocked_node = MagicMock(autospec=Node)
-        anode = mocked_node()
-        anode.radioConfig.preferences.wifi_ssid = 'foo'
-        anode.radioConfig.preferences.ls_secs = 300
-        anode.radioConfig.preferences.fixed_position = False
-        Globals.getInstance().set_target_node(anode)
-
-        main()
-
-        mo.assert_called()
-
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'ls_secs: 300', out, re.MULTILINE)
-        assert re.search(r'wifi_ssid: foo', out, re.MULTILINE)
-        assert re.search(r'fixed_position: False', out, re.MULTILINE)
-        assert err == ''
-
-
-
-def test_main_gpio_rd(caplog, capsys, reset_globals) -
-
-

Test –gpio_rd with a named gpio channel

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_gpio_rd(caplog, capsys, reset_globals):
-    """Test --gpio_rd with a named gpio channel"""
-    # Note: On the Heltec v2.1, there is a GPIO pin GPIO 13 that does not have a
-    # red arrow (meaning ok to use for our purposes)
-    # See https://resource.heltec.cn/download/WiFi_LoRa_32/WIFI_LoRa_32_V2.pdf
-    # To find out the mask for GPIO 13, let us assign n as 13.
-    # 1. Subtract 1 from n (n is now 12)
-    # 2. Find the 2^n or 2^12 (4096)
-    # 3. Convert 4096 decimal to hex (0x1000)
-    # You can use python:
-    # >>> print(hex(2**12))
-    # 0x1000
-    sys.argv = ['', '--gpio-rd', '0x1000', '--dest', '!1234']
-    Globals.getInstance().set_args(sys.argv)
-
-    channel = Channel(index=1, role=1)
-    channel.settings.modem_config = 3
-    channel.settings.psk = b'\x01'
-
-    packet = {
-
-            'from': 682968668,
-            'to': 682968612,
-            'channel': 1,
-            'decoded': {
-                'portnum': 'REMOTE_HARDWARE_APP',
-                'payload': b'\x08\x05\x18\x80 ',
-                'requestId': 1629980484,
-                'remotehw': {
-                    'typ': 'READ_GPIOS_REPLY',
-                    'gpioValue': '4096',
-                    'raw': 'faked',
-                    'id': 1693085229,
-                    'rxTime': 1640294262,
-                    'rxSnr': 4.75,
-                    'hopLimit': 3,
-                    'wantAck': True,
-                    }
-                }
-            }
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.getChannelByName.return_value = channel
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with caplog.at_level(logging.DEBUG):
-            main()
-            onGPIOreceive(packet, mo)
-    assert re.search(r'readGPIOs nodeid:!1234 mask:4096', caplog.text, re.MULTILINE)
-    out, err = capsys.readouterr()
-    assert re.search(r'Connected to radio', out, re.MULTILINE)
-    assert re.search(r'Reading GPIO mask 0x1000 ', out, re.MULTILINE)
-    assert re.search(r'Received RemoteHardware typ=READ_GPIOS_REPLY, gpio_value=4096', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_gpio_rd_no_dest(capsys, reset_globals) -
-
-

Test –gpio_rd with a named gpio channel but no dest was specified

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_gpio_rd_no_dest(capsys, reset_globals):
-    """Test --gpio_rd with a named gpio channel but no dest was specified"""
-    sys.argv = ['', '--gpio-rd', '0x2000']
-    Globals.getInstance().set_args(sys.argv)
-
-    channel = Channel(index=1, role=1)
-    channel.settings.modem_config = 3
-    channel.settings.psk = b'\x01'
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.getChannelByName.return_value = channel
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Warning: Must use a destination node ID', out)
-        assert err == ''
-
-
-
-def test_main_gpio_rd_no_gpio_channel(capsys, reset_globals) -
-
-

Test –gpio_rd with no named gpio channel

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_gpio_rd_no_gpio_channel(capsys, reset_globals):
-    """Test --gpio_rd with no named gpio channel"""
-    sys.argv = ['', '--gpio-rd', '0x10']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.getChannelByName.return_value = None
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Warning: No channel named', out)
-        assert err == ''
-
-
-
-def test_main_info(capsys, reset_globals) -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_info(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_info_with_ble_interface(capsys, reset_globals) -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_info_with_ble_interface(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--ble', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=BLEInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.ble_interface.BLEInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_info_with_seriallog_output_txt(capsys, reset_globals) -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_info_with_seriallog_output_txt(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--seriallog', 'output.txt']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-    # do some cleanup
-    os.remove('output.txt')
-
-
-
-def test_main_info_with_seriallog_stdout(capsys, reset_globals) -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_info_with_seriallog_stdout(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--seriallog', 'stdout']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_info_with_tcp_interface(capsys, reset_globals) -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_info_with_tcp_interface(capsys, reset_globals):
-    """Test --info"""
-    sys.argv = ['', '--info', '--host', 'meshtastic.local']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=TCPInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-    with patch('meshtastic.tcp_interface.TCPInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_init_parser_no_args(capsys, reset_globals) -
-
-

Test no arguments

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_init_parser_no_args(capsys, reset_globals):
-    """Test no arguments"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-    initParser()
-    out, err = capsys.readouterr()
-    assert out == ''
-    assert err == ''
-
-
-
-def test_main_init_parser_version(capsys, reset_globals) -
-
-

Test –version

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_init_parser_version(capsys, reset_globals):
-    """Test --version"""
-    sys.argv = ['', '--version']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        initParser()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    out, err = capsys.readouterr()
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert err == ''
-
-
-
-def test_main_main_no_args(reset_globals) -
-
-

Test with no args

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_main_no_args(reset_globals):
-    """Test with no args"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_main_main_version(capsys, reset_globals) -
-
-

Test –version

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_main_version(capsys, reset_globals):
-    """Test --version"""
-    sys.argv = ['', '--version']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    out, err = capsys.readouterr()
-    assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
-    assert err == ''
-
-
-
-def test_main_no_proto(capsys, reset_globals) -
-
-

Test –noproto (using –info for output)

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_no_proto(capsys, reset_globals):
-    """Test --noproto (using --info for output)"""
-    sys.argv = ['', '--info', '--noproto']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showInfo():
-        print('inside mocked showInfo')
-    iface.showInfo.side_effect = mock_showInfo
-
-    # Override the time.sleep so there is no loop
-    def my_sleep(amount):
-        sys.exit(0)
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with patch('time.sleep', side_effect=my_sleep):
-            with pytest.raises(SystemExit) as pytest_wrapped_e:
-                main()
-            assert pytest_wrapped_e.type == SystemExit
-            assert pytest_wrapped_e.value.code == 0
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
-            assert err == ''
-
-
-
-def test_main_nodes(capsys, reset_globals) -
-
-

Test –nodes

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_nodes(capsys, reset_globals):
-    """Test --nodes"""
-    sys.argv = ['', '--nodes']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_showNodes():
-        print('inside mocked showNodes')
-    iface.showNodes.side_effect = mock_showNodes
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked showNodes', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_onConnection(reset_globals, capsys) -
-
-

Test onConnection

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_onConnection(reset_globals, capsys):
-    """Test onConnection"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-    iface = MagicMock(autospec=SerialInterface)
-    class TempTopic:
-        """ temp class for topic """
-        def getName(self):
-            """ return the fake name of a topic"""
-            return 'foo'
-    mytopic = TempTopic()
-    onConnection(iface, mytopic)
-    out, err = capsys.readouterr()
-    assert re.search(r'Connection changed: foo', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_onReceive_empty(caplog, reset_globals) -
-
-

Test onReceive

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_onReceive_empty(caplog, reset_globals):
-    """Test onReceive"""
-    sys.argv = ['']
-    Globals.getInstance().set_args(sys.argv)
-    iface = MagicMock(autospec=SerialInterface)
-    packet = {'decoded': 'foo'}
-    with caplog.at_level(logging.DEBUG):
-        onReceive(packet, iface)
-    assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
-
-
-
-def test_main_onReceive_with_reply(caplog, capsys, reset_globals) -
-
-

Test onReceive with a reply -To capture: on one device run '–sendtext aaa –reply' and on another -device run '–sendtext bbb –reply', then back to the first device and -run '–sendtext aaa2 –reply'. You should now see a "Sending reply" message.

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_onReceive_with_reply(caplog, capsys, reset_globals):
-    """Test onReceive with a reply
-       To capture: on one device run '--sendtext aaa --reply' and on another
-       device run '--sendtext bbb --reply', then back to the first device and
-       run '--sendtext aaa2 --reply'. You should now see a "Sending reply" message.
-    """
-    sys.argv = ['', '--sendtext', 'hello', '--reply']
-    Globals.getInstance().set_args(sys.argv)
-
-    # Note: 'TEXT_MESSAGE_APP' value is 1
-
-    send_packet = {
-            'to': 4294967295,
-            'decoded': {
-                'portnum': 1,
-                'payload': "hello"
-                },
-            'id': 334776977,
-            'hop_limit': 3,
-            'want_ack': True
-            }
-
-    reply_packet = {
-            'from': 682968668,
-            'to': 4294967295,
-            'decoded': {
-                'portnum': 'TEXT_MESSAGE_APP',
-                'payload': b'bbb',
-                'text': 'bbb'
-                },
-            'id': 1709936182,
-            'rxTime': 1640381999,
-            'rxSnr': 6.0,
-            'hopLimit': 3,
-            'raw': 'faked',
-            'fromId': '!28b5465c',
-            'toId': '^all'
-            }
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.myInfo.my_node_num = 4294967295
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with caplog.at_level(logging.DEBUG):
-            main()
-            onReceive(send_packet, iface)
-            onReceive(reply_packet, iface)
-        assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
-        out, err = capsys.readouterr()
-        assert re.search(r'got msg ', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_onReceive_with_sendtext(caplog, reset_globals) -
-
-

Test onReceive with sendtext -The entire point of this test is to make sure the interface.close() call -is made in onReceive().

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_onReceive_with_sendtext(caplog, reset_globals):
-    """Test onReceive with sendtext
-       The entire point of this test is to make sure the interface.close() call
-       is made in onReceive().
-    """
-    sys.argv = ['', '--sendtext', 'hello']
-    Globals.getInstance().set_args(sys.argv)
-
-    # Note: 'TEXT_MESSAGE_APP' value is 1
-    packet = {
-            'to': 4294967295,
-            'decoded': {
-                'portnum': 1,
-                'payload': "hello"
-                },
-            'id': 334776977,
-            'hop_limit': 3,
-            'want_ack': True
-            }
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.myInfo.my_node_num = 4294967295
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with caplog.at_level(logging.DEBUG):
-            main()
-            onReceive(packet, iface)
-        assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
-        mo.assert_called()
-
-
-
-def test_main_pos_fields_arg_of_zero(capsys, reset_globals) -
-
-

Test –pos-fields an arg of 0 (which shows list)

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_pos_fields_arg_of_zero(capsys, reset_globals):
-    """Test --pos-fields an arg of 0 (which shows list)"""
-    sys.argv = ['', '--pos-fields', '0']
-    Globals.getInstance().set_args(sys.argv)
-
-    pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-        with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc:
-
-            def throw_value_error_exception(exc):
-                raise ValueError()
-            mrc.Value.side_effect = throw_value_error_exception
-            mrc.keys.return_value = [ 'POS_UNDEFINED', 'POS_ALTITUDE', 'POS_ALT_MSL',
-                                      'POS_GEO_SEP', 'POS_DOP', 'POS_HVDOP', 'POS_BATTERY',
-                                      'POS_SATINVIEW', 'POS_SEQ_NOS', 'POS_TIMESTAMP']
-
-            main()
-
-            mrc.Value.assert_called()
-            mrc.keys.assert_called()
-            mo.assert_called()
-
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'ERROR: supported position fields are:', out, re.MULTILINE)
-            assert re.search(r"['POS_UNDEFINED', 'POS_ALTITUDE', 'POS_ALT_MSL', 'POS_GEO_SEP',"\
-                              "'POS_DOP', 'POS_HVDOP', 'POS_BATTERY', 'POS_SATINVIEW', 'POS_SEQ_NOS',"\
-                              "'POS_TIMESTAMP']", out, re.MULTILINE)
-            assert err == ''
-
-
-
-def test_main_pos_fields_no_args(capsys, reset_globals) -
-
-

Test –pos-fields no args (which shows settings)

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_pos_fields_no_args(capsys, reset_globals):
-    """Test --pos-fields no args (which shows settings)"""
-    sys.argv = ['', '--pos-fields']
-    Globals.getInstance().set_args(sys.argv)
-
-    pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-        with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc:
-            # kind of cheating here, we are setting up the node
-            mocked_node = MagicMock(autospec=Node)
-            anode = mocked_node()
-            anode.radioConfig.preferences.position_flags = 35
-            Globals.getInstance().set_target_node(anode)
-
-            mrc.values.return_value = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256]
-            # Note: When you use side_effect and a list, each call will use a value from the front of the list then
-            # remove that value from the list. If there are three values in the list, we expect it to be called
-            # three times.
-            mrc.Name.side_effect = [ 'POS_ALTITUDE', 'POS_ALT_MSL', 'POS_BATTERY' ]
-
-            main()
-
-            mrc.Name.assert_called()
-            mrc.values.assert_called()
-            mo.assert_called()
-
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'POS_ALTITUDE POS_ALT_MSL POS_BATTERY', out, re.MULTILINE)
-            assert err == ''
-
-
-
-def test_main_pos_fields_valid_values(capsys, reset_globals) -
-
-

Test –pos-fields with valid values

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_pos_fields_valid_values(capsys, reset_globals):
-    """Test --pos-fields with valid values"""
-    sys.argv = ['', '--pos-fields', 'POS_GEO_SEP', 'POS_ALT_MSL']
-    Globals.getInstance().set_args(sys.argv)
-
-    pos_flags = MagicMock(autospec=meshtastic.radioconfig_pb2.PositionFlags)
-
-    with patch('meshtastic.serial_interface.SerialInterface') as mo:
-        with patch('meshtastic.radioconfig_pb2.PositionFlags', return_value=pos_flags) as mrc:
-
-            mrc.Value.side_effect = [ 4, 2 ]
-
-            main()
-
-            mrc.Value.assert_called()
-            mo.assert_called()
-
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'Setting position fields to 6', out, re.MULTILINE)
-            assert re.search(r'Set position_flags to 6', out, re.MULTILINE)
-            assert re.search(r'Writing modified preferences to device', out, re.MULTILINE)
-            assert err == ''
-
-
-
-def test_main_qr(capsys, reset_globals) -
-
-

Test –qr

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_qr(capsys, reset_globals):
-    """Test --qr"""
-    sys.argv = ['', '--qr']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    # TODO: could mock/check url
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Primary channel URL', out, re.MULTILINE)
-        # if a qr code is generated it will have lots of these
-        assert re.search(r'\[7m', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_reboot(capsys, reset_globals) -
-
-

Test –reboot

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_reboot(capsys, reset_globals):
-    """Test --reboot"""
-    sys.argv = ['', '--reboot']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_reboot():
-        print('inside mocked reboot')
-    mocked_node.reboot.side_effect = mock_reboot
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'inside mocked reboot', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_sendping(capsys, reset_globals) -
-
-

Test –sendping

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_sendping(capsys, reset_globals):
-    """Test --sendping"""
-    sys.argv = ['', '--sendping']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendData(payload, dest, portNum, wantAck, wantResponse):
-        print('inside mocked sendData')
-    iface.sendData.side_effect = mock_sendData
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending ping message', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendData', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_sendtext(capsys, reset_globals) -
-
-

Test –sendtext

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_sendtext(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendText(text, dest, wantAck, channelIndex):
-        print('inside mocked sendText')
-    iface.sendText.side_effect = mock_sendText
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending text message', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendText', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_sendtext_with_channel(capsys, reset_globals) -
-
-

Test –sendtext

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_sendtext_with_channel(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello', '--ch-index', '1']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendText(text, dest, wantAck, channelIndex):
-        print('inside mocked sendText')
-    iface.sendText.side_effect = mock_sendText
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending text message', out, re.MULTILINE)
-        assert re.search(r'on channelIndex:1', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendText', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_sendtext_with_dest(capsys, reset_globals) -
-
-

Test –sendtext with –dest

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_sendtext_with_dest(capsys, reset_globals):
-    """Test --sendtext with --dest"""
-    sys.argv = ['', '--sendtext', 'hello', '--dest', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendText(text, dest, wantAck, channelIndex):
-        print('inside mocked sendText')
-    iface.sendText.side_effect = mock_sendText
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Sending text message', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendText', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_sendtext_with_invalid_channel(capsys, reset_globals) -
-
-

Test –sendtext

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_sendtext_with_invalid_channel(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello', '--ch-index', '-1']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getChannelByChannelIndex.return_value = None
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        iface.getNode.return_value.getChannelByChannelIndex.return_value = None
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'is not a valid channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_sendtext_with_invalid_channel_nine(capsys, reset_globals) -
-
-

Test –sendtext

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_sendtext_with_invalid_channel_nine(capsys, reset_globals):
-    """Test --sendtext"""
-    sys.argv = ['', '--sendtext', 'hello', '--ch-index', '9']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        iface.getNode.return_value.getChannelByChannelIndex.return_value = None
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'is not a valid channel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_setPref_invalid_field(capsys, reset_globals) -
-
-

Test setPref() with a invalid field

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_setPref_invalid_field(capsys, reset_globals):
-    """Test setPref() with a invalid field"""
-
-
-    class Field:
-        """Simple class for testing."""
-
-        def __init__(self, name):
-            """constructor"""
-            self.name = name
-
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    # Note: This is a subset of the real fields
-    ls_secs_field = Field('ls_secs')
-    is_router = Field('is_router')
-    fixed_position = Field('fixed_position')
-
-    fields = [ ls_secs_field, is_router, fixed_position ]
-    prefs.DESCRIPTOR.fields = fields
-
-    setPref(prefs, 'foo', '300')
-    out, err = capsys.readouterr()
-    assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-    # ensure they are sorted
-    assert re.search(r'fixed_position\s+is_router\s+ls_secs', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_setPref_valid_field(capsys, reset_globals) -
-
-

Test setPref() with a valid field

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_setPref_valid_field(capsys, reset_globals):
-    """Test setPref() with a valid field"""
-
-    class Field:
-        """Simple class for testing."""
-
-        def __init__(self, name, enum_type):
-            """constructor"""
-            self.name = name
-            self.enum_type = enum_type
-
-    ls_secs_field = Field('ls_secs', 'int')
-    prefs = MagicMock()
-    prefs.DESCRIPTOR.fields_by_name.get.return_value = ls_secs_field
-
-    setPref(prefs, 'ls_secs', '300')
-    out, err = capsys.readouterr()
-    assert re.search(r'Set ls_secs to 300', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_set_ham_to_KI123(capsys, reset_globals) -
-
-

Test –set-ham KI123

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_set_ham_to_KI123(capsys, reset_globals):
-    """Test --set-ham KI123"""
-    sys.argv = ['', '--set-ham', 'KI123']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_turnOffEncryptionOnPrimaryChannel():
-        print('inside mocked turnOffEncryptionOnPrimaryChannel')
-    def mock_setOwner(name, is_licensed):
-        print('inside mocked setOwner')
-    mocked_node.turnOffEncryptionOnPrimaryChannel.side_effect = mock_turnOffEncryptionOnPrimaryChannel
-    mocked_node.setOwner.side_effect = mock_setOwner
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Setting Ham ID to KI123', out, re.MULTILINE)
-        assert re.search(r'inside mocked setOwner', out, re.MULTILINE)
-        assert re.search(r'inside mocked turnOffEncryptionOnPrimaryChannel', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_set_owner_to_bob(capsys, reset_globals) -
-
-

Test –set-owner bob

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_set_owner_to_bob(capsys, reset_globals):
-    """Test --set-owner bob"""
-    sys.argv = ['', '--set-owner', 'bob']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Setting device owner to bob', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_set_team_invalid(capsys, reset_globals) -
-
-

Test –set-team using an invalid team name

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_set_team_invalid(capsys, reset_globals):
-    """Test --set-team using an invalid team name"""
-    sys.argv = ['', '--set-team', 'NOTCYAN']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-
-    def throw_an_exception(exc):
-        raise ValueError("Fake exception.")
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with patch('meshtastic.mesh_pb2.Team') as mm:
-            mm.Value.side_effect = throw_an_exception
-            main()
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'ERROR: Team', out, re.MULTILINE)
-            assert err == ''
-            mo.assert_called()
-            mm.Value.assert_called()
-
-
-
-def test_main_set_team_valid(capsys, reset_globals) -
-
-

Test –set-team

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_set_team_valid(capsys, reset_globals):
-    """Test --set-team"""
-    sys.argv = ['', '--set-team', 'CYAN']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_setOwner(team):
-        print('inside mocked setOwner')
-    mocked_node.setOwner.side_effect = mock_setOwner
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with patch('meshtastic.mesh_pb2.Team') as mm:
-            mm.Name.return_value = 'FAKENAME'
-            mm.Value.return_value = 'FAKEVAL'
-            main()
-            out, err = capsys.readouterr()
-            assert re.search(r'Connected to radio', out, re.MULTILINE)
-            assert re.search(r'Setting team to', out, re.MULTILINE)
-            assert err == ''
-            mo.assert_called()
-            mm.Name.assert_called()
-            mm.Value.assert_called()
-
-
-
-def test_main_set_valid(capsys, reset_globals) -
-
-

Test –set with valid field

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_set_valid(capsys, reset_globals):
-    """Test --set with valid field"""
-    sys.argv = ['', '--set', 'wifi_ssid', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Set wifi_ssid to foo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_set_with_invalid(capsys, reset_globals) -
-
-

Test –set with invalid field

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_set_with_invalid(capsys, reset_globals):
-    """Test --set with invalid field"""
-    sys.argv = ['', '--set', 'foo', 'foo']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_user_prefs = MagicMock()
-    mocked_user_prefs.DESCRIPTOR.fields_by_name.get.return_value = None
-
-    mocked_node = MagicMock(autospec=Node)
-    mocked_node.radioConfig.preferences = ( mocked_user_prefs )
-
-    iface = MagicMock(autospec=SerialInterface)
-    iface.getNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'does not have an attribute called foo', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_setalt(capsys, reset_globals) -
-
-

Test –setalt

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_setalt(capsys, reset_globals):
-    """Test --setalt"""
-    sys.argv = ['', '--setalt', '51']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_writeConfig():
-        print('inside mocked writeConfig')
-    mocked_node.writeConfig.side_effect = mock_writeConfig
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendPosition(lat, lon, alt):
-        print('inside mocked sendPosition')
-    iface.sendPosition.side_effect = mock_sendPosition
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Fixing altitude', out, re.MULTILINE)
-        assert re.search(r'Setting device position', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendPosition', out, re.MULTILINE)
-        # TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_setchan(capsys, reset_globals) -
-
-

Test –setchan (deprecated)

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_setchan(capsys, reset_globals):
-    """Test --setchan (deprecated)"""
-    sys.argv = ['', '--setchan', 'a', 'b']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            main()
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_main_setlat(capsys, reset_globals) -
-
-

Test –sendlat

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_setlat(capsys, reset_globals):
-    """Test --sendlat"""
-    sys.argv = ['', '--setlat', '37.5']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_writeConfig():
-        print('inside mocked writeConfig')
-    mocked_node.writeConfig.side_effect = mock_writeConfig
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendPosition(lat, lon, alt):
-        print('inside mocked sendPosition')
-    iface.sendPosition.side_effect = mock_sendPosition
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Fixing latitude', out, re.MULTILINE)
-        assert re.search(r'Setting device position', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendPosition', out, re.MULTILINE)
-        # TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_setlon(capsys, reset_globals) -
-
-

Test –setlon

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_setlon(capsys, reset_globals):
-    """Test --setlon"""
-    sys.argv = ['', '--setlon', '-122.1']
-    Globals.getInstance().set_args(sys.argv)
-
-    mocked_node = MagicMock(autospec=Node)
-    def mock_writeConfig():
-        print('inside mocked writeConfig')
-    mocked_node.writeConfig.side_effect = mock_writeConfig
-
-    iface = MagicMock(autospec=SerialInterface)
-    def mock_sendPosition(lat, lon, alt):
-        print('inside mocked sendPosition')
-    iface.sendPosition.side_effect = mock_sendPosition
-    iface.localNode.return_value = mocked_node
-
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert re.search(r'Fixing longitude', out, re.MULTILINE)
-        assert re.search(r'Setting device position', out, re.MULTILINE)
-        assert re.search(r'inside mocked sendPosition', out, re.MULTILINE)
-        # TODO: Why does this not work? assert re.search(r'inside mocked writeConfig', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_seturl(capsys, reset_globals) -
-
-

Test –seturl (url used below is what is generated after a factory_reset)

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_seturl(capsys, reset_globals):
-    """Test --seturl (url used below is what is generated after a factory_reset)"""
-    sys.argv = ['', '--seturl', 'https://www.meshtastic.org/d/#CgUYAyIBAQ']
-    Globals.getInstance().set_args(sys.argv)
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        main()
-        out, err = capsys.readouterr()
-        assert re.search(r'Connected to radio', out, re.MULTILINE)
-        assert err == ''
-        mo.assert_called()
-
-
-
-def test_main_support(capsys, reset_globals) -
-
-

Test –support

-
- -Expand source code - -
@pytest.mark.unit
-def test_main_support(capsys, reset_globals):
-    """Test --support"""
-    sys.argv = ['', '--support']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    out, err = capsys.readouterr()
-    assert re.search(r'System', out, re.MULTILINE)
-    assert re.search(r'Platform', out, re.MULTILINE)
-    assert re.search(r'Machine', out, re.MULTILINE)
-    assert re.search(r'Executable', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_main_test_no_ports(patched_find_ports, reset_globals) -
-
-

Test –test with no hardware

-
- -Expand source code - -
@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=[])
-def test_main_test_no_ports(patched_find_ports, reset_globals):
-    """Test --test with no hardware"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    assert Globals.getInstance().get_target_node() is None
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    patched_find_ports.assert_called()
-
-
-
-def test_main_test_one_port(patched_find_ports, reset_globals) -
-
-

Test –test with one fake port

-
- -Expand source code - -
@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1'])
-def test_main_test_one_port(patched_find_ports, reset_globals):
-    """Test --test with one fake port"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    assert Globals.getInstance().get_target_node() is None
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    patched_find_ports.assert_called()
-
-
-
-def test_main_test_two_ports_fails(patched_find_ports, patched_test_all, reset_globals) -
-
-

Test –test two fake ports and testAll() is a simulated failure

-
- -Expand source code - -
@pytest.mark.unit
-@patch('meshtastic.test.testAll', return_value=False)
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1', '/dev/ttyFake2'])
-def test_main_test_two_ports_fails(patched_find_ports, patched_test_all, reset_globals):
-    """Test --test two fake ports and testAll() is a simulated failure"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    # TODO: why does this fail? patched_find_ports.assert_called()
-    patched_test_all.assert_called()
-
-
-
-def test_main_test_two_ports_success(patched_find_ports, patched_test_all, reset_globals) -
-
-

Test –test two fake ports and testAll() is a simulated success

-
- -Expand source code - -
@pytest.mark.unit
-@patch('meshtastic.test.testAll', return_value=True)
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1', '/dev/ttyFake2'])
-def test_main_test_two_ports_success(patched_find_ports, patched_test_all, reset_globals):
-    """Test --test two fake ports and testAll() is a simulated success"""
-    sys.argv = ['', '--test']
-    Globals.getInstance().set_args(sys.argv)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        main()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-    # TODO: why does this fail? patched_find_ports.assert_called()
-    patched_test_all.assert_called()
-
-
-
-
-
-

Classes

-
-
-class Channel -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var DISABLED
-
-
-
-
var PRIMARY
-
-
-
-
var Role
-
-
-
-
var SECONDARY
-
-
-
-
-

Instance variables

-
-
var index
-
-

Field Channel.index

-
-
var role
-
-

Field Channel.role

-
-
var settings
-
-

Field Channel.settings

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_mesh_interface.html b/docs/meshtastic/tests/test_mesh_interface.html deleted file mode 100644 index 8510038..0000000 --- a/docs/meshtastic/tests/test_mesh_interface.html +++ /dev/null @@ -1,1290 +0,0 @@ - - - - - - -meshtastic.tests.test_mesh_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_mesh_interface

-
-
-

Meshtastic unit tests for mesh_interface.py

-
- -Expand source code - -
"""Meshtastic unit tests for mesh_interface.py"""
-
-import re
-import logging
-
-from unittest.mock import patch, MagicMock
-import pytest
-
-from ..mesh_interface import MeshInterface
-from ..node import Node
-from .. import mesh_pb2
-from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
-
-
-@pytest.mark.unit
-def test_MeshInterface(capsys, reset_globals):
-    """Test that we can instantiate a MeshInterface"""
-    iface = MeshInterface(noProto=True)
-    anode = Node('foo', 'bar')
-
-    nodes = {
-        '!9388f81c': {
-            'num': 2475227164,
-            'user': {
-                'id': '!9388f81c',
-                'longName': 'Unknown f81c',
-                'shortName': '?1C',
-                'macaddr': 'RBeTiPgc',
-                'hwModel': 'TBEAM'
-            },
-            'position': {},
-            'lastHeard': 1640204888
-        }
-    }
-
-    iface.nodesByNum = {1: anode }
-    iface.nodes = nodes
-
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-
-    iface.showInfo()
-    iface.localNode.showInfo()
-    iface.showNodes()
-    iface.sendText('hello')
-    iface.close()
-    out, err = capsys.readouterr()
-    assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
-    assert re.search(r'Nodes', out, re.MULTILINE)
-    assert re.search(r'Preferences', out, re.MULTILINE)
-    assert re.search(r'Channels', out, re.MULTILINE)
-    assert re.search(r'Primary channel URL', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_getMyUser(reset_globals, iface_with_nodes):
-    """Test getMyUser()"""
-    iface = iface_with_nodes
-
-    iface.myInfo.my_node_num = 2475227164
-    myuser = iface.getMyUser()
-    print(f'myuser:{myuser}')
-    assert myuser is not None
-    assert myuser["id"] == '!9388f81c'
-
-
-@pytest.mark.unit
-def test_getLongName(reset_globals, iface_with_nodes):
-    """Test getLongName()"""
-    iface = iface_with_nodes
-    iface.myInfo.my_node_num = 2475227164
-    mylongname = iface.getLongName()
-    assert mylongname == 'Unknown f81c'
-
-
-@pytest.mark.unit
-def test_getShortName(reset_globals, iface_with_nodes):
-    """Test getShortName()."""
-    iface = iface_with_nodes
-    iface.myInfo.my_node_num = 2475227164
-    myshortname = iface.getShortName()
-    assert myshortname == '?1C'
-
-
-@pytest.mark.unit
-def test_handlePacketFromRadio_no_from(capsys, reset_globals):
-    """Test _handlePacketFromRadio with no 'from' in the mesh packet."""
-    iface = MeshInterface(noProto=True)
-    meshPacket = mesh_pb2.MeshPacket()
-    iface._handlePacketFromRadio(meshPacket)
-    out, err = capsys.readouterr()
-    assert re.search(r'Device returned a packet we sent, ignoring', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_handlePacketFromRadio_with_a_portnum(caplog, reset_globals):
-    """Test _handlePacketFromRadio with a portnum
-       Since we have an attribute called 'from', we cannot simply 'set' it.
-       Had to implement a hack just to be able to test some code.
-    """
-    iface = MeshInterface(noProto=True)
-    meshPacket = mesh_pb2.MeshPacket()
-    meshPacket.decoded.payload = b''
-    meshPacket.decoded.portnum = 1
-    with caplog.at_level(logging.WARNING):
-        iface._handlePacketFromRadio(meshPacket, hack=True)
-    assert re.search(r'Not populating fromId', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_handlePacketFromRadio_no_portnum(caplog, reset_globals):
-    """Test _handlePacketFromRadio without a portnum"""
-    iface = MeshInterface(noProto=True)
-    meshPacket = mesh_pb2.MeshPacket()
-    meshPacket.decoded.payload = b''
-    with caplog.at_level(logging.WARNING):
-        iface._handlePacketFromRadio(meshPacket, hack=True)
-    assert re.search(r'Not populating fromId', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_getNode_with_local(reset_globals):
-    """Test getNode"""
-    iface = MeshInterface(noProto=True)
-    anode = iface.getNode(LOCAL_ADDR)
-    assert anode == iface.localNode
-
-
-@pytest.mark.unit
-def test_getNode_not_local(reset_globals, caplog):
-    """Test getNode not local"""
-    iface = MeshInterface(noProto=True)
-    anode = MagicMock(autospec=Node)
-    with caplog.at_level(logging.DEBUG):
-        with patch('meshtastic.node.Node', return_value=anode):
-            another_node = iface.getNode('bar2')
-            assert another_node != iface.localNode
-    assert re.search(r'About to requestConfig', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_getNode_not_local_timeout(reset_globals, capsys):
-    """Test getNode not local, simulate timeout"""
-    iface = MeshInterface(noProto=True)
-    anode = MagicMock(autospec=Node)
-    anode.waitForConfig.return_value = False
-    with patch('meshtastic.node.Node', return_value=anode):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            iface.getNode('bar2')
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.match(r'Error: Timed out waiting for node config', out)
-        assert err == ''
-
-
-@pytest.mark.unit
-def test_sendPosition(reset_globals, caplog):
-    """Test sendPosition"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface.sendPosition()
-    iface.close()
-    assert re.search(r'p.time:', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_handleFromRadio_empty_payload(reset_globals, caplog):
-    """Test _handleFromRadio"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._handleFromRadio(b'')
-    iface.close()
-    assert re.search(r'Unexpected FromRadio payload', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_handleFromRadio_with_my_info(reset_globals, caplog):
-    """Test _handleFromRadio with my_info"""
-    # Note: I captured the '--debug --info' for the bytes below.
-    # It "translates" to this:
-    # my_info {
-    #  my_node_num: 682584012
-    #  num_bands: 13
-    #  firmware_version: "1.2.49.5354c49"
-    #  reboot_count: 13
-    #  bitrate: 17.088470458984375
-    #  message_timeout_msec: 300000
-    #  min_app_version: 20200
-    #  max_channels: 8
-    # }
-    from_radio_bytes = b'\x1a,\x08\xcc\xcf\xbd\xc5\x02\x18\r2\x0e1.2.49.5354c49P\r]0\xb5\x88Ah\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._handleFromRadio(from_radio_bytes)
-    iface.close()
-    assert re.search(r'Received myinfo', caplog.text, re.MULTILINE)
-    assert re.search(r'num_bands: 13', caplog.text, re.MULTILINE)
-    assert re.search(r'max_channels: 8', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_handleFromRadio_with_node_info(reset_globals, caplog, capsys):
-    """Test _handleFromRadio with node_info"""
-    # Note: I captured the '--debug --info' for the bytes below.
-    # It "translates" to this:
-    # node_info {
-    #  num: 682584012
-    #  user {
-    #    id: "!28af67cc"
-    #    long_name: "Unknown 67cc"
-    #    short_name: "?CC"
-    #    macaddr: "$o(\257g\314"
-    #    hw_model: HELTEC_V2_1
-    #  }
-    #  position {
-    #    }
-    #  }
-
-    from_radio_bytes = b'"2\x08\xcc\xcf\xbd\xc5\x02\x12(\n\t!28af67cc\x12\x0cUnknown 67cc\x1a\x03?CC"\x06$o(\xafg\xcc0\n\x1a\x00'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._startConfig()
-        iface._handleFromRadio(from_radio_bytes)
-        assert re.search(r'Received nodeinfo', caplog.text, re.MULTILINE)
-        assert re.search(r'682584012', caplog.text, re.MULTILINE)
-        assert re.search(r'HELTEC_V2_1', caplog.text, re.MULTILINE)
-        # validate some of showNodes() output
-        iface.showNodes()
-        out, err = capsys.readouterr()
-        assert re.search(r' 1 ', out, re.MULTILINE)
-        assert re.search(r'│ Unknown 67cc │ ', out, re.MULTILINE)
-        assert re.search(r'│ !28af67cc │ N/A   │ N/A         │ N/A', out, re.MULTILINE)
-        assert err == ''
-        iface.close()
-
-
-@pytest.mark.unit
-def test_handleFromRadio_with_node_info_tbeam1(reset_globals, caplog, capsys):
-    """Test _handleFromRadio with node_info"""
-    # Note: Captured the '--debug --info' for the bytes below.
-    # pylint: disable=C0301
-    from_radio_bytes = b'"=\x08\x80\xf8\xc8\xf6\x07\x12"\n\t!7ed23c00\x12\x07TBeam 1\x1a\x02T1"\x06\x94\xb9~\xd2<\x000\x04\x1a\x07 ]MN\x01\xbea%\xad\x01\xbea=\x00\x00,A'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._startConfig()
-        iface._handleFromRadio(from_radio_bytes)
-        assert re.search(r'Received nodeinfo', caplog.text, re.MULTILINE)
-        assert re.search(r'TBeam 1', caplog.text, re.MULTILINE)
-        assert re.search(r'2127707136', caplog.text, re.MULTILINE)
-        # validate some of showNodes() output
-        iface.showNodes()
-        out, err = capsys.readouterr()
-        assert re.search(r' 1 ', out, re.MULTILINE)
-        assert re.search(r'│ TBeam 1 │ ', out, re.MULTILINE)
-        assert re.search(r'│ !7ed23c00 │', out, re.MULTILINE)
-        assert err == ''
-        iface.close()
-
-
-@pytest.mark.unit
-def test_handleFromRadio_with_node_info_tbeam_with_bad_data(reset_globals, caplog, capsys):
-    """Test _handleFromRadio with node_info with some bad data (issue#172) - ensure we do not throw exception"""
-    # Note: Captured the '--debug --info' for the bytes below.
-    from_radio_bytes = b'"\x17\x08\xdc\x8a\x8a\xae\x02\x12\x08"\x06\x00\x00\x00\x00\x00\x00\x1a\x00=\x00\x00\xb8@'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._startConfig()
-        iface._handleFromRadio(from_radio_bytes)
-
-
-@pytest.mark.unit
-def test_MeshInterface_sendToRadioImpl(caplog, reset_globals):
-    """Test _sendToRadioImp()"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._sendToRadioImpl('foo')
-    assert re.search(r'Subclass must provide toradio', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-@pytest.mark.unit
-def test_MeshInterface_sendToRadio_no_proto(caplog, reset_globals):
-    """Test sendToRadio()"""
-    iface = MeshInterface()
-    with caplog.at_level(logging.DEBUG):
-        iface._sendToRadioImpl('foo')
-    assert re.search(r'Subclass must provide toradio', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-@pytest.mark.unit
-def test_sendData_too_long(caplog, reset_globals):
-    """Test when data payload is too big"""
-    iface = MeshInterface(noProto=True)
-    some_large_text = b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    with caplog.at_level(logging.DEBUG):
-        with pytest.raises(Exception) as pytest_wrapped_e:
-            iface.sendData(some_large_text)
-            assert re.search('Data payload too big', caplog.text, re.MULTILINE)
-        assert pytest_wrapped_e.type == Exception
-    iface.close()
-
-
-@pytest.mark.unit
-def test_sendData_unknown_app(capsys, reset_globals):
-    """Test sendData when unknown app"""
-    iface = MeshInterface(noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        iface.sendData(b'hello', portNum=0)
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: A non-zero port number', out, re.MULTILINE)
-    assert err == ''
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_sendPosition_with_a_position(caplog, reset_globals):
-    """Test sendPosition when lat/long/alt"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface.sendPosition(latitude=40.8, longitude=-111.86, altitude=201)
-        assert re.search(r'p.latitude_i:408', caplog.text, re.MULTILINE)
-        assert re.search(r'p.longitude_i:-11186', caplog.text, re.MULTILINE)
-        assert re.search(r'p.altitude:201', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_sendPacket_with_no_destination(capsys, reset_globals):
-    """Test _sendPacket()"""
-    iface = MeshInterface(noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        iface._sendPacket(b'', destinationId=None)
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: destinationId must not be None', out, re.MULTILINE)
-    assert err == ''
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_sendPacket_with_destination_as_int(caplog, reset_globals):
-    """Test _sendPacket() with int as a destination"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=123)
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_sendPacket_with_destination_starting_with_a_bang(caplog, reset_globals):
-    """Test _sendPacket() with int as a destination"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId='!1234')
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog, reset_globals):
-    """Test _sendPacket() with BROADCAST_ADDR as a destination"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=BROADCAST_ADDR)
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys, reset_globals):
-    """Test _sendPacket() with LOCAL_ADDR as a destination with no myInfo"""
-    iface = MeshInterface(noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: No myInfo', out, re.MULTILINE)
-    assert err == ''
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog, reset_globals):
-    """Test _sendPacket() with LOCAL_ADDR as a destination with myInfo"""
-    iface = MeshInterface(noProto=True)
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_sendPacket_with_destination_is_blank_with_nodes(capsys, reset_globals, iface_with_nodes):
-    """Test _sendPacket() with '' as a destination with myInfo"""
-    iface = iface_with_nodes
-    meshPacket = mesh_pb2.MeshPacket()
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        iface._sendPacket(meshPacket, destinationId='')
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.match(r'Warning: NodeId  not found in DB', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_sendPacket_with_destination_is_blank_without_nodes(caplog, reset_globals, iface_with_nodes):
-    """Test _sendPacket() with '' as a destination with myInfo"""
-    iface = iface_with_nodes
-    iface.nodes = None
-    meshPacket = mesh_pb2.MeshPacket()
-    with caplog.at_level(logging.WARNING):
-        iface._sendPacket(meshPacket, destinationId='')
-    assert re.search(r'Warning: There were no self.nodes.', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_getMyNodeInfo(reset_globals):
-    """Test getMyNodeInfo()"""
-    iface = MeshInterface(noProto=True)
-    anode = iface.getNode(LOCAL_ADDR)
-    iface.nodesByNum = {1: anode }
-    assert iface.nodesByNum.get(1) == anode
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-    iface.myInfo.my_node_num = 1
-    myinfo = iface.getMyNodeInfo()
-    assert myinfo == anode
-
-
-@pytest.mark.unit
-def test_generatePacketId(capsys, reset_globals):
-    """Test _generatePacketId() when no currentPacketId (not connected)"""
-    iface = MeshInterface(noProto=True)
-    # not sure when this condition would ever happen... but we can simulate it
-    iface.currentPacketId = None
-    assert iface.currentPacketId is None
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        iface._generatePacketId()
-        out, err = capsys.readouterr()
-        assert re.search(r'Not connected yet, can not generate packet', out, re.MULTILINE)
-        assert err == ''
-    assert pytest_wrapped_e.type == Exception
-
-
-
-
-
-
-
-

Functions

-
-
-def test_MeshInterface(capsys, reset_globals) -
-
-

Test that we can instantiate a MeshInterface

-
- -Expand source code - -
@pytest.mark.unit
-def test_MeshInterface(capsys, reset_globals):
-    """Test that we can instantiate a MeshInterface"""
-    iface = MeshInterface(noProto=True)
-    anode = Node('foo', 'bar')
-
-    nodes = {
-        '!9388f81c': {
-            'num': 2475227164,
-            'user': {
-                'id': '!9388f81c',
-                'longName': 'Unknown f81c',
-                'shortName': '?1C',
-                'macaddr': 'RBeTiPgc',
-                'hwModel': 'TBEAM'
-            },
-            'position': {},
-            'lastHeard': 1640204888
-        }
-    }
-
-    iface.nodesByNum = {1: anode }
-    iface.nodes = nodes
-
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-
-    iface.showInfo()
-    iface.localNode.showInfo()
-    iface.showNodes()
-    iface.sendText('hello')
-    iface.close()
-    out, err = capsys.readouterr()
-    assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
-    assert re.search(r'Nodes', out, re.MULTILINE)
-    assert re.search(r'Preferences', out, re.MULTILINE)
-    assert re.search(r'Channels', out, re.MULTILINE)
-    assert re.search(r'Primary channel URL', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_MeshInterface_sendToRadioImpl(caplog, reset_globals) -
-
-

Test _sendToRadioImp()

-
- -Expand source code - -
@pytest.mark.unit
-def test_MeshInterface_sendToRadioImpl(caplog, reset_globals):
-    """Test _sendToRadioImp()"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._sendToRadioImpl('foo')
-    assert re.search(r'Subclass must provide toradio', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-
-def test_MeshInterface_sendToRadio_no_proto(caplog, reset_globals) -
-
-

Test sendToRadio()

-
- -Expand source code - -
@pytest.mark.unit
-def test_MeshInterface_sendToRadio_no_proto(caplog, reset_globals):
-    """Test sendToRadio()"""
-    iface = MeshInterface()
-    with caplog.at_level(logging.DEBUG):
-        iface._sendToRadioImpl('foo')
-    assert re.search(r'Subclass must provide toradio', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-
-def test_generatePacketId(capsys, reset_globals) -
-
-

Test _generatePacketId() when no currentPacketId (not connected)

-
- -Expand source code - -
@pytest.mark.unit
-def test_generatePacketId(capsys, reset_globals):
-    """Test _generatePacketId() when no currentPacketId (not connected)"""
-    iface = MeshInterface(noProto=True)
-    # not sure when this condition would ever happen... but we can simulate it
-    iface.currentPacketId = None
-    assert iface.currentPacketId is None
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        iface._generatePacketId()
-        out, err = capsys.readouterr()
-        assert re.search(r'Not connected yet, can not generate packet', out, re.MULTILINE)
-        assert err == ''
-    assert pytest_wrapped_e.type == Exception
-
-
-
-def test_getLongName(reset_globals, iface_with_nodes) -
-
-

Test getLongName()

-
- -Expand source code - -
@pytest.mark.unit
-def test_getLongName(reset_globals, iface_with_nodes):
-    """Test getLongName()"""
-    iface = iface_with_nodes
-    iface.myInfo.my_node_num = 2475227164
-    mylongname = iface.getLongName()
-    assert mylongname == 'Unknown f81c'
-
-
-
-def test_getMyNodeInfo(reset_globals) -
-
-

Test getMyNodeInfo()

-
- -Expand source code - -
@pytest.mark.unit
-def test_getMyNodeInfo(reset_globals):
-    """Test getMyNodeInfo()"""
-    iface = MeshInterface(noProto=True)
-    anode = iface.getNode(LOCAL_ADDR)
-    iface.nodesByNum = {1: anode }
-    assert iface.nodesByNum.get(1) == anode
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-    iface.myInfo.my_node_num = 1
-    myinfo = iface.getMyNodeInfo()
-    assert myinfo == anode
-
-
-
-def test_getMyUser(reset_globals, iface_with_nodes) -
-
-

Test getMyUser()

-
- -Expand source code - -
@pytest.mark.unit
-def test_getMyUser(reset_globals, iface_with_nodes):
-    """Test getMyUser()"""
-    iface = iface_with_nodes
-
-    iface.myInfo.my_node_num = 2475227164
-    myuser = iface.getMyUser()
-    print(f'myuser:{myuser}')
-    assert myuser is not None
-    assert myuser["id"] == '!9388f81c'
-
-
-
-def test_getNode_not_local(reset_globals, caplog) -
-
-

Test getNode not local

-
- -Expand source code - -
@pytest.mark.unit
-def test_getNode_not_local(reset_globals, caplog):
-    """Test getNode not local"""
-    iface = MeshInterface(noProto=True)
-    anode = MagicMock(autospec=Node)
-    with caplog.at_level(logging.DEBUG):
-        with patch('meshtastic.node.Node', return_value=anode):
-            another_node = iface.getNode('bar2')
-            assert another_node != iface.localNode
-    assert re.search(r'About to requestConfig', caplog.text, re.MULTILINE)
-
-
-
-def test_getNode_not_local_timeout(reset_globals, capsys) -
-
-

Test getNode not local, simulate timeout

-
- -Expand source code - -
@pytest.mark.unit
-def test_getNode_not_local_timeout(reset_globals, capsys):
-    """Test getNode not local, simulate timeout"""
-    iface = MeshInterface(noProto=True)
-    anode = MagicMock(autospec=Node)
-    anode.waitForConfig.return_value = False
-    with patch('meshtastic.node.Node', return_value=anode):
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            iface.getNode('bar2')
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.match(r'Error: Timed out waiting for node config', out)
-        assert err == ''
-
-
-
-def test_getNode_with_local(reset_globals) -
-
-

Test getNode

-
- -Expand source code - -
@pytest.mark.unit
-def test_getNode_with_local(reset_globals):
-    """Test getNode"""
-    iface = MeshInterface(noProto=True)
-    anode = iface.getNode(LOCAL_ADDR)
-    assert anode == iface.localNode
-
-
-
-def test_getShortName(reset_globals, iface_with_nodes) -
-
-

Test getShortName().

-
- -Expand source code - -
@pytest.mark.unit
-def test_getShortName(reset_globals, iface_with_nodes):
-    """Test getShortName()."""
-    iface = iface_with_nodes
-    iface.myInfo.my_node_num = 2475227164
-    myshortname = iface.getShortName()
-    assert myshortname == '?1C'
-
-
-
-def test_handleFromRadio_empty_payload(reset_globals, caplog) -
-
-

Test _handleFromRadio

-
- -Expand source code - -
@pytest.mark.unit
-def test_handleFromRadio_empty_payload(reset_globals, caplog):
-    """Test _handleFromRadio"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._handleFromRadio(b'')
-    iface.close()
-    assert re.search(r'Unexpected FromRadio payload', caplog.text, re.MULTILINE)
-
-
-
-def test_handleFromRadio_with_my_info(reset_globals, caplog) -
-
-

Test _handleFromRadio with my_info

-
- -Expand source code - -
@pytest.mark.unit
-def test_handleFromRadio_with_my_info(reset_globals, caplog):
-    """Test _handleFromRadio with my_info"""
-    # Note: I captured the '--debug --info' for the bytes below.
-    # It "translates" to this:
-    # my_info {
-    #  my_node_num: 682584012
-    #  num_bands: 13
-    #  firmware_version: "1.2.49.5354c49"
-    #  reboot_count: 13
-    #  bitrate: 17.088470458984375
-    #  message_timeout_msec: 300000
-    #  min_app_version: 20200
-    #  max_channels: 8
-    # }
-    from_radio_bytes = b'\x1a,\x08\xcc\xcf\xbd\xc5\x02\x18\r2\x0e1.2.49.5354c49P\r]0\xb5\x88Ah\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._handleFromRadio(from_radio_bytes)
-    iface.close()
-    assert re.search(r'Received myinfo', caplog.text, re.MULTILINE)
-    assert re.search(r'num_bands: 13', caplog.text, re.MULTILINE)
-    assert re.search(r'max_channels: 8', caplog.text, re.MULTILINE)
-
-
-
-def test_handleFromRadio_with_node_info(reset_globals, caplog, capsys) -
-
-

Test _handleFromRadio with node_info

-
- -Expand source code - -
@pytest.mark.unit
-def test_handleFromRadio_with_node_info(reset_globals, caplog, capsys):
-    """Test _handleFromRadio with node_info"""
-    # Note: I captured the '--debug --info' for the bytes below.
-    # It "translates" to this:
-    # node_info {
-    #  num: 682584012
-    #  user {
-    #    id: "!28af67cc"
-    #    long_name: "Unknown 67cc"
-    #    short_name: "?CC"
-    #    macaddr: "$o(\257g\314"
-    #    hw_model: HELTEC_V2_1
-    #  }
-    #  position {
-    #    }
-    #  }
-
-    from_radio_bytes = b'"2\x08\xcc\xcf\xbd\xc5\x02\x12(\n\t!28af67cc\x12\x0cUnknown 67cc\x1a\x03?CC"\x06$o(\xafg\xcc0\n\x1a\x00'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._startConfig()
-        iface._handleFromRadio(from_radio_bytes)
-        assert re.search(r'Received nodeinfo', caplog.text, re.MULTILINE)
-        assert re.search(r'682584012', caplog.text, re.MULTILINE)
-        assert re.search(r'HELTEC_V2_1', caplog.text, re.MULTILINE)
-        # validate some of showNodes() output
-        iface.showNodes()
-        out, err = capsys.readouterr()
-        assert re.search(r' 1 ', out, re.MULTILINE)
-        assert re.search(r'│ Unknown 67cc │ ', out, re.MULTILINE)
-        assert re.search(r'│ !28af67cc │ N/A   │ N/A         │ N/A', out, re.MULTILINE)
-        assert err == ''
-        iface.close()
-
-
-
-def test_handleFromRadio_with_node_info_tbeam1(reset_globals, caplog, capsys) -
-
-

Test _handleFromRadio with node_info

-
- -Expand source code - -
@pytest.mark.unit
-def test_handleFromRadio_with_node_info_tbeam1(reset_globals, caplog, capsys):
-    """Test _handleFromRadio with node_info"""
-    # Note: Captured the '--debug --info' for the bytes below.
-    # pylint: disable=C0301
-    from_radio_bytes = b'"=\x08\x80\xf8\xc8\xf6\x07\x12"\n\t!7ed23c00\x12\x07TBeam 1\x1a\x02T1"\x06\x94\xb9~\xd2<\x000\x04\x1a\x07 ]MN\x01\xbea%\xad\x01\xbea=\x00\x00,A'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._startConfig()
-        iface._handleFromRadio(from_radio_bytes)
-        assert re.search(r'Received nodeinfo', caplog.text, re.MULTILINE)
-        assert re.search(r'TBeam 1', caplog.text, re.MULTILINE)
-        assert re.search(r'2127707136', caplog.text, re.MULTILINE)
-        # validate some of showNodes() output
-        iface.showNodes()
-        out, err = capsys.readouterr()
-        assert re.search(r' 1 ', out, re.MULTILINE)
-        assert re.search(r'│ TBeam 1 │ ', out, re.MULTILINE)
-        assert re.search(r'│ !7ed23c00 │', out, re.MULTILINE)
-        assert err == ''
-        iface.close()
-
-
-
-def test_handleFromRadio_with_node_info_tbeam_with_bad_data(reset_globals, caplog, capsys) -
-
-

Test _handleFromRadio with node_info with some bad data (issue#172) - ensure we do not throw exception

-
- -Expand source code - -
@pytest.mark.unit
-def test_handleFromRadio_with_node_info_tbeam_with_bad_data(reset_globals, caplog, capsys):
-    """Test _handleFromRadio with node_info with some bad data (issue#172) - ensure we do not throw exception"""
-    # Note: Captured the '--debug --info' for the bytes below.
-    from_radio_bytes = b'"\x17\x08\xdc\x8a\x8a\xae\x02\x12\x08"\x06\x00\x00\x00\x00\x00\x00\x1a\x00=\x00\x00\xb8@'
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface._startConfig()
-        iface._handleFromRadio(from_radio_bytes)
-
-
-
-def test_handlePacketFromRadio_no_from(capsys, reset_globals) -
-
-

Test _handlePacketFromRadio with no 'from' in the mesh packet.

-
- -Expand source code - -
@pytest.mark.unit
-def test_handlePacketFromRadio_no_from(capsys, reset_globals):
-    """Test _handlePacketFromRadio with no 'from' in the mesh packet."""
-    iface = MeshInterface(noProto=True)
-    meshPacket = mesh_pb2.MeshPacket()
-    iface._handlePacketFromRadio(meshPacket)
-    out, err = capsys.readouterr()
-    assert re.search(r'Device returned a packet we sent, ignoring', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_handlePacketFromRadio_no_portnum(caplog, reset_globals) -
-
-

Test _handlePacketFromRadio without a portnum

-
- -Expand source code - -
@pytest.mark.unit
-def test_handlePacketFromRadio_no_portnum(caplog, reset_globals):
-    """Test _handlePacketFromRadio without a portnum"""
-    iface = MeshInterface(noProto=True)
-    meshPacket = mesh_pb2.MeshPacket()
-    meshPacket.decoded.payload = b''
-    with caplog.at_level(logging.WARNING):
-        iface._handlePacketFromRadio(meshPacket, hack=True)
-    assert re.search(r'Not populating fromId', caplog.text, re.MULTILINE)
-
-
-
-def test_handlePacketFromRadio_with_a_portnum(caplog, reset_globals) -
-
-

Test _handlePacketFromRadio with a portnum -Since we have an attribute called 'from', we cannot simply 'set' it. -Had to implement a hack just to be able to test some code.

-
- -Expand source code - -
@pytest.mark.unit
-def test_handlePacketFromRadio_with_a_portnum(caplog, reset_globals):
-    """Test _handlePacketFromRadio with a portnum
-       Since we have an attribute called 'from', we cannot simply 'set' it.
-       Had to implement a hack just to be able to test some code.
-    """
-    iface = MeshInterface(noProto=True)
-    meshPacket = mesh_pb2.MeshPacket()
-    meshPacket.decoded.payload = b''
-    meshPacket.decoded.portnum = 1
-    with caplog.at_level(logging.WARNING):
-        iface._handlePacketFromRadio(meshPacket, hack=True)
-    assert re.search(r'Not populating fromId', caplog.text, re.MULTILINE)
-
-
-
-def test_sendData_too_long(caplog, reset_globals) -
-
-

Test when data payload is too big

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendData_too_long(caplog, reset_globals):
-    """Test when data payload is too big"""
-    iface = MeshInterface(noProto=True)
-    some_large_text = b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    some_large_text += b'This is a long text that will be too long for send text.'
-    with caplog.at_level(logging.DEBUG):
-        with pytest.raises(Exception) as pytest_wrapped_e:
-            iface.sendData(some_large_text)
-            assert re.search('Data payload too big', caplog.text, re.MULTILINE)
-        assert pytest_wrapped_e.type == Exception
-    iface.close()
-
-
-
-def test_sendData_unknown_app(capsys, reset_globals) -
-
-

Test sendData when unknown app

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendData_unknown_app(capsys, reset_globals):
-    """Test sendData when unknown app"""
-    iface = MeshInterface(noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        iface.sendData(b'hello', portNum=0)
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: A non-zero port number', out, re.MULTILINE)
-    assert err == ''
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog, reset_globals) -
-
-

Test _sendPacket() with BROADCAST_ADDR as a destination

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog, reset_globals):
-    """Test _sendPacket() with BROADCAST_ADDR as a destination"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=BROADCAST_ADDR)
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-
-def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys, reset_globals) -
-
-

Test _sendPacket() with LOCAL_ADDR as a destination with no myInfo

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys, reset_globals):
-    """Test _sendPacket() with LOCAL_ADDR as a destination with no myInfo"""
-    iface = MeshInterface(noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: No myInfo', out, re.MULTILINE)
-    assert err == ''
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog, reset_globals) -
-
-

Test _sendPacket() with LOCAL_ADDR as a destination with myInfo

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog, reset_globals):
-    """Test _sendPacket() with LOCAL_ADDR as a destination with myInfo"""
-    iface = MeshInterface(noProto=True)
-    myInfo = MagicMock()
-    iface.myInfo = myInfo
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-
-def test_sendPacket_with_destination_as_int(caplog, reset_globals) -
-
-

Test _sendPacket() with int as a destination

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_destination_as_int(caplog, reset_globals):
-    """Test _sendPacket() with int as a destination"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId=123)
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-
-def test_sendPacket_with_destination_is_blank_with_nodes(capsys, reset_globals, iface_with_nodes) -
-
-

Test _sendPacket() with '' as a destination with myInfo

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_destination_is_blank_with_nodes(capsys, reset_globals, iface_with_nodes):
-    """Test _sendPacket() with '' as a destination with myInfo"""
-    iface = iface_with_nodes
-    meshPacket = mesh_pb2.MeshPacket()
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        iface._sendPacket(meshPacket, destinationId='')
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.match(r'Warning: NodeId  not found in DB', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_sendPacket_with_destination_is_blank_without_nodes(caplog, reset_globals, iface_with_nodes) -
-
-

Test _sendPacket() with '' as a destination with myInfo

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_destination_is_blank_without_nodes(caplog, reset_globals, iface_with_nodes):
-    """Test _sendPacket() with '' as a destination with myInfo"""
-    iface = iface_with_nodes
-    iface.nodes = None
-    meshPacket = mesh_pb2.MeshPacket()
-    with caplog.at_level(logging.WARNING):
-        iface._sendPacket(meshPacket, destinationId='')
-    assert re.search(r'Warning: There were no self.nodes.', caplog.text, re.MULTILINE)
-
-
-
-def test_sendPacket_with_destination_starting_with_a_bang(caplog, reset_globals) -
-
-

Test _sendPacket() with int as a destination

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_destination_starting_with_a_bang(caplog, reset_globals):
-    """Test _sendPacket() with int as a destination"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        meshPacket = mesh_pb2.MeshPacket()
-        iface._sendPacket(meshPacket, destinationId='!1234')
-        assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
-
-
-
-def test_sendPacket_with_no_destination(capsys, reset_globals) -
-
-

Test _sendPacket()

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPacket_with_no_destination(capsys, reset_globals):
-    """Test _sendPacket()"""
-    iface = MeshInterface(noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        iface._sendPacket(b'', destinationId=None)
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: destinationId must not be None', out, re.MULTILINE)
-    assert err == ''
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_sendPosition(reset_globals, caplog) -
-
-

Test sendPosition

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPosition(reset_globals, caplog):
-    """Test sendPosition"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface.sendPosition()
-    iface.close()
-    assert re.search(r'p.time:', caplog.text, re.MULTILINE)
-
-
-
-def test_sendPosition_with_a_position(caplog, reset_globals) -
-
-

Test sendPosition when lat/long/alt

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendPosition_with_a_position(caplog, reset_globals):
-    """Test sendPosition when lat/long/alt"""
-    iface = MeshInterface(noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        iface.sendPosition(latitude=40.8, longitude=-111.86, altitude=201)
-        assert re.search(r'p.latitude_i:408', caplog.text, re.MULTILINE)
-        assert re.search(r'p.longitude_i:-11186', caplog.text, re.MULTILINE)
-        assert re.search(r'p.altitude:201', caplog.text, re.MULTILINE)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_node.html b/docs/meshtastic/tests/test_node.html deleted file mode 100644 index 5ee6949..0000000 --- a/docs/meshtastic/tests/test_node.html +++ /dev/null @@ -1,2272 +0,0 @@ - - - - - - -meshtastic.tests.test_node API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_node

-
-
-

Meshtastic unit tests for node.py

-
- -Expand source code - -
"""Meshtastic unit tests for node.py"""
-
-import re
-import logging
-
-from unittest.mock import patch, MagicMock
-import pytest
-
-from ..node import Node
-from ..serial_interface import SerialInterface
-from ..admin_pb2 import AdminMessage
-from ..channel_pb2 import Channel
-from ..radioconfig_pb2 import RadioConfig
-
-
-@pytest.mark.unit
-def test_node(capsys):
-    """Test that we can instantiate a Node"""
-    anode = Node('foo', 'bar')
-    radioConfig = RadioConfig()
-    anode.radioConfig = radioConfig
-    anode.showChannels()
-    anode.showInfo()
-    out, err = capsys.readouterr()
-    assert re.search(r'Preferences', out)
-    assert re.search(r'Channels', out)
-    assert re.search(r'Primary channel URL', out)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_node_reqquestConfig():
-    """Test run requestConfig"""
-    iface = MagicMock(autospec=SerialInterface)
-    amesg = MagicMock(autospec=AdminMessage)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
-            anode = Node(mo, 'bar')
-            anode.requestConfig()
-
-
-@pytest.mark.unit
-def test_setOwner_and_team(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='Test123', short_name='123', team=1)
-    assert re.search(r'p.set_owner.long_name:Test123:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:123:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:1', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_setOwner_no_short_name(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='Test123')
-    assert re.search(r'p.set_owner.long_name:Test123:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:Tst:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_setOwner_no_short_name_and_long_name_is_short(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='Tnt')
-    assert re.search(r'p.set_owner.long_name:Tnt:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:Tnt:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_setOwner_no_short_name_and_long_name_has_words(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='A B C', is_licensed=True)
-    assert re.search(r'p.set_owner.long_name:A B C:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:ABC:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:True', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_exitSimulator(caplog):
-    """Test exitSimulator"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.exitSimulator()
-    assert re.search(r'in exitSimulator', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_reboot(caplog):
-    """Test reboot"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.reboot()
-    assert re.search(r'Telling node to reboot', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_setURL_empty_url():
-    """Test reboot"""
-    anode = Node('foo', 'bar', noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode.setURL('')
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_setURL_valid_URL(caplog):
-    """Test setURL"""
-    iface = MagicMock(autospec=SerialInterface)
-    url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
-    with caplog.at_level(logging.DEBUG):
-        anode = Node(iface, 'bar', noProto=True)
-        anode.radioConfig = 'baz'
-        channels = ['zoo']
-        anode.channels = channels
-        anode.setURL(url)
-    assert re.search(r'Channel i:0', caplog.text, re.MULTILINE)
-    assert re.search(r'modem_config: Bw125Cr48Sf4096', caplog.text, re.MULTILINE)
-    assert re.search(r'psk: "\\001"', caplog.text, re.MULTILINE)
-    assert re.search(r'role: PRIMARY', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_setURL_valid_URL_but_no_settings(caplog):
-    """Test setURL"""
-    iface = MagicMock(autospec=SerialInterface)
-    url = "https://www.meshtastic.org/d/#"
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode = Node(iface, 'bar', noProto=True)
-        anode.radioConfig = 'baz'
-        anode.setURL(url)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_showChannels(capsys):
-    """Test showChannels"""
-    anode = Node('foo', 'bar')
-
-    # primary channel
-    # role: 0=Disabled, 1=Primary, 2=Secondary
-    # modem_config: 0-5
-    # role: 0=Disabled, 1=Primary, 2=Secondary
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testing'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    anode.showChannels()
-    out, err = capsys.readouterr()
-    assert re.search(r'Channels:', out, re.MULTILINE)
-    # primary channel
-    assert re.search(r'Primary channel URL', out, re.MULTILINE)
-    assert re.search(r'PRIMARY psk=default ', out, re.MULTILINE)
-    assert re.search(r'"modemConfig": "Bw125Cr48Sf4096"', out, re.MULTILINE)
-    assert re.search(r'"psk": "AQ=="', out, re.MULTILINE)
-    # secondary channel
-    assert re.search(r'SECONDARY psk=secret ', out, re.MULTILINE)
-    assert re.search(r'"psk": "ipR5DsbJHjWREkCmMKi0M4cA8ksO539Bes31sJAwqDQ="', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_getChannelByChannelIndex():
-    """Test getChannelByChannelIndex()"""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1) # primary channel
-    channel2 = Channel(index=2, role=2) # secondary channel
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-
-    # test primary
-    assert anode.getChannelByChannelIndex(0) is not None
-    # test secondary
-    assert anode.getChannelByChannelIndex(1) is not None
-    # test disabled
-    assert anode.getChannelByChannelIndex(2) is not None
-    # test invalid values
-    assert anode.getChannelByChannelIndex(-1) is None
-    assert anode.getChannelByChannelIndex(9) is None
-
-
-@pytest.mark.unit
-def test_deleteChannel_try_to_delete_primary_channel(capsys):
-    """Try to delete primary channel."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    # no secondary channels
-    channel2 = Channel(index=2, role=0)
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode.deleteChannel(0)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: Only SECONDARY channels can be deleted', out, re.MULTILINE)
-    assert err == ''
-
-@pytest.mark.unit
-def test_deleteChannel_secondary():
-    """Try to delete a secondary channel."""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testing'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        anode.channels = channels
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'testing'
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-        anode.deleteChannel(1)
-
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == ''
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-
-@pytest.mark.unit
-def test_deleteChannel_secondary_with_admin_channel_after_testing():
-    """Try to delete a secondary channel where there is an admin channel."""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testing'
-
-    channel3 = Channel(index=3, role=2)
-    channel3.settings.name = 'admin'
-
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        assert mo.localNode == anode
-
-        anode.channels = channels
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'testing'
-        assert channels[2].settings.name == 'admin'
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-        anode.deleteChannel(1)
-
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'admin'
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-
-@pytest.mark.unit
-def test_deleteChannel_secondary_with_admin_channel_before_testing():
-    """Try to delete a secondary channel where there is an admin channel."""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=2)
-    channel3.settings.name = 'testing'
-
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        anode.channels = channels
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'admin'
-        assert channels[2].settings.name == 'testing'
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-        anode.deleteChannel(2)
-
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'admin'
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-
-@pytest.mark.unit
-def test_getChannelByName(capsys):
-    """Get a channel by the name."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getChannelByName('admin')
-    assert ch.index == 2
-
-
-@pytest.mark.unit
-def test_getChannelByName_invalid_name(capsys):
-    """Get a channel by the name but one that is not present."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getChannelByName('testing')
-    assert ch is None
-
-
-@pytest.mark.unit
-def test_getDisabledChannel(capsys):
-    """Get the first disabled channel."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testingA'
-
-    channel3 = Channel(index=3, role=2)
-    channel3.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel3.settings.name = 'testingB'
-
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getDisabledChannel()
-    assert ch.index == 4
-
-
-@pytest.mark.unit
-def test_getDisabledChannel_where_all_channels_are_used(capsys):
-    """Get the first disabled channel."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel3 = Channel(index=3, role=2)
-    channel4 = Channel(index=4, role=2)
-    channel5 = Channel(index=5, role=2)
-    channel6 = Channel(index=6, role=2)
-    channel7 = Channel(index=7, role=2)
-    channel8 = Channel(index=8, role=2)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getDisabledChannel()
-    assert ch is None
-
-
-@pytest.mark.unit
-def test_getAdminChannelIndex(capsys):
-    """Get the 'admin' channel index."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    i = anode._getAdminChannelIndex()
-    assert i == 2
-
-
-@pytest.mark.unit
-def test_getAdminChannelIndex_when_no_admin_named_channel(capsys):
-    """Get the 'admin' channel when there is not one."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=0)
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    i = anode._getAdminChannelIndex()
-    assert i == 0
-
-
-# TODO: should we check if we need to turn it off?
-@pytest.mark.unit
-def test_turnOffEncryptionOnPrimaryChannel(capsys):
-    """Turn off encryption when there is a psk."""
-    anode = Node('foo', 'bar', noProto=True)
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    # value from using "--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b "
-    channel1.settings.psk = b'\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++'
-
-    channel2 = Channel(index=2, role=0)
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    anode.turnOffEncryptionOnPrimaryChannel()
-    out, err = capsys.readouterr()
-    assert re.search(r'Writing modified channels to device', out)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_writeConfig_with_no_radioConfig(capsys):
-    """Test writeConfig with no radioConfig."""
-    anode = Node('foo', 'bar', noProto=True)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode.writeConfig()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Error: No RadioConfig has been read', out)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_writeConfig(caplog):
-    """Test writeConfig"""
-    anode = Node('foo', 'bar', noProto=True)
-    radioConfig = RadioConfig()
-    anode.radioConfig = radioConfig
-
-    with caplog.at_level(logging.DEBUG):
-        anode.writeConfig()
-    assert re.search(r'Wrote config', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_requestChannel_not_localNode(caplog):
-    """Test _requestChannel()"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-        with caplog.at_level(logging.DEBUG):
-            anode._requestChannel(0)
-            assert re.search(r'Requesting channel 0 info from remote node', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_requestChannel_localNode(caplog):
-    """Test _requestChannel()"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        with caplog.at_level(logging.DEBUG):
-            anode._requestChannel(0)
-            assert re.search(r'Requesting channel 0', caplog.text, re.MULTILINE)
-            assert not re.search(r'from remote node', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_onResponseRequestChannel(caplog):
-    """Test onResponseRequestChannel()"""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    msg1 = MagicMock(autospec=AdminMessage)
-    msg1.get_channel_response = channel1
-
-    msg2 = MagicMock(autospec=AdminMessage)
-    channel2 = Channel(index=2, role=0) # disabled
-    msg2.get_channel_response = channel2
-
-    # default primary channel
-    packet1 = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b':\t\x12\x05\x18\x03"\x01\x01\x18\x01',
-            'requestId': 2615094405,
-            'admin': {
-                'getChannelResponse': {
-                    'settings': {
-                        'modemConfig': 'Bw125Cr48Sf4096',
-                        'psk': 'AQ=='
-                    },
-                    'role': 'PRIMARY'
-                },
-                'raw': msg1,
-            }
-        },
-        'id': 1692918436,
-        'hopLimit': 3,
-        'priority':
-        'RELIABLE',
-        'raw': 'fake',
-        'fromId': '!9388f81c',
-        'toId': '!9388f81c'
-        }
-
-    # no other channels
-    packet2 = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b':\x04\x08\x02\x12\x00',
-            'requestId': 743049663,
-            'admin': {
-                'getChannelResponse': {
-                    'index': 2,
-                    'settings': {}
-                },
-                'raw': msg2,
-            }
-        },
-        'id': 1692918456,
-        'rxTime': 1640202239,
-        'hopLimit': 3,
-        'priority': 'RELIABLE',
-        'raw': 'faked',
-        'fromId': '!9388f81c',
-        'toId': '!9388f81c'
-    }
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        radioConfig = RadioConfig()
-        anode.radioConfig = radioConfig
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        with caplog.at_level(logging.DEBUG):
-            anode.requestConfig()
-            anode.onResponseRequestChannel(packet1)
-            assert re.search(r'Received channel', caplog.text, re.MULTILINE)
-            anode.onResponseRequestChannel(packet2)
-            assert re.search(r'Received channel', caplog.text, re.MULTILINE)
-            assert re.search(r'Finished downloading channels', caplog.text, re.MULTILINE)
-            assert len(anode.channels) == 8
-            assert anode.channels[0].settings.modem_config == 3
-            assert anode.channels[1].settings.name == ''
-            assert anode.channels[2].settings.name == ''
-            assert anode.channels[3].settings.name == ''
-            assert anode.channels[4].settings.name == ''
-            assert anode.channels[5].settings.name == ''
-            assert anode.channels[6].settings.name == ''
-            assert anode.channels[7].settings.name == ''
-
-
-@pytest.mark.unit
-def test_onResponseRequestSetting(caplog):
-    """Test onResponseRequestSetting()"""
-    # Note: Split out the get_radio_response to a MagicMock
-    # so it could be "returned" (not really sure how to do that
-    # in a python dict.
-    amsg = MagicMock(autospec=AdminMessage)
-    amsg.get_radio_response = """{
-  preferences {
-    phone_timeout_secs: 900
-    ls_secs: 300
-    position_broadcast_smart: true
-    position_flags: 35
-  }
-}"""
-    packet = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b'*\x0e\n\x0c0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#',
-            'requestId': 3145147848,
-            'admin': {
-                'getRadioResponse': {
-                    'preferences': {
-                        'phoneTimeoutSecs': 900,
-                        'lsSecs': 300,
-                        'positionBroadcastSmart': True,
-                        'positionFlags': 35
-                     }
-                },
-                'raw': amsg
-            },
-            'id': 365963704,
-            'rxTime': 1640195197,
-            'hopLimit': 3,
-            'priority': 'RELIABLE',
-            'raw': 'faked',
-            'fromId': '!9388f81c',
-            'toId': '!9388f81c'
-        }
-    }
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        radioConfig = RadioConfig()
-        anode.radioConfig = radioConfig
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        with caplog.at_level(logging.DEBUG):
-            anode.onResponseRequestSettings(packet)
-            assert re.search(r'Received radio config, now fetching channels..', caplog.text, re.MULTILINE)
-
-
-@pytest.mark.unit
-def test_onResponseRequestSetting_with_error(capsys):
-    """Test onResponseRequestSetting() with an error"""
-    packet = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b'*\x0e\n\x0c0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#',
-            'requestId': 3145147848,
-            'routing': {
-                'errorReason': 'some made up error',
-            },
-            'admin': {
-                'getRadioResponse': {
-                    'preferences': {
-                        'phoneTimeoutSecs': 900,
-                        'lsSecs': 300,
-                        'positionBroadcastSmart': True,
-                        'positionFlags': 35
-                     }
-                },
-            },
-            'id': 365963704,
-            'rxTime': 1640195197,
-            'hopLimit': 3,
-            'priority': 'RELIABLE',
-            'fromId': '!9388f81c',
-            'toId': '!9388f81c'
-        }
-    }
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        radioConfig = RadioConfig()
-        anode.radioConfig = radioConfig
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        anode.onResponseRequestSettings(packet)
-        out, err = capsys.readouterr()
-        assert re.search(r'Error on response', out)
-        assert err == ''
-
-
-
-
-
-
-
-

Functions

-
-
-def test_deleteChannel_secondary() -
-
-

Try to delete a secondary channel.

-
- -Expand source code - -
@pytest.mark.unit
-def test_deleteChannel_secondary():
-    """Try to delete a secondary channel."""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testing'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        anode.channels = channels
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'testing'
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-        anode.deleteChannel(1)
-
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == ''
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-
-
-def test_deleteChannel_secondary_with_admin_channel_after_testing() -
-
-

Try to delete a secondary channel where there is an admin channel.

-
- -Expand source code - -
@pytest.mark.unit
-def test_deleteChannel_secondary_with_admin_channel_after_testing():
-    """Try to delete a secondary channel where there is an admin channel."""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testing'
-
-    channel3 = Channel(index=3, role=2)
-    channel3.settings.name = 'admin'
-
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        assert mo.localNode == anode
-
-        anode.channels = channels
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'testing'
-        assert channels[2].settings.name == 'admin'
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-        anode.deleteChannel(1)
-
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'admin'
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-
-
-def test_deleteChannel_secondary_with_admin_channel_before_testing() -
-
-

Try to delete a secondary channel where there is an admin channel.

-
- -Expand source code - -
@pytest.mark.unit
-def test_deleteChannel_secondary_with_admin_channel_before_testing():
-    """Try to delete a secondary channel where there is an admin channel."""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=2)
-    channel3.settings.name = 'testing'
-
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        anode.channels = channels
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'admin'
-        assert channels[2].settings.name == 'testing'
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-        anode.deleteChannel(2)
-
-        assert len(anode.channels) == 8
-        assert channels[0].settings.modem_config == 3
-        assert channels[1].settings.name == 'admin'
-        assert channels[2].settings.name == ''
-        assert channels[3].settings.name == ''
-        assert channels[4].settings.name == ''
-        assert channels[5].settings.name == ''
-        assert channels[6].settings.name == ''
-        assert channels[7].settings.name == ''
-
-
-
-def test_deleteChannel_try_to_delete_primary_channel(capsys) -
-
-

Try to delete primary channel.

-
- -Expand source code - -
@pytest.mark.unit
-def test_deleteChannel_try_to_delete_primary_channel(capsys):
-    """Try to delete primary channel."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    # no secondary channels
-    channel2 = Channel(index=2, role=0)
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode.deleteChannel(0)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: Only SECONDARY channels can be deleted', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_exitSimulator(caplog) -
-
-

Test exitSimulator

-
- -Expand source code - -
@pytest.mark.unit
-def test_exitSimulator(caplog):
-    """Test exitSimulator"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.exitSimulator()
-    assert re.search(r'in exitSimulator', caplog.text, re.MULTILINE)
-
-
-
-def test_getAdminChannelIndex(capsys) -
-
-

Get the 'admin' channel index.

-
- -Expand source code - -
@pytest.mark.unit
-def test_getAdminChannelIndex(capsys):
-    """Get the 'admin' channel index."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    i = anode._getAdminChannelIndex()
-    assert i == 2
-
-
-
-def test_getAdminChannelIndex_when_no_admin_named_channel(capsys) -
-
-

Get the 'admin' channel when there is not one.

-
- -Expand source code - -
@pytest.mark.unit
-def test_getAdminChannelIndex_when_no_admin_named_channel(capsys):
-    """Get the 'admin' channel when there is not one."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=0)
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    i = anode._getAdminChannelIndex()
-    assert i == 0
-
-
-
-def test_getChannelByChannelIndex() -
-
-

Test getChannelByChannelIndex()

-
- -Expand source code - -
@pytest.mark.unit
-def test_getChannelByChannelIndex():
-    """Test getChannelByChannelIndex()"""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1) # primary channel
-    channel2 = Channel(index=2, role=2) # secondary channel
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-
-    # test primary
-    assert anode.getChannelByChannelIndex(0) is not None
-    # test secondary
-    assert anode.getChannelByChannelIndex(1) is not None
-    # test disabled
-    assert anode.getChannelByChannelIndex(2) is not None
-    # test invalid values
-    assert anode.getChannelByChannelIndex(-1) is None
-    assert anode.getChannelByChannelIndex(9) is None
-
-
-
-def test_getChannelByName(capsys) -
-
-

Get a channel by the name.

-
- -Expand source code - -
@pytest.mark.unit
-def test_getChannelByName(capsys):
-    """Get a channel by the name."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getChannelByName('admin')
-    assert ch.index == 2
-
-
-
-def test_getChannelByName_invalid_name(capsys) -
-
-

Get a channel by the name but one that is not present.

-
- -Expand source code - -
@pytest.mark.unit
-def test_getChannelByName_invalid_name(capsys):
-    """Get a channel by the name but one that is not present."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'admin'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getChannelByName('testing')
-    assert ch is None
-
-
-
-def test_getDisabledChannel(capsys) -
-
-

Get the first disabled channel.

-
- -Expand source code - -
@pytest.mark.unit
-def test_getDisabledChannel(capsys):
-    """Get the first disabled channel."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testingA'
-
-    channel3 = Channel(index=3, role=2)
-    channel3.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel3.settings.name = 'testingB'
-
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getDisabledChannel()
-    assert ch.index == 4
-
-
-
-def test_getDisabledChannel_where_all_channels_are_used(capsys) -
-
-

Get the first disabled channel.

-
- -Expand source code - -
@pytest.mark.unit
-def test_getDisabledChannel_where_all_channels_are_used(capsys):
-    """Get the first disabled channel."""
-    anode = Node('foo', 'bar')
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel3 = Channel(index=3, role=2)
-    channel4 = Channel(index=4, role=2)
-    channel5 = Channel(index=5, role=2)
-    channel6 = Channel(index=6, role=2)
-    channel7 = Channel(index=7, role=2)
-    channel8 = Channel(index=8, role=2)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    ch = anode.getDisabledChannel()
-    assert ch is None
-
-
-
-def test_node(capsys) -
-
-

Test that we can instantiate a Node

-
- -Expand source code - -
@pytest.mark.unit
-def test_node(capsys):
-    """Test that we can instantiate a Node"""
-    anode = Node('foo', 'bar')
-    radioConfig = RadioConfig()
-    anode.radioConfig = radioConfig
-    anode.showChannels()
-    anode.showInfo()
-    out, err = capsys.readouterr()
-    assert re.search(r'Preferences', out)
-    assert re.search(r'Channels', out)
-    assert re.search(r'Primary channel URL', out)
-    assert err == ''
-
-
-
-def test_node_reqquestConfig() -
-
-

Test run requestConfig

-
- -Expand source code - -
@pytest.mark.unit
-def test_node_reqquestConfig():
-    """Test run requestConfig"""
-    iface = MagicMock(autospec=SerialInterface)
-    amesg = MagicMock(autospec=AdminMessage)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
-            anode = Node(mo, 'bar')
-            anode.requestConfig()
-
-
-
-def test_onResponseRequestChannel(caplog) -
-
-

Test onResponseRequestChannel()

-
- -Expand source code - -
@pytest.mark.unit
-def test_onResponseRequestChannel(caplog):
-    """Test onResponseRequestChannel()"""
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    msg1 = MagicMock(autospec=AdminMessage)
-    msg1.get_channel_response = channel1
-
-    msg2 = MagicMock(autospec=AdminMessage)
-    channel2 = Channel(index=2, role=0) # disabled
-    msg2.get_channel_response = channel2
-
-    # default primary channel
-    packet1 = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b':\t\x12\x05\x18\x03"\x01\x01\x18\x01',
-            'requestId': 2615094405,
-            'admin': {
-                'getChannelResponse': {
-                    'settings': {
-                        'modemConfig': 'Bw125Cr48Sf4096',
-                        'psk': 'AQ=='
-                    },
-                    'role': 'PRIMARY'
-                },
-                'raw': msg1,
-            }
-        },
-        'id': 1692918436,
-        'hopLimit': 3,
-        'priority':
-        'RELIABLE',
-        'raw': 'fake',
-        'fromId': '!9388f81c',
-        'toId': '!9388f81c'
-        }
-
-    # no other channels
-    packet2 = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b':\x04\x08\x02\x12\x00',
-            'requestId': 743049663,
-            'admin': {
-                'getChannelResponse': {
-                    'index': 2,
-                    'settings': {}
-                },
-                'raw': msg2,
-            }
-        },
-        'id': 1692918456,
-        'rxTime': 1640202239,
-        'hopLimit': 3,
-        'priority': 'RELIABLE',
-        'raw': 'faked',
-        'fromId': '!9388f81c',
-        'toId': '!9388f81c'
-    }
-
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        radioConfig = RadioConfig()
-        anode.radioConfig = radioConfig
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        with caplog.at_level(logging.DEBUG):
-            anode.requestConfig()
-            anode.onResponseRequestChannel(packet1)
-            assert re.search(r'Received channel', caplog.text, re.MULTILINE)
-            anode.onResponseRequestChannel(packet2)
-            assert re.search(r'Received channel', caplog.text, re.MULTILINE)
-            assert re.search(r'Finished downloading channels', caplog.text, re.MULTILINE)
-            assert len(anode.channels) == 8
-            assert anode.channels[0].settings.modem_config == 3
-            assert anode.channels[1].settings.name == ''
-            assert anode.channels[2].settings.name == ''
-            assert anode.channels[3].settings.name == ''
-            assert anode.channels[4].settings.name == ''
-            assert anode.channels[5].settings.name == ''
-            assert anode.channels[6].settings.name == ''
-            assert anode.channels[7].settings.name == ''
-
-
-
-def test_onResponseRequestSetting(caplog) -
-
-

Test onResponseRequestSetting()

-
- -Expand source code - -
@pytest.mark.unit
-def test_onResponseRequestSetting(caplog):
-    """Test onResponseRequestSetting()"""
-    # Note: Split out the get_radio_response to a MagicMock
-    # so it could be "returned" (not really sure how to do that
-    # in a python dict.
-    amsg = MagicMock(autospec=AdminMessage)
-    amsg.get_radio_response = """{
-  preferences {
-    phone_timeout_secs: 900
-    ls_secs: 300
-    position_broadcast_smart: true
-    position_flags: 35
-  }
-}"""
-    packet = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b'*\x0e\n\x0c0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#',
-            'requestId': 3145147848,
-            'admin': {
-                'getRadioResponse': {
-                    'preferences': {
-                        'phoneTimeoutSecs': 900,
-                        'lsSecs': 300,
-                        'positionBroadcastSmart': True,
-                        'positionFlags': 35
-                     }
-                },
-                'raw': amsg
-            },
-            'id': 365963704,
-            'rxTime': 1640195197,
-            'hopLimit': 3,
-            'priority': 'RELIABLE',
-            'raw': 'faked',
-            'fromId': '!9388f81c',
-            'toId': '!9388f81c'
-        }
-    }
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        radioConfig = RadioConfig()
-        anode.radioConfig = radioConfig
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        with caplog.at_level(logging.DEBUG):
-            anode.onResponseRequestSettings(packet)
-            assert re.search(r'Received radio config, now fetching channels..', caplog.text, re.MULTILINE)
-
-
-
-def test_onResponseRequestSetting_with_error(capsys) -
-
-

Test onResponseRequestSetting() with an error

-
- -Expand source code - -
@pytest.mark.unit
-def test_onResponseRequestSetting_with_error(capsys):
-    """Test onResponseRequestSetting() with an error"""
-    packet = {
-        'from': 2475227164,
-        'to': 2475227164,
-        'decoded': {
-            'portnum': 'ADMIN_APP',
-            'payload': b'*\x0e\n\x0c0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#',
-            'requestId': 3145147848,
-            'routing': {
-                'errorReason': 'some made up error',
-            },
-            'admin': {
-                'getRadioResponse': {
-                    'preferences': {
-                        'phoneTimeoutSecs': 900,
-                        'lsSecs': 300,
-                        'positionBroadcastSmart': True,
-                        'positionFlags': 35
-                     }
-                },
-            },
-            'id': 365963704,
-            'rxTime': 1640195197,
-            'hopLimit': 3,
-            'priority': 'RELIABLE',
-            'fromId': '!9388f81c',
-            'toId': '!9388f81c'
-        }
-    }
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        radioConfig = RadioConfig()
-        anode.radioConfig = radioConfig
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        anode.onResponseRequestSettings(packet)
-        out, err = capsys.readouterr()
-        assert re.search(r'Error on response', out)
-        assert err == ''
-
-
-
-def test_reboot(caplog) -
-
-

Test reboot

-
- -Expand source code - -
@pytest.mark.unit
-def test_reboot(caplog):
-    """Test reboot"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.reboot()
-    assert re.search(r'Telling node to reboot', caplog.text, re.MULTILINE)
-
-
-
-def test_requestChannel_localNode(caplog) -
-
-

Test _requestChannel()

-
- -Expand source code - -
@pytest.mark.unit
-def test_requestChannel_localNode(caplog):
-    """Test _requestChannel()"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-
-        # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
-        mo.localNode = anode
-
-        with caplog.at_level(logging.DEBUG):
-            anode._requestChannel(0)
-            assert re.search(r'Requesting channel 0', caplog.text, re.MULTILINE)
-            assert not re.search(r'from remote node', caplog.text, re.MULTILINE)
-
-
-
-def test_requestChannel_not_localNode(caplog) -
-
-

Test _requestChannel()

-
- -Expand source code - -
@pytest.mark.unit
-def test_requestChannel_not_localNode(caplog):
-    """Test _requestChannel()"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        mo.myInfo.max_channels = 8
-        anode = Node(mo, 'bar', noProto=True)
-        with caplog.at_level(logging.DEBUG):
-            anode._requestChannel(0)
-            assert re.search(r'Requesting channel 0 info from remote node', caplog.text, re.MULTILINE)
-
-
-
-def test_setOwner_and_team(caplog) -
-
-

Test setOwner

-
- -Expand source code - -
@pytest.mark.unit
-def test_setOwner_and_team(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='Test123', short_name='123', team=1)
-    assert re.search(r'p.set_owner.long_name:Test123:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:123:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:1', caplog.text, re.MULTILINE)
-
-
-
-def test_setOwner_no_short_name(caplog) -
-
-

Test setOwner

-
- -Expand source code - -
@pytest.mark.unit
-def test_setOwner_no_short_name(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='Test123')
-    assert re.search(r'p.set_owner.long_name:Test123:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:Tst:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
-
-
-
-def test_setOwner_no_short_name_and_long_name_has_words(caplog) -
-
-

Test setOwner

-
- -Expand source code - -
@pytest.mark.unit
-def test_setOwner_no_short_name_and_long_name_has_words(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='A B C', is_licensed=True)
-    assert re.search(r'p.set_owner.long_name:A B C:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:ABC:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:True', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
-
-
-
-def test_setOwner_no_short_name_and_long_name_is_short(caplog) -
-
-

Test setOwner

-
- -Expand source code - -
@pytest.mark.unit
-def test_setOwner_no_short_name_and_long_name_is_short(caplog):
-    """Test setOwner"""
-    anode = Node('foo', 'bar', noProto=True)
-    with caplog.at_level(logging.DEBUG):
-        anode.setOwner(long_name ='Tnt')
-    assert re.search(r'p.set_owner.long_name:Tnt:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.short_name:Tnt:', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.is_licensed:False', caplog.text, re.MULTILINE)
-    assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
-
-
-
-def test_setURL_empty_url() -
-
-

Test reboot

-
- -Expand source code - -
@pytest.mark.unit
-def test_setURL_empty_url():
-    """Test reboot"""
-    anode = Node('foo', 'bar', noProto=True)
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode.setURL('')
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_setURL_valid_URL(caplog) -
-
-

Test setURL

-
- -Expand source code - -
@pytest.mark.unit
-def test_setURL_valid_URL(caplog):
-    """Test setURL"""
-    iface = MagicMock(autospec=SerialInterface)
-    url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
-    with caplog.at_level(logging.DEBUG):
-        anode = Node(iface, 'bar', noProto=True)
-        anode.radioConfig = 'baz'
-        channels = ['zoo']
-        anode.channels = channels
-        anode.setURL(url)
-    assert re.search(r'Channel i:0', caplog.text, re.MULTILINE)
-    assert re.search(r'modem_config: Bw125Cr48Sf4096', caplog.text, re.MULTILINE)
-    assert re.search(r'psk: "\\001"', caplog.text, re.MULTILINE)
-    assert re.search(r'role: PRIMARY', caplog.text, re.MULTILINE)
-
-
-
-def test_setURL_valid_URL_but_no_settings(caplog) -
-
-

Test setURL

-
- -Expand source code - -
@pytest.mark.unit
-def test_setURL_valid_URL_but_no_settings(caplog):
-    """Test setURL"""
-    iface = MagicMock(autospec=SerialInterface)
-    url = "https://www.meshtastic.org/d/#"
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode = Node(iface, 'bar', noProto=True)
-        anode.radioConfig = 'baz'
-        anode.setURL(url)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_showChannels(capsys) -
-
-

Test showChannels

-
- -Expand source code - -
@pytest.mark.unit
-def test_showChannels(capsys):
-    """Test showChannels"""
-    anode = Node('foo', 'bar')
-
-    # primary channel
-    # role: 0=Disabled, 1=Primary, 2=Secondary
-    # modem_config: 0-5
-    # role: 0=Disabled, 1=Primary, 2=Secondary
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    channel1.settings.psk = b'\x01'
-
-    channel2 = Channel(index=2, role=2)
-    channel2.settings.psk = b'\x8a\x94y\x0e\xc6\xc9\x1e5\x91\x12@\xa60\xa8\xb43\x87\x00\xf2K\x0e\xe7\x7fAz\xcd\xf5\xb0\x900\xa84'
-    channel2.settings.name = 'testing'
-
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    anode.showChannels()
-    out, err = capsys.readouterr()
-    assert re.search(r'Channels:', out, re.MULTILINE)
-    # primary channel
-    assert re.search(r'Primary channel URL', out, re.MULTILINE)
-    assert re.search(r'PRIMARY psk=default ', out, re.MULTILINE)
-    assert re.search(r'"modemConfig": "Bw125Cr48Sf4096"', out, re.MULTILINE)
-    assert re.search(r'"psk": "AQ=="', out, re.MULTILINE)
-    # secondary channel
-    assert re.search(r'SECONDARY psk=secret ', out, re.MULTILINE)
-    assert re.search(r'"psk": "ipR5DsbJHjWREkCmMKi0M4cA8ksO539Bes31sJAwqDQ="', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_turnOffEncryptionOnPrimaryChannel(capsys) -
-
-

Turn off encryption when there is a psk.

-
- -Expand source code - -
@pytest.mark.unit
-def test_turnOffEncryptionOnPrimaryChannel(capsys):
-    """Turn off encryption when there is a psk."""
-    anode = Node('foo', 'bar', noProto=True)
-
-    channel1 = Channel(index=1, role=1)
-    channel1.settings.modem_config = 3
-    # value from using "--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b "
-    channel1.settings.psk = b'\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++\x1a\x1a\x1a\x1a++++'
-
-    channel2 = Channel(index=2, role=0)
-    channel3 = Channel(index=3, role=0)
-    channel4 = Channel(index=4, role=0)
-    channel5 = Channel(index=5, role=0)
-    channel6 = Channel(index=6, role=0)
-    channel7 = Channel(index=7, role=0)
-    channel8 = Channel(index=8, role=0)
-
-    channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
-
-    anode.channels = channels
-    anode.turnOffEncryptionOnPrimaryChannel()
-    out, err = capsys.readouterr()
-    assert re.search(r'Writing modified channels to device', out)
-    assert err == ''
-
-
-
-def test_writeConfig(caplog) -
-
-

Test writeConfig

-
- -Expand source code - -
@pytest.mark.unit
-def test_writeConfig(caplog):
-    """Test writeConfig"""
-    anode = Node('foo', 'bar', noProto=True)
-    radioConfig = RadioConfig()
-    anode.radioConfig = radioConfig
-
-    with caplog.at_level(logging.DEBUG):
-        anode.writeConfig()
-    assert re.search(r'Wrote config', caplog.text, re.MULTILINE)
-
-
-
-def test_writeConfig_with_no_radioConfig(capsys) -
-
-

Test writeConfig with no radioConfig.

-
- -Expand source code - -
@pytest.mark.unit
-def test_writeConfig_with_no_radioConfig(capsys):
-    """Test writeConfig with no radioConfig."""
-    anode = Node('foo', 'bar', noProto=True)
-
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        anode.writeConfig()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Error: No RadioConfig has been read', out)
-    assert err == ''
-
-
-
-
-
-

Classes

-
-
-class AdminMessage -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
-

Instance variables

-
-
var confirm_set_channel
-
-

Field AdminMessage.confirm_set_channel

-
-
var confirm_set_radio
-
-

Field AdminMessage.confirm_set_radio

-
-
var exit_simulator
-
-

Field AdminMessage.exit_simulator

-
-
var get_channel_request
-
-

Field AdminMessage.get_channel_request

-
-
var get_channel_response
-
-

Field AdminMessage.get_channel_response

-
-
var get_radio_request
-
-

Field AdminMessage.get_radio_request

-
-
var get_radio_response
-
-

Field AdminMessage.get_radio_response

-
-
var reboot_seconds
-
-

Field AdminMessage.reboot_seconds

-
-
var set_channel
-
-

Field AdminMessage.set_channel

-
-
var set_owner
-
-

Field AdminMessage.set_owner

-
-
var set_radio
-
-

Field AdminMessage.set_radio

-
-
-
-
-class Channel -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var DISABLED
-
-
-
-
var PRIMARY
-
-
-
-
var Role
-
-
-
-
var SECONDARY
-
-
-
-
-

Instance variables

-
-
var index
-
-

Field Channel.index

-
-
var role
-
-

Field Channel.role

-
-
var settings
-
-

Field Channel.settings

-
-
-
-
-class RadioConfig -(*args, **kwargs) -
-
-

A ProtocolMessage

-

Ancestors

-
    -
  • google.protobuf.pyext._message.CMessage
  • -
  • google.protobuf.message.Message
  • -
-

Class variables

-
-
var DESCRIPTOR
-
-
-
-
var UserPreferences
-
-

A ProtocolMessage

-
-
-

Instance variables

-
-
var preferences
-
-

Field RadioConfig.preferences

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_remote_hardware.html b/docs/meshtastic/tests/test_remote_hardware.html deleted file mode 100644 index 031c0cc..0000000 --- a/docs/meshtastic/tests/test_remote_hardware.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - -meshtastic.tests.test_remote_hardware API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_remote_hardware

-
-
-

Meshtastic unit tests for remote_hardware.py

-
- -Expand source code - -
"""Meshtastic unit tests for remote_hardware.py"""
-
-import logging
-import re
-
-from unittest.mock import patch, MagicMock
-import pytest
-
-from ..remote_hardware import RemoteHardwareClient, onGPIOreceive
-from ..serial_interface import SerialInterface
-
-
-@pytest.mark.unit
-def test_RemoteHardwareClient():
-    """Test that we can instantiate a RemoteHardwareClient instance"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    assert rhw.iface == iface
-    iface.close()
-
-
-@pytest.mark.unit
-def test_onGPIOreceive(capsys):
-    """Test onGPIOreceive"""
-    iface = MagicMock(autospec=SerialInterface)
-    packet = {'decoded': {'remotehw': {'typ': 'foo', 'gpioValue': '4096' }}}
-    onGPIOreceive(packet, iface)
-    out, err = capsys.readouterr()
-    assert re.search(r'Received RemoteHardware', out)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_RemoteHardwareClient_no_gpio_channel(capsys):
-    """Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            RemoteHardwareClient(mo)
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Warning: No channel named', out)
-        assert err == ""
-
-
-@pytest.mark.unit
-def test_readGPIOs(caplog):
-    """Test readGPIOs"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    with caplog.at_level(logging.DEBUG):
-        rhw.readGPIOs('0x10', 123)
-    assert re.search(r'readGPIOs', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-@pytest.mark.unit
-def test_writeGPIOs(caplog):
-    """Test writeGPIOs"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    with caplog.at_level(logging.DEBUG):
-        rhw.writeGPIOs('0x10', 123, 1)
-    assert re.search(r'writeGPIOs', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-@pytest.mark.unit
-def test_watchGPIOs(caplog):
-    """Test watchGPIOs"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    with caplog.at_level(logging.DEBUG):
-        rhw.watchGPIOs('0x10', 123)
-    assert re.search(r'watchGPIOs', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-@pytest.mark.unit
-def test_sendHardware_no_nodeid():
-    """Test sending no nodeid to _sendHardware()"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            rhw = RemoteHardwareClient(mo)
-            rhw._sendHardware(None, None)
-        assert pytest_wrapped_e.type == SystemExit
-
-
-
-
-
-
-
-

Functions

-
-
-def test_RemoteHardwareClient() -
-
-

Test that we can instantiate a RemoteHardwareClient instance

-
- -Expand source code - -
@pytest.mark.unit
-def test_RemoteHardwareClient():
-    """Test that we can instantiate a RemoteHardwareClient instance"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    assert rhw.iface == iface
-    iface.close()
-
-
-
-def test_RemoteHardwareClient_no_gpio_channel(capsys) -
-
-

Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'

-
- -Expand source code - -
@pytest.mark.unit
-def test_RemoteHardwareClient_no_gpio_channel(capsys):
-    """Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        mo.localNode.getChannelByName.return_value = None
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            RemoteHardwareClient(mo)
-        assert pytest_wrapped_e.type == SystemExit
-        assert pytest_wrapped_e.value.code == 1
-        out, err = capsys.readouterr()
-        assert re.search(r'Warning: No channel named', out)
-        assert err == ""
-
-
-
-def test_onGPIOreceive(capsys) -
-
-

Test onGPIOreceive

-
- -Expand source code - -
@pytest.mark.unit
-def test_onGPIOreceive(capsys):
-    """Test onGPIOreceive"""
-    iface = MagicMock(autospec=SerialInterface)
-    packet = {'decoded': {'remotehw': {'typ': 'foo', 'gpioValue': '4096' }}}
-    onGPIOreceive(packet, iface)
-    out, err = capsys.readouterr()
-    assert re.search(r'Received RemoteHardware', out)
-    assert err == ''
-
-
-
-def test_readGPIOs(caplog) -
-
-

Test readGPIOs

-
- -Expand source code - -
@pytest.mark.unit
-def test_readGPIOs(caplog):
-    """Test readGPIOs"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    with caplog.at_level(logging.DEBUG):
-        rhw.readGPIOs('0x10', 123)
-    assert re.search(r'readGPIOs', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-
-def test_sendHardware_no_nodeid() -
-
-

Test sending no nodeid to _sendHardware()

-
- -Expand source code - -
@pytest.mark.unit
-def test_sendHardware_no_nodeid():
-    """Test sending no nodeid to _sendHardware()"""
-    iface = MagicMock(autospec=SerialInterface)
-    with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
-        with pytest.raises(SystemExit) as pytest_wrapped_e:
-            rhw = RemoteHardwareClient(mo)
-            rhw._sendHardware(None, None)
-        assert pytest_wrapped_e.type == SystemExit
-
-
-
-def test_watchGPIOs(caplog) -
-
-

Test watchGPIOs

-
- -Expand source code - -
@pytest.mark.unit
-def test_watchGPIOs(caplog):
-    """Test watchGPIOs"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    with caplog.at_level(logging.DEBUG):
-        rhw.watchGPIOs('0x10', 123)
-    assert re.search(r'watchGPIOs', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-
-def test_writeGPIOs(caplog) -
-
-

Test writeGPIOs

-
- -Expand source code - -
@pytest.mark.unit
-def test_writeGPIOs(caplog):
-    """Test writeGPIOs"""
-    iface = MagicMock(autospec=SerialInterface)
-    rhw = RemoteHardwareClient(iface)
-    with caplog.at_level(logging.DEBUG):
-        rhw.writeGPIOs('0x10', 123, 1)
-    assert re.search(r'writeGPIOs', caplog.text, re.MULTILINE)
-    iface.close()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_serial_interface.html b/docs/meshtastic/tests/test_serial_interface.html deleted file mode 100644 index d78c922..0000000 --- a/docs/meshtastic/tests/test_serial_interface.html +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - -meshtastic.tests.test_serial_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_serial_interface

-
-
-

Meshtastic unit tests for serial_interface.py

-
- -Expand source code - -
"""Meshtastic unit tests for serial_interface.py"""
-
-import re
-
-
-from unittest.mock import patch
-import pytest
-
-from ..serial_interface import SerialInterface
-
-@pytest.mark.unit
-@patch('serial.Serial')
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
-def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
-    """Test that we can instantiate a SerialInterface with a single port"""
-    iface = SerialInterface(noProto=True)
-    iface.showInfo()
-    iface.localNode.showInfo()
-    iface.close()
-    mocked_findPorts.assert_called()
-    mocked_serial.assert_called()
-
-
-@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=[])
-def test_SerialInterface_no_ports(mocked_findPorts, capsys):
-    """Test that we can instantiate a SerialInterface with no ports"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        SerialInterface(noProto=True)
-    mocked_findPorts.assert_called()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: No Meshtastic devices detected', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake1', '/dev/ttyUSBfake2'])
-def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
-    """Test that we can instantiate a SerialInterface with two ports"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        SerialInterface(noProto=True)
-    mocked_findPorts.assert_called()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: Multiple serial ports were detected', out, re.MULTILINE)
-    assert err == ''
-
-
-
-
-
-
-
-

Functions

-
-
-def test_SerialInterface_multiple_ports(mocked_findPorts, capsys) -
-
-

Test that we can instantiate a SerialInterface with two ports

-
- -Expand source code - -
@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake1', '/dev/ttyUSBfake2'])
-def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
-    """Test that we can instantiate a SerialInterface with two ports"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        SerialInterface(noProto=True)
-    mocked_findPorts.assert_called()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: Multiple serial ports were detected', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_SerialInterface_no_ports(mocked_findPorts, capsys) -
-
-

Test that we can instantiate a SerialInterface with no ports

-
- -Expand source code - -
@pytest.mark.unit
-@patch('meshtastic.util.findPorts', return_value=[])
-def test_SerialInterface_no_ports(mocked_findPorts, capsys):
-    """Test that we can instantiate a SerialInterface with no ports"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        SerialInterface(noProto=True)
-    mocked_findPorts.assert_called()
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-    out, err = capsys.readouterr()
-    assert re.search(r'Warning: No Meshtastic devices detected', out, re.MULTILINE)
-    assert err == ''
-
-
-
-def test_SerialInterface_single_port(mocked_findPorts, mocked_serial) -
-
-

Test that we can instantiate a SerialInterface with a single port

-
- -Expand source code - -
@pytest.mark.unit
-@patch('serial.Serial')
-@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
-def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
-    """Test that we can instantiate a SerialInterface with a single port"""
-    iface = SerialInterface(noProto=True)
-    iface.showInfo()
-    iface.localNode.showInfo()
-    iface.close()
-    mocked_findPorts.assert_called()
-    mocked_serial.assert_called()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_smoke1.html b/docs/meshtastic/tests/test_smoke1.html deleted file mode 100644 index c694e7a..0000000 --- a/docs/meshtastic/tests/test_smoke1.html +++ /dev/null @@ -1,1822 +0,0 @@ - - - - - - -meshtastic.tests.test_smoke1 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_smoke1

-
-
-

Meshtastic smoke tests with a single device via USB

-
- -Expand source code - -
"""Meshtastic smoke tests with a single device via USB"""
-import re
-import subprocess
-import time
-import os
-
-# Do not like using hard coded sleeps, but it probably makes
-# sense to pause for the radio at apprpriate times
-import pytest
-
-from ..util import findPorts
-
-# seconds to pause after running a meshtastic command
-PAUSE_AFTER_COMMAND = 2
-PAUSE_AFTER_REBOOT = 7
-
-
-@pytest.mark.smoke1
-def test_smoke1_reboot():
-    """Test reboot"""
-    return_value, _ = subprocess.getstatusoutput('meshtastic --reboot')
-    assert return_value == 0
-    # pause for the radio to reset (10 seconds for the pause, and a few more seconds to be back up)
-    time.sleep(18)
-
-
-@pytest.mark.smoke1
-def test_smoke1_info():
-    """Test --info"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^My info', out, re.MULTILINE)
-    assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
-    assert re.search(r'^Preferences', out, re.MULTILINE)
-    assert re.search(r'^Channels', out, re.MULTILINE)
-    assert re.search(r'^  PRIMARY', out, re.MULTILINE)
-    assert re.search(r'^Primary channel URL', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_sendping():
-    """Test --sendping"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --sendping')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Sending ping message', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_get_with_invalid_setting():
-    """Test '--get a_bad_setting'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --get a_bad_setting')
-    assert re.search(r'Choices in sorted order', out)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_set_with_invalid_setting():
-    """Test '--set a_bad_setting'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set a_bad_setting foo')
-    assert re.search(r'Choices in sorted order', out)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_ch_set_with_invalid_settingpatch_find_ports():
-    """Test '--ch-set with a_bad_setting'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set invalid_setting foo --ch-index 0')
-    assert re.search(r'Choices in sorted order', out)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_pos_fields():
-    """Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --pos-fields POS_ALTITUDE POS_ALT_MSL POS_BATTERY')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting position fields to 35', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --pos-fields')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'POS_ALTITUDE', out, re.MULTILINE)
-    assert re.search(r'POS_ALT_MSL', out, re.MULTILINE)
-    assert re.search(r'POS_BATTERY', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_test_with_arg_but_no_hardware():
-    """Test --test
-       Note: Since only one device is connected, it will not do much.
-    """
-    return_value, out = subprocess.getstatusoutput('meshtastic --test')
-    assert re.search(r'^Warning: Must have at least two devices', out, re.MULTILINE)
-    assert return_value == 1
-
-
-@pytest.mark.smoke1
-def test_smoke1_debug():
-    """Test --debug"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info --debug')
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^DEBUG file', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_seriallog_to_file():
-    """Test --seriallog to a file creates a file"""
-    filename = 'tmpoutput.txt'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --info --seriallog {filename}')
-    assert os.path.exists(f"{filename}")
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-@pytest.mark.smoke1
-def test_smoke1_qr():
-    """Test --qr"""
-    filename = 'tmpqr'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --qr > {filename}')
-    assert os.path.exists(f"{filename}")
-    # not really testing that a valid qr code is created, just that the file size
-    # is reasonably big enough for a qr code
-    assert os.stat(f"{filename}").st_size > 20000
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-@pytest.mark.smoke1
-def test_smoke1_nodes():
-    """Test --nodes"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --nodes')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^│   N │ User', out, re.MULTILINE)
-    assert re.search(r'^│   1 │', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_send_hello():
-    """Test --sendtext hello"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --sendtext hello')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Sending text message hello to \^all', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_port():
-    """Test --port"""
-    # first, get the ports
-    ports = findPorts()
-    # hopefully there is just one
-    assert len(ports) == 1
-    port = ports[0]
-    return_value, out = subprocess.getstatusoutput(f'meshtastic --port {port} --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_is_router_true():
-    """Test --set is_router true"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to true', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: True', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_location_info():
-    """Test --setlat, --setlon and --setalt """
-    return_value, out = subprocess.getstatusoutput('meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Fixing altitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing latitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing longitude', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out2 = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'1337', out2, re.MULTILINE)
-    assert re.search(r'32.7767', out2, re.MULTILINE)
-    assert re.search(r'-96.797', out2, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_is_router_false():
-    """Test --set is_router false"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router false')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to false', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: False', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_owner():
-    """Test --set-owner name"""
-    # make sure the owner is not Joe
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Bob')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Bob', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Joe')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_team():
-    """Test --set-team """
-    # unset the team
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CLEAR')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CYAN')
-    assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'CYAN', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_values():
-    """Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
-       --ch-shortslow, and --ch-shortfast arguments
-    """
-    exp = {
-            '--ch-longslow': 'Bw125Cr48Sf4096',
-            '--ch-longfast': 'Bw31_25Cr48Sf512',
-            '--ch-mediumslow': 'Bw250Cr46Sf2048',
-            '--ch-mediumfast': 'Bw250Cr47Sf1024',
-            # for some reason, this value does not show any modemConfig
-            '--ch-shortslow': '{ "psk',
-            '--ch-shortfast': 'Bw500Cr45Sf128'
-          }
-
-    for key, val in exp.items():
-        print(key, val)
-        return_value, out = subprocess.getstatusoutput(f'meshtastic {key}')
-        assert re.match(r'Connected to radio', out)
-        assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio (might reboot)
-        time.sleep(PAUSE_AFTER_REBOOT)
-        return_value, out = subprocess.getstatusoutput('meshtastic --info')
-        assert re.search(val, out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio
-        time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_set_name():
-    """Test --ch-set name"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set name to MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_set_downlink_and_uplink():
-    """Test -ch-set downlink_enabled X and --ch-set uplink_enabled X"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'uplinkEnabled', out, re.MULTILINE)
-    assert not re.search(r'downlinkEnabled', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled true --ch-set uplink_enabled true --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set downlink_enabled to true', out, re.MULTILINE)
-    assert re.search(r'^Set uplink_enabled to true', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'uplinkEnabled', out, re.MULTILINE)
-    assert re.search(r'downlinkEnabled', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_add_and_ch_del():
-    """Test --ch-add"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-index 1 --ch-del')
-    assert re.search(r'Deleting channel 1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    # make sure the secondar channel is not there
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert not re.search(r'SECONDARY', out, re.MULTILINE)
-    assert not re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_enable_and_disable():
-    """Test --ch-enable and --ch-disable"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure they need to specify a --ch-index
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable')
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'DISABLED', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-enable --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_del_a_disabled_non_primary_channel():
-    """Test --ch-del will work on a disabled non-primary channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure they need to specify a --ch-index
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable')
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert not re.search(r'DISABLED', out, re.MULTILINE)
-    assert not re.search(r'SECONDARY', out, re.MULTILINE)
-    assert not re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_attempt_to_delete_primary_channel():
-    """Test that we cannot delete the PRIMARY channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 0')
-    assert re.search(r'Warning: Cannot delete primary channel', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_attempt_to_disable_primary_channel():
-    """Test that we cannot disable the PRIMARY channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable --ch-index 0')
-    assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_attempt_to_enable_primary_channel():
-    """Test that we cannot enable the PRIMARY channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-enable --ch-index 0')
-    assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_ensure_ch_del_second_of_three_channels():
-    """Test that when we delete the 2nd of 3 channels, that it deletes the correct channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing2')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing2', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing2', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_ensure_ch_del_third_of_three_channels():
-    """Test that when we delete the 3rd of 3 channels, that it deletes the correct channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing2')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing2', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 2')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_ch_set_modem_config():
-    """Test --ch-set modem_config"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512')
-    assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512 --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_seturl_default():
-    """Test --seturl with default value"""
-    # set some channel value so we no longer have a default channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name foo --ch-index 0')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure we no longer have a default primary channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-    url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_seturl_invalid_url():
-    """Test --seturl with invalid url"""
-    # Note: This url is no longer a valid url.
-    url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ="
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('Warning: There were no settings', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-@pytest.mark.smoke1
-def test_smoke1_configure():
-    """Test --configure"""
-    _ , out = subprocess.getstatusoutput(f"meshtastic --configure example_config.yaml")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('^Setting device owner to Bob TBeam', out, re.MULTILINE)
-    assert re.search('^Fixing altitude at 304 meters', out, re.MULTILINE)
-    assert re.search('^Fixing latitude at 35.8', out, re.MULTILINE)
-    assert re.search('^Fixing longitude at -93.8', out, re.MULTILINE)
-    assert re.search('^Setting device position', out, re.MULTILINE)
-    assert re.search('^Set region to 1', out, re.MULTILINE)
-    assert re.search('^Set is_always_powered to true', out, re.MULTILINE)
-    assert re.search('^Set send_owner_interval to 2', out, re.MULTILINE)
-    assert re.search('^Set screen_on_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Set wait_bluetooth_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Writing modified preferences to device', out, re.MULTILINE)
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_ham():
-    """Test --set-ham
-       Note: Do a factory reset after this setting so it is very short-lived.
-    """
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-ham KI1234')
-    assert re.search(r'Setting Ham ID', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Owner: KI1234', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_set_wifi_settings():
-    """Test --set wifi_ssid and --set wifi_password"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set wifi_ssid "some_ssid" --set wifi_password "temp1234"')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set wifi_ssid to some_ssid', out, re.MULTILINE)
-    assert re.search(r'^Set wifi_password to temp1234', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get wifi_ssid --get wifi_password')
-    assert re.search(r'^wifi_ssid: some_ssid', out, re.MULTILINE)
-    assert re.search(r'^wifi_password: sekrit', out, re.MULTILINE)
-    assert return_value == 0
-
-
-@pytest.mark.smoke1
-def test_smoke1_factory_reset():
-    """Test factory reset"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set factory_reset true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set factory_reset to true', out, re.MULTILINE)
-    assert re.search(r'^Writing modified preferences to device', out, re.MULTILINE)
-    assert return_value == 0
-    # NOTE: The radio may not be responsive after this, may need to do a manual reboot
-    # by pressing the button
-
-
-
-
-
-
-
-

Functions

-
-
-def test_ch_set_with_invalid_settingpatch_find_ports() -
-
-

Test '–ch-set with a_bad_setting'.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_ch_set_with_invalid_settingpatch_find_ports():
-    """Test '--ch-set with a_bad_setting'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set invalid_setting foo --ch-index 0')
-    assert re.search(r'Choices in sorted order', out)
-    assert return_value == 0
-
-
-
-def test_get_with_invalid_setting() -
-
-

Test '–get a_bad_setting'.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_get_with_invalid_setting():
-    """Test '--get a_bad_setting'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --get a_bad_setting')
-    assert re.search(r'Choices in sorted order', out)
-    assert return_value == 0
-
-
-
-def test_set_with_invalid_setting() -
-
-

Test '–set a_bad_setting'.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_set_with_invalid_setting():
-    """Test '--set a_bad_setting'."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set a_bad_setting foo')
-    assert re.search(r'Choices in sorted order', out)
-    assert return_value == 0
-
-
-
-def test_smoke1_attempt_to_delete_primary_channel() -
-
-

Test that we cannot delete the PRIMARY channel.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_attempt_to_delete_primary_channel():
-    """Test that we cannot delete the PRIMARY channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 0')
-    assert re.search(r'Warning: Cannot delete primary channel', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_attempt_to_disable_primary_channel() -
-
-

Test that we cannot disable the PRIMARY channel.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_attempt_to_disable_primary_channel():
-    """Test that we cannot disable the PRIMARY channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable --ch-index 0')
-    assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_attempt_to_enable_primary_channel() -
-
-

Test that we cannot enable the PRIMARY channel.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_attempt_to_enable_primary_channel():
-    """Test that we cannot enable the PRIMARY channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-enable --ch-index 0')
-    assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_ch_add_and_ch_del() -
-
-

Test –ch-add

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_add_and_ch_del():
-    """Test --ch-add"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-index 1 --ch-del')
-    assert re.search(r'Deleting channel 1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    # make sure the secondar channel is not there
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert not re.search(r'SECONDARY', out, re.MULTILINE)
-    assert not re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ch_del_a_disabled_non_primary_channel() -
-
-

Test –ch-del will work on a disabled non-primary channel.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_del_a_disabled_non_primary_channel():
-    """Test --ch-del will work on a disabled non-primary channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure they need to specify a --ch-index
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable')
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert not re.search(r'DISABLED', out, re.MULTILINE)
-    assert not re.search(r'SECONDARY', out, re.MULTILINE)
-    assert not re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_ch_enable_and_disable() -
-
-

Test –ch-enable and –ch-disable

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_enable_and_disable():
-    """Test --ch-enable and --ch-disable"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
-    assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure they need to specify a --ch-index
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable')
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'DISABLED', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-enable --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
- -
-

Test -ch-set downlink_enabled X and –ch-set uplink_enabled X

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_set_downlink_and_uplink():
-    """Test -ch-set downlink_enabled X and --ch-set uplink_enabled X"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'uplinkEnabled', out, re.MULTILINE)
-    assert not re.search(r'downlinkEnabled', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled true --ch-set uplink_enabled true --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set downlink_enabled to true', out, re.MULTILINE)
-    assert re.search(r'^Set uplink_enabled to true', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'uplinkEnabled', out, re.MULTILINE)
-    assert re.search(r'downlinkEnabled', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ch_set_modem_config() -
-
-

Test –ch-set modem_config

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_set_modem_config():
-    """Test --ch-set modem_config"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512')
-    assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512 --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ch_set_name() -
-
-

Test –ch-set name

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_set_name():
-    """Test --ch-set name"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel --ch-index 0')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set name to MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'MyChannel', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ch_values() -
-
-

Test –ch-longslow, –ch-longfast, –ch-mediumslow, –ch-mediumsfast, -–ch-shortslow, and –ch-shortfast arguments

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ch_values():
-    """Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
-       --ch-shortslow, and --ch-shortfast arguments
-    """
-    exp = {
-            '--ch-longslow': 'Bw125Cr48Sf4096',
-            '--ch-longfast': 'Bw31_25Cr48Sf512',
-            '--ch-mediumslow': 'Bw250Cr46Sf2048',
-            '--ch-mediumfast': 'Bw250Cr47Sf1024',
-            # for some reason, this value does not show any modemConfig
-            '--ch-shortslow': '{ "psk',
-            '--ch-shortfast': 'Bw500Cr45Sf128'
-          }
-
-    for key, val in exp.items():
-        print(key, val)
-        return_value, out = subprocess.getstatusoutput(f'meshtastic {key}')
-        assert re.match(r'Connected to radio', out)
-        assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio (might reboot)
-        time.sleep(PAUSE_AFTER_REBOOT)
-        return_value, out = subprocess.getstatusoutput('meshtastic --info')
-        assert re.search(val, out, re.MULTILINE)
-        assert return_value == 0
-        # pause for the radio
-        time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_configure() -
-
-

Test –configure

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_configure():
-    """Test --configure"""
-    _ , out = subprocess.getstatusoutput(f"meshtastic --configure example_config.yaml")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('^Setting device owner to Bob TBeam', out, re.MULTILINE)
-    assert re.search('^Fixing altitude at 304 meters', out, re.MULTILINE)
-    assert re.search('^Fixing latitude at 35.8', out, re.MULTILINE)
-    assert re.search('^Fixing longitude at -93.8', out, re.MULTILINE)
-    assert re.search('^Setting device position', out, re.MULTILINE)
-    assert re.search('^Set region to 1', out, re.MULTILINE)
-    assert re.search('^Set is_always_powered to true', out, re.MULTILINE)
-    assert re.search('^Set send_owner_interval to 2', out, re.MULTILINE)
-    assert re.search('^Set screen_on_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Set wait_bluetooth_secs to 31536000', out, re.MULTILINE)
-    assert re.search('^Writing modified preferences to device', out, re.MULTILINE)
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-
-
-
-def test_smoke1_debug() -
-
-

Test –debug

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_debug():
-    """Test --debug"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info --debug')
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^DEBUG file', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_ensure_ch_del_second_of_three_channels() -
-
-

Test that when we delete the 2nd of 3 channels, that it deletes the correct channel.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ensure_ch_del_second_of_three_channels():
-    """Test that when we delete the 2nd of 3 channels, that it deletes the correct channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing2')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing2', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing2', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_ensure_ch_del_third_of_three_channels() -
-
-

Test that when we delete the 3rd of 3 channels, that it deletes the correct channel.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_ensure_ch_del_third_of_three_channels():
-    """Test that when we delete the 3rd of 3 channels, that it deletes the correct channel."""
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'SECONDARY', out, re.MULTILINE)
-    assert re.search(r'testing1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing2')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing2', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 2')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'testing1', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 1')
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_factory_reset() -
-
-

Test factory reset

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_factory_reset():
-    """Test factory reset"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set factory_reset true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set factory_reset to true', out, re.MULTILINE)
-    assert re.search(r'^Writing modified preferences to device', out, re.MULTILINE)
-    assert return_value == 0
-    # NOTE: The radio may not be responsive after this, may need to do a manual reboot
-    # by pressing the button
-
-
-
-def test_smoke1_info() -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_info():
-    """Test --info"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^My info', out, re.MULTILINE)
-    assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
-    assert re.search(r'^Preferences', out, re.MULTILINE)
-    assert re.search(r'^Channels', out, re.MULTILINE)
-    assert re.search(r'^  PRIMARY', out, re.MULTILINE)
-    assert re.search(r'^Primary channel URL', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_nodes() -
-
-

Test –nodes

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_nodes():
-    """Test --nodes"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --nodes')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^│   N │ User', out, re.MULTILINE)
-    assert re.search(r'^│   1 │', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_port() -
-
-

Test –port

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_port():
-    """Test --port"""
-    # first, get the ports
-    ports = findPorts()
-    # hopefully there is just one
-    assert len(ports) == 1
-    port = ports[0]
-    return_value, out = subprocess.getstatusoutput(f'meshtastic --port {port} --info')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_pos_fields() -
-
-

Test –pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_pos_fields():
-    """Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --pos-fields POS_ALTITUDE POS_ALT_MSL POS_BATTERY')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting position fields to 35', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --pos-fields')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'POS_ALTITUDE', out, re.MULTILINE)
-    assert re.search(r'POS_ALT_MSL', out, re.MULTILINE)
-    assert re.search(r'POS_BATTERY', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_qr() -
-
-

Test –qr

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_qr():
-    """Test --qr"""
-    filename = 'tmpqr'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --qr > {filename}')
-    assert os.path.exists(f"{filename}")
-    # not really testing that a valid qr code is created, just that the file size
-    # is reasonably big enough for a qr code
-    assert os.stat(f"{filename}").st_size > 20000
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-
-def test_smoke1_reboot() -
-
-

Test reboot

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_reboot():
-    """Test reboot"""
-    return_value, _ = subprocess.getstatusoutput('meshtastic --reboot')
-    assert return_value == 0
-    # pause for the radio to reset (10 seconds for the pause, and a few more seconds to be back up)
-    time.sleep(18)
-
-
-
-def test_smoke1_send_hello() -
-
-

Test –sendtext hello

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_send_hello():
-    """Test --sendtext hello"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --sendtext hello')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Sending text message hello to \^all', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_sendping() -
-
-

Test –sendping

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_sendping():
-    """Test --sendping"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --sendping')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Sending ping message', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_seriallog_to_file() -
-
-

Test –seriallog to a file creates a file

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_seriallog_to_file():
-    """Test --seriallog to a file creates a file"""
-    filename = 'tmpoutput.txt'
-    if os.path.exists(f"{filename}"):
-        os.remove(f"{filename}")
-    return_value, _ = subprocess.getstatusoutput(f'meshtastic --info --seriallog {filename}')
-    assert os.path.exists(f"{filename}")
-    assert return_value == 0
-    os.remove(f"{filename}")
-
-
-
-def test_smoke1_set_ham() -
-
-

Test –set-ham -Note: Do a factory reset after this setting so it is very short-lived.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_ham():
-    """Test --set-ham
-       Note: Do a factory reset after this setting so it is very short-lived.
-    """
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-ham KI1234')
-    assert re.search(r'Setting Ham ID', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Owner: KI1234', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_is_router_false() -
-
-

Test –set is_router false

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_is_router_false():
-    """Test --set is_router false"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router false')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to false', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: False', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_is_router_true() -
-
-

Test –set is_router true

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_is_router_true():
-    """Test --set is_router true"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set is_router true')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set is_router to true', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
-    assert re.search(r'^is_router: True', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_location_info() -
-
-

Test –setlat, –setlon and –setalt

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_location_info():
-    """Test --setlat, --setlon and --setalt """
-    return_value, out = subprocess.getstatusoutput('meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Fixing altitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing latitude', out, re.MULTILINE)
-    assert re.search(r'^Fixing longitude', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out2 = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'1337', out2, re.MULTILINE)
-    assert re.search(r'32.7767', out2, re.MULTILINE)
-    assert re.search(r'-96.797', out2, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_owner() -
-
-

Test –set-owner name

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_owner():
-    """Test --set-owner name"""
-    # make sure the owner is not Joe
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Bob')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Bob', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Joe')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting device owner to Joe', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Owner: Joe', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_team() -
-
-

Test –set-team

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_team():
-    """Test --set-team """
-    # unset the team
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CLEAR')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --set-team CYAN')
-    assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_REBOOT)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'CYAN', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_set_wifi_settings() -
-
-

Test –set wifi_ssid and –set wifi_password

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_set_wifi_settings():
-    """Test --set wifi_ssid and --set wifi_password"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --set wifi_ssid "some_ssid" --set wifi_password "temp1234"')
-    assert re.match(r'Connected to radio', out)
-    assert re.search(r'^Set wifi_ssid to some_ssid', out, re.MULTILINE)
-    assert re.search(r'^Set wifi_password to temp1234', out, re.MULTILINE)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --get wifi_ssid --get wifi_password')
-    assert re.search(r'^wifi_ssid: some_ssid', out, re.MULTILINE)
-    assert re.search(r'^wifi_password: sekrit', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_seturl_default() -
-
-

Test –seturl with default value

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_seturl_default():
-    """Test --seturl with default value"""
-    # set some channel value so we no longer have a default channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name foo --ch-index 0')
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    # ensure we no longer have a default primary channel
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert not re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-    url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert return_value == 0
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search('CgUYAyIBAQ', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-def test_smoke1_seturl_invalid_url() -
-
-

Test –seturl with invalid url

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_seturl_invalid_url():
-    """Test --seturl with invalid url"""
-    # Note: This url is no longer a valid url.
-    url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ="
-    return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
-    assert re.match(r'Connected to radio', out)
-    assert re.search('Warning: There were no settings', out, re.MULTILINE)
-    assert return_value == 1
-    # pause for the radio
-    time.sleep(PAUSE_AFTER_COMMAND)
-
-
-
-def test_smoke1_test_with_arg_but_no_hardware() -
-
-

Test –test -Note: Since only one device is connected, it will not do much.

-
- -Expand source code - -
@pytest.mark.smoke1
-def test_smoke1_test_with_arg_but_no_hardware():
-    """Test --test
-       Note: Since only one device is connected, it will not do much.
-    """
-    return_value, out = subprocess.getstatusoutput('meshtastic --test')
-    assert re.search(r'^Warning: Must have at least two devices', out, re.MULTILINE)
-    assert return_value == 1
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_smoke2.html b/docs/meshtastic/tests/test_smoke2.html deleted file mode 100644 index dc5ea44..0000000 --- a/docs/meshtastic/tests/test_smoke2.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - -meshtastic.tests.test_smoke2 API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_smoke2

-
-
-

Meshtastic smoke tests with 2 devices connected via USB

-
- -Expand source code - -
"""Meshtastic smoke tests with 2 devices connected via USB"""
-import re
-import subprocess
-
-import pytest
-
-
-@pytest.mark.smoke2
-def test_smoke2_info():
-    """Test --info with 2 devices connected serially"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Warning: Multiple', out, re.MULTILINE)
-    assert return_value == 1
-
-
-@pytest.mark.smoke2
-def test_smoke2_test():
-    """Test --test"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --test')
-    assert re.search(r'Writing serial debugging', out, re.MULTILINE)
-    assert re.search(r'Ports opened', out, re.MULTILINE)
-    assert re.search(r'Running 5 tests', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-
-
-
-
-

Functions

-
-
-def test_smoke2_info() -
-
-

Test –info with 2 devices connected serially

-
- -Expand source code - -
@pytest.mark.smoke2
-def test_smoke2_info():
-    """Test --info with 2 devices connected serially"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info')
-    assert re.search(r'Warning: Multiple', out, re.MULTILINE)
-    assert return_value == 1
-
-
-
-def test_smoke2_test() -
-
-

Test –test

-
- -Expand source code - -
@pytest.mark.smoke2
-def test_smoke2_test():
-    """Test --test"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --test')
-    assert re.search(r'Writing serial debugging', out, re.MULTILINE)
-    assert re.search(r'Ports opened', out, re.MULTILINE)
-    assert re.search(r'Running 5 tests', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_smoke_wifi.html b/docs/meshtastic/tests/test_smoke_wifi.html deleted file mode 100644 index 1bf6bc2..0000000 --- a/docs/meshtastic/tests/test_smoke_wifi.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - -meshtastic.tests.test_smoke_wifi API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_smoke_wifi

-
-
-

Meshtastic smoke tests a device setup with wifi.

-

Need to have run the following on an esp32 device: -meshtastic –set wifi_ssid 'foo' –set wifi_password 'sekret'

-
- -Expand source code - -
"""Meshtastic smoke tests a device setup with wifi.
-
-   Need to have run the following on an esp32 device:
-      meshtastic --set wifi_ssid 'foo' --set wifi_password 'sekret'
-"""
-import re
-import subprocess
-
-import pytest
-
-
-@pytest.mark.smokewifi
-def test_smokewifi_info():
-    """Test --info"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info --host meshtastic.local')
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^My info', out, re.MULTILINE)
-    assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
-    assert re.search(r'^Preferences', out, re.MULTILINE)
-    assert re.search(r'^Channels', out, re.MULTILINE)
-    assert re.search(r'^  PRIMARY', out, re.MULTILINE)
-    assert re.search(r'^Primary channel URL', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-
-
-
-
-

Functions

-
-
-def test_smokewifi_info() -
-
-

Test –info

-
- -Expand source code - -
@pytest.mark.smokewifi
-def test_smokewifi_info():
-    """Test --info"""
-    return_value, out = subprocess.getstatusoutput('meshtastic --info --host meshtastic.local')
-    assert re.search(r'^Owner', out, re.MULTILINE)
-    assert re.search(r'^My info', out, re.MULTILINE)
-    assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
-    assert re.search(r'^Preferences', out, re.MULTILINE)
-    assert re.search(r'^Channels', out, re.MULTILINE)
-    assert re.search(r'^  PRIMARY', out, re.MULTILINE)
-    assert re.search(r'^Primary channel URL', out, re.MULTILINE)
-    assert return_value == 0
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_stream_interface.html b/docs/meshtastic/tests/test_stream_interface.html deleted file mode 100644 index 4502208..0000000 --- a/docs/meshtastic/tests/test_stream_interface.html +++ /dev/null @@ -1,246 +0,0 @@ - - - - - - -meshtastic.tests.test_stream_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_stream_interface

-
-
-

Meshtastic unit tests for stream_interface.py

-
- -Expand source code - -
"""Meshtastic unit tests for stream_interface.py"""
-
-import logging
-import re
-
-from unittest.mock import MagicMock
-import pytest
-
-from ..stream_interface import StreamInterface
-
-
-@pytest.mark.unit
-def test_StreamInterface():
-    """Test that we cannot instantiate a StreamInterface based on noProto"""
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        StreamInterface()
-    assert pytest_wrapped_e.type == Exception
-
-
-# Note: This takes a bit, so moving from unit to slow
-@pytest.mark.unitslow
-def test_StreamInterface_with_noProto(caplog, reset_globals):
-    """Test that we can instantiate a StreamInterface based on nonProto
-       and we can read/write bytes from a mocked stream
-    """
-    stream = MagicMock()
-    test_data = b'hello'
-    stream.read.return_value = test_data
-    with caplog.at_level(logging.DEBUG):
-        iface = StreamInterface(noProto=True, connectNow=False)
-        iface.stream = stream
-        iface._writeBytes(test_data)
-        data = iface._readBytes(len(test_data))
-        assert data == test_data
-
-
-# Note: This takes a bit, so moving from unit to slow
-# Tip: If you want to see the print output, run with '-s' flag:
-#      pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
-@pytest.mark.unitslow
-def test_sendToRadioImpl(caplog, reset_globals):
-    """Test _sendToRadioImpl()"""
-
-#    def add_header(b):
-#        """Add header stuffs for radio"""
-#        bufLen = len(b)
-#        header = bytes([START1, START2, (bufLen >> 8) & 0xff,  bufLen & 0xff])
-#        return header + b
-
-    # captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
-    raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
-    raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
-    # pylint: disable=C0301
-    raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
-    raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
-    # pylint: disable=C0301
-    raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
-    # pylint: disable=C0301
-    raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
-    # pylint: disable=C0301
-    raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
-    raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
-    raw_blank = b''
-
-    test_data = b'hello'
-    stream = MagicMock()
-    #stream.read.return_value = add_header(test_data)
-    stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
-                                raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
-                                raw_blank, raw_blank]
-    toRadio = MagicMock()
-    toRadio.SerializeToString.return_value = test_data
-    with caplog.at_level(logging.DEBUG):
-        iface = StreamInterface(noProto=True, connectNow=False)
-        iface.stream = stream
-        iface.connect()
-        iface._sendToRadioImpl(toRadio)
-        assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
-        assert re.search(r'reading character', caplog.text, re.MULTILINE)
-        assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
-        print(caplog.text)
-
-
-
-
-
-
-
-

Functions

-
-
-def test_StreamInterface() -
-
-

Test that we cannot instantiate a StreamInterface based on noProto

-
- -Expand source code - -
@pytest.mark.unit
-def test_StreamInterface():
-    """Test that we cannot instantiate a StreamInterface based on noProto"""
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        StreamInterface()
-    assert pytest_wrapped_e.type == Exception
-
-
-
-def test_StreamInterface_with_noProto(caplog, reset_globals) -
-
-

Test that we can instantiate a StreamInterface based on nonProto -and we can read/write bytes from a mocked stream

-
- -Expand source code - -
@pytest.mark.unitslow
-def test_StreamInterface_with_noProto(caplog, reset_globals):
-    """Test that we can instantiate a StreamInterface based on nonProto
-       and we can read/write bytes from a mocked stream
-    """
-    stream = MagicMock()
-    test_data = b'hello'
-    stream.read.return_value = test_data
-    with caplog.at_level(logging.DEBUG):
-        iface = StreamInterface(noProto=True, connectNow=False)
-        iface.stream = stream
-        iface._writeBytes(test_data)
-        data = iface._readBytes(len(test_data))
-        assert data == test_data
-
-
-
-def test_sendToRadioImpl(caplog, reset_globals) -
-
-

Test _sendToRadioImpl()

-
- -Expand source code - -
@pytest.mark.unitslow
-def test_sendToRadioImpl(caplog, reset_globals):
-    """Test _sendToRadioImpl()"""
-
-#    def add_header(b):
-#        """Add header stuffs for radio"""
-#        bufLen = len(b)
-#        header = bytes([START1, START2, (bufLen >> 8) & 0xff,  bufLen & 0xff])
-#        return header + b
-
-    # captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
-    raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
-    raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
-    # pylint: disable=C0301
-    raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
-    raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
-    # pylint: disable=C0301
-    raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
-    # pylint: disable=C0301
-    raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
-    # pylint: disable=C0301
-    raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
-    raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
-    raw_blank = b''
-
-    test_data = b'hello'
-    stream = MagicMock()
-    #stream.read.return_value = add_header(test_data)
-    stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
-                                raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
-                                raw_blank, raw_blank]
-    toRadio = MagicMock()
-    toRadio.SerializeToString.return_value = test_data
-    with caplog.at_level(logging.DEBUG):
-        iface = StreamInterface(noProto=True, connectNow=False)
-        iface.stream = stream
-        iface.connect()
-        iface._sendToRadioImpl(toRadio)
-        assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
-        assert re.search(r'reading character', caplog.text, re.MULTILINE)
-        assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
-        print(caplog.text)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_tcp_interface.html b/docs/meshtastic/tests/test_tcp_interface.html deleted file mode 100644 index 2b86418..0000000 --- a/docs/meshtastic/tests/test_tcp_interface.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - -meshtastic.tests.test_tcp_interface API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_tcp_interface

-
-
-

Meshtastic unit tests for tcp_interface.py

-
- -Expand source code - -
"""Meshtastic unit tests for tcp_interface.py"""
-
-import re
-
-from unittest.mock import patch
-import pytest
-
-from ..tcp_interface import TCPInterface
-
-
-@pytest.mark.unit
-def test_TCPInterface(capsys):
-    """Test that we can instantiate a TCPInterface"""
-    with patch('socket.socket') as mock_socket:
-        iface = TCPInterface(hostname='localhost', noProto=True)
-        iface.showInfo()
-        iface.localNode.showInfo()
-        out, err = capsys.readouterr()
-        assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
-        assert re.search(r'Nodes', out, re.MULTILINE)
-        assert re.search(r'Preferences', out, re.MULTILINE)
-        assert re.search(r'Channels', out, re.MULTILINE)
-        assert re.search(r'Primary channel URL', out, re.MULTILINE)
-        assert err == ''
-        assert mock_socket.called
-        iface.close()
-
-
-
-
-
-
-
-

Functions

-
-
-def test_TCPInterface(capsys) -
-
-

Test that we can instantiate a TCPInterface

-
- -Expand source code - -
@pytest.mark.unit
-def test_TCPInterface(capsys):
-    """Test that we can instantiate a TCPInterface"""
-    with patch('socket.socket') as mock_socket:
-        iface = TCPInterface(hostname='localhost', noProto=True)
-        iface.showInfo()
-        iface.localNode.showInfo()
-        out, err = capsys.readouterr()
-        assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
-        assert re.search(r'Nodes', out, re.MULTILINE)
-        assert re.search(r'Preferences', out, re.MULTILINE)
-        assert re.search(r'Channels', out, re.MULTILINE)
-        assert re.search(r'Primary channel URL', out, re.MULTILINE)
-        assert err == ''
-        assert mock_socket.called
-        iface.close()
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tests/test_util.html b/docs/meshtastic/tests/test_util.html deleted file mode 100644 index 543b891..0000000 --- a/docs/meshtastic/tests/test_util.html +++ /dev/null @@ -1,519 +0,0 @@ - - - - - - -meshtastic.tests.test_util API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tests.test_util

-
-
-

Meshtastic unit tests for util.py

-
- -Expand source code - -
"""Meshtastic unit tests for util.py"""
-
-import re
-import logging
-
-import pytest
-
-from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
-                             support_info, genPSK256, fromStr, fromPSK,
-                             quoteBooleans, catchAndIgnore)
-
-
-@pytest.mark.unit
-def test_genPSK256():
-    """Test genPSK256"""
-    assert genPSK256() != ''
-
-
-@pytest.mark.unit
-def test_fromStr():
-    """Test fromStr"""
-    assert fromStr('') == b''
-    assert fromStr('0x12') == b'\x12'
-    assert fromStr('t')
-    assert fromStr('T')
-    assert fromStr('true')
-    assert fromStr('True')
-    assert fromStr('yes')
-    assert fromStr('Yes')
-    assert fromStr('f') is False
-    assert fromStr('F') is False
-    assert fromStr('false') is False
-    assert fromStr('False') is False
-    assert fromStr('no') is False
-    assert fromStr('No') is False
-    assert fromStr('100.01') == 100.01
-    assert fromStr('123') == 123
-    assert fromStr('abc') == 'abc'
-
-
-@pytest.mark.unit
-def test_quoteBooleans():
-    """Test quoteBooleans"""
-    assert quoteBooleans('') == ''
-    assert quoteBooleans('foo') == 'foo'
-    assert quoteBooleans('true') == 'true'
-    assert quoteBooleans('false') == 'false'
-    assert quoteBooleans(': true') == ": 'true'"
-    assert quoteBooleans(': false') == ": 'false'"
-
-@pytest.mark.unit
-def test_fromPSK():
-    """Test fromPSK"""
-    assert fromPSK('random') != ''
-    assert fromPSK('none') == b'\x00'
-    assert fromPSK('default') == b'\x01'
-    assert fromPSK('simple22') == b'\x17'
-    assert fromPSK('trash') == 'trash'
-
-
-@pytest.mark.unit
-def test_stripnl():
-    """Test stripnl"""
-    assert stripnl('') == ''
-    assert stripnl('a\n') == 'a'
-    assert stripnl(' a \n ') == 'a'
-    assert stripnl('a\nb') == 'a b'
-
-
-@pytest.mark.unit
-def test_pskToString_empty_string():
-    """Test pskToString empty string"""
-    assert pskToString('') == 'unencrypted'
-
-
-@pytest.mark.unit
-def test_pskToString_string():
-    """Test pskToString string"""
-    assert pskToString('hunter123') == 'secret'
-
-
-@pytest.mark.unit
-def test_pskToString_one_byte_zero_value():
-    """Test pskToString one byte that is value of 0"""
-    assert pskToString(bytes([0x00])) == 'unencrypted'
-
-
-@pytest.mark.unit
-def test_pskToString_one_byte_non_zero_value():
-    """Test pskToString one byte that is non-zero"""
-    assert pskToString(bytes([0x01])) == 'default'
-
-
-@pytest.mark.unit
-def test_pskToString_many_bytes():
-    """Test pskToString many bytes"""
-    assert pskToString(bytes([0x02, 0x01])) == 'secret'
-
-
-@pytest.mark.unit
-def test_pskToString_simple():
-    """Test pskToString simple"""
-    assert pskToString(bytes([0x03])) == 'simple2'
-
-
-@pytest.mark.unit
-def test_our_exit_zero_return_value():
-    """Test our_exit with a zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Warning: Some message", 0)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-
-
-@pytest.mark.unit
-def test_our_exit_non_zero_return_value():
-    """Test our_exit with a non-zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Error: Some message", 1)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-@pytest.mark.unit
-def test_fixme():
-    """Test fixme()"""
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        fixme("some exception")
-    assert pytest_wrapped_e.type == Exception
-
-
-@pytest.mark.unit
-def test_support_info(capsys):
-    """Test support_info"""
-    support_info()
-    out, err = capsys.readouterr()
-    assert re.search(r'System', out, re.MULTILINE)
-    assert re.search(r'Platform', out, re.MULTILINE)
-    assert re.search(r'Machine', out, re.MULTILINE)
-    assert re.search(r'Executable', out, re.MULTILINE)
-    assert err == ''
-
-
-@pytest.mark.unit
-def test_catchAndIgnore(caplog):
-    """Test catchAndIgnore() does not actually throw an exception, but just logs"""
-    def some_closure():
-        raise Exception('foo')
-    with caplog.at_level(logging.DEBUG):
-        catchAndIgnore("something", some_closure)
-    assert re.search(r'Exception thrown in something', caplog.text, re.MULTILINE)
-
-
-
-
-
-
-
-

Functions

-
-
-def test_catchAndIgnore(caplog) -
-
-

Test catchAndIgnore() does not actually throw an exception, but just logs

-
- -Expand source code - -
@pytest.mark.unit
-def test_catchAndIgnore(caplog):
-    """Test catchAndIgnore() does not actually throw an exception, but just logs"""
-    def some_closure():
-        raise Exception('foo')
-    with caplog.at_level(logging.DEBUG):
-        catchAndIgnore("something", some_closure)
-    assert re.search(r'Exception thrown in something', caplog.text, re.MULTILINE)
-
-
-
-def test_fixme() -
-
-

Test fixme()

-
- -Expand source code - -
@pytest.mark.unit
-def test_fixme():
-    """Test fixme()"""
-    with pytest.raises(Exception) as pytest_wrapped_e:
-        fixme("some exception")
-    assert pytest_wrapped_e.type == Exception
-
-
-
-def test_fromPSK() -
-
-

Test fromPSK

-
- -Expand source code - -
@pytest.mark.unit
-def test_fromPSK():
-    """Test fromPSK"""
-    assert fromPSK('random') != ''
-    assert fromPSK('none') == b'\x00'
-    assert fromPSK('default') == b'\x01'
-    assert fromPSK('simple22') == b'\x17'
-    assert fromPSK('trash') == 'trash'
-
-
-
-def test_fromStr() -
-
-

Test fromStr

-
- -Expand source code - -
@pytest.mark.unit
-def test_fromStr():
-    """Test fromStr"""
-    assert fromStr('') == b''
-    assert fromStr('0x12') == b'\x12'
-    assert fromStr('t')
-    assert fromStr('T')
-    assert fromStr('true')
-    assert fromStr('True')
-    assert fromStr('yes')
-    assert fromStr('Yes')
-    assert fromStr('f') is False
-    assert fromStr('F') is False
-    assert fromStr('false') is False
-    assert fromStr('False') is False
-    assert fromStr('no') is False
-    assert fromStr('No') is False
-    assert fromStr('100.01') == 100.01
-    assert fromStr('123') == 123
-    assert fromStr('abc') == 'abc'
-
-
-
-def test_genPSK256() -
-
-

Test genPSK256

-
- -Expand source code - -
@pytest.mark.unit
-def test_genPSK256():
-    """Test genPSK256"""
-    assert genPSK256() != ''
-
-
-
-def test_our_exit_non_zero_return_value() -
-
-

Test our_exit with a non-zero return value

-
- -Expand source code - -
@pytest.mark.unit
-def test_our_exit_non_zero_return_value():
-    """Test our_exit with a non-zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Error: Some message", 1)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 1
-
-
-
-def test_our_exit_zero_return_value() -
-
-

Test our_exit with a zero return value

-
- -Expand source code - -
@pytest.mark.unit
-def test_our_exit_zero_return_value():
-    """Test our_exit with a zero return value"""
-    with pytest.raises(SystemExit) as pytest_wrapped_e:
-        our_exit("Warning: Some message", 0)
-    assert pytest_wrapped_e.type == SystemExit
-    assert pytest_wrapped_e.value.code == 0
-
-
-
-def test_pskToString_empty_string() -
-
-

Test pskToString empty string

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_empty_string():
-    """Test pskToString empty string"""
-    assert pskToString('') == 'unencrypted'
-
-
-
-def test_pskToString_many_bytes() -
-
-

Test pskToString many bytes

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_many_bytes():
-    """Test pskToString many bytes"""
-    assert pskToString(bytes([0x02, 0x01])) == 'secret'
-
-
-
-def test_pskToString_one_byte_non_zero_value() -
-
-

Test pskToString one byte that is non-zero

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_one_byte_non_zero_value():
-    """Test pskToString one byte that is non-zero"""
-    assert pskToString(bytes([0x01])) == 'default'
-
-
-
-def test_pskToString_one_byte_zero_value() -
-
-

Test pskToString one byte that is value of 0

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_one_byte_zero_value():
-    """Test pskToString one byte that is value of 0"""
-    assert pskToString(bytes([0x00])) == 'unencrypted'
-
-
-
-def test_pskToString_simple() -
-
-

Test pskToString simple

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_simple():
-    """Test pskToString simple"""
-    assert pskToString(bytes([0x03])) == 'simple2'
-
-
-
-def test_pskToString_string() -
-
-

Test pskToString string

-
- -Expand source code - -
@pytest.mark.unit
-def test_pskToString_string():
-    """Test pskToString string"""
-    assert pskToString('hunter123') == 'secret'
-
-
-
-def test_quoteBooleans() -
-
-

Test quoteBooleans

-
- -Expand source code - -
@pytest.mark.unit
-def test_quoteBooleans():
-    """Test quoteBooleans"""
-    assert quoteBooleans('') == ''
-    assert quoteBooleans('foo') == 'foo'
-    assert quoteBooleans('true') == 'true'
-    assert quoteBooleans('false') == 'false'
-    assert quoteBooleans(': true') == ": 'true'"
-    assert quoteBooleans(': false') == ": 'false'"
-
-
-
-def test_stripnl() -
-
-

Test stripnl

-
- -Expand source code - -
@pytest.mark.unit
-def test_stripnl():
-    """Test stripnl"""
-    assert stripnl('') == ''
-    assert stripnl('a\n') == 'a'
-    assert stripnl(' a \n ') == 'a'
-    assert stripnl('a\nb') == 'a b'
-
-
-
-def test_support_info(capsys) -
-
-

Test support_info

-
- -Expand source code - -
@pytest.mark.unit
-def test_support_info(capsys):
-    """Test support_info"""
-    support_info()
-    out, err = capsys.readouterr()
-    assert re.search(r'System', out, re.MULTILINE)
-    assert re.search(r'Platform', out, re.MULTILINE)
-    assert re.search(r'Machine', out, re.MULTILINE)
-    assert re.search(r'Executable', out, re.MULTILINE)
-    assert err == ''
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/tunnel.html b/docs/meshtastic/tunnel.html deleted file mode 100644 index 42127e5..0000000 --- a/docs/meshtastic/tunnel.html +++ /dev/null @@ -1,731 +0,0 @@ - - - - - - -meshtastic.tunnel API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.tunnel

-
-
-

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

-
- -Expand source code - -
"""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
-"""
-
-import logging
-import threading
-from pubsub import pub
-
-from pytap2 import TapDevice
-
-from . import portnums_pb2
-
-# 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
-        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):
-        """onReceive"""
-        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]
-            # pylint: disable=line-too-long
-            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):
-        """Close"""
-        self.tun.close()
-
-
-
-
-
-

Global variables

-
-
var tcpBlacklist
-
-

A list of protocols we ignore

-
-
var tunnelInstance
-
-

A list of chatty UDP services we should never accidentally -forward to our slow network

-
-
var udpBlacklist
-
-

A list of TCP services to block

-
-
-
-
-

Functions

-
-
-def hexstr(barray) -
-
-

Print a string of hex digits

-
- -Expand source code - -
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

-
- -Expand source code - -
def ipstr(barray):
-    """Print a string of ip digits"""
-    return ".".join('{}'.format(x) for x in barray)
-
-
-
-def onTunnelReceive(packet, interface) -
-
-

Callback for received tunneled messages from mesh

-

FIXME figure out how to do closures with methods in python

-
- -Expand source code - -
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)
-
-
-
-def readnet_u16(p, offset) -
-
-

Read big endian u16 (network byte order)

-
- -Expand source code - -
def readnet_u16(p, offset):
-    """Read big endian u16 (network byte order)"""
-    return p[offset] * 256 + p[offset + 1]
-
-
-
-
-
-

Classes

-
-
-class Tunnel -(iface, subnet=None, netmask='255.255.0.0') -
-
-

A TUN based IP tunnel over meshtastic

-

Constructor

-

iface is the already open MeshInterface instance -subnet is used to construct our network number (normally 10.115.x.x)

-
- -Expand source code - -
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
-        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):
-        """onReceive"""
-        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]
-            # pylint: disable=line-too-long
-            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):
-        """Close"""
-        self.tun.close()
-
-

Methods

-
-
-def _Tunnel__tunReader(self) -
-
-
-
- -Expand source code - -
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) -
-
-
-
- -Expand source code - -
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) -
-
-
-
- -Expand source code - -
def _nodeNumToIp(self, nodeNum):
-    return f"{self.subnetPrefix}.{(nodeNum >> 8) & 0xff}.{nodeNum & 0xff}"
-
-
-
-def _shouldFilterPacket(self, p) -
-
-

Given a packet, decode it and return true if it should be ignored

-
- -Expand source code - -
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]
-        # pylint: disable=line-too-long
-        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 close(self) -
-
-

Close

-
- -Expand source code - -
def close(self):
-    """Close"""
-    self.tun.close()
-
-
-
-def onReceive(self, packet) -
-
-

onReceive

-
- -Expand source code - -
def onReceive(self, packet):
-    """onReceive"""
-    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 sendPacket(self, destAddr, p) -
-
-

Forward the provided IP packet into the mesh

-
- -Expand source code - -
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)}")
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/meshtastic/util.html b/docs/meshtastic/util.html deleted file mode 100644 index c41cd8a..0000000 --- a/docs/meshtastic/util.html +++ /dev/null @@ -1,709 +0,0 @@ - - - - - - -meshtastic.util API documentation - - - - - - - - - - - -
-
-
-

Module meshtastic.util

-
-
-

Utility functions.

-
- -Expand source code - -
"""Utility functions.
-"""
-import traceback
-from queue import Queue
-import os
-import sys
-import time
-import platform
-import logging
-import threading
-import serial
-import serial.tools.list_ports
-import pkg_resources
-
-"""Some devices such as a seger jlink we never want to accidentally open"""
-blacklistVids = dict.fromkeys([0x1366])
-
-
-def quoteBooleans(a_string):
-    """Quote booleans
-        given a string that contains ": true", replace with ": 'true'" (or false)
-    """
-    tmp = a_string.replace(": true", ": 'true'")
-    tmp = tmp.replace(": false", ": 'false'")
-    return tmp
-
-def genPSK256():
-    """Generate a random preshared key"""
-    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.lower() in {"t", "true", "yes"}:
-        val = True
-    elif valstr.lower() in {"f", "false", "no"}:
-        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
-
-
-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"
-
-
-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 an exception for things that needs to be fixed"""
-    raise Exception(f"FIXME: {message}")
-
-
-def catchAndIgnore(reason, closure):
-    """Call a closure but if it throws an exception 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 is not 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:
-    """Timeout class"""
-    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):
-        """ Queue up the work"""
-        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())
-
-
-def our_exit(message, return_value = 1):
-    """Print the message and return a value.
-       return_value defaults to 1 (non-successful)
-    """
-    print(message)
-    sys.exit(return_value)
-
-
-def support_info():
-    """Print out info that helps troubleshooting of the cli."""
-    print('')
-    print('If having issues with meshtastic cli or python library')
-    print('or wish to make feature requests, visit:')
-    print('https://github.com/meshtastic/Meshtastic-python/issues')
-    print('When adding an issue, be sure to include the following info:')
-    print(' System: {0}'.format(platform.system()))
-    print('   Platform: {0}'.format(platform.platform()))
-    print('   Release: {0}'.format(platform.uname().release))
-    print('   Machine: {0}'.format(platform.uname().machine))
-    print('   Encoding (stdin): {0}'.format(sys.stdin.encoding))
-    print('   Encoding (stdout): {0}'.format(sys.stdout.encoding))
-    print(' meshtastic: v{0}'.format(pkg_resources.require('meshtastic')[0].version))
-    print(' Executable: {0}'.format(sys.argv[0]))
-    print(' Python: {0} {1} {2}'.format(platform.python_version(),
-          platform.python_implementation(), platform.python_compiler()))
-    print('')
-    print('Please add the output from the command: meshtastic --info')
-
-
-
-
-
-
-
-

Functions

-
-
-def catchAndIgnore(reason, closure) -
-
-

Call a closure but if it throws an exception print it and continue

-
- -Expand source code - -
def catchAndIgnore(reason, closure):
-    """Call a closure but if it throws an exception 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

-
- -Expand source code - -
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 is not None and port.vid not in blacklistVids,
-                        serial.tools.list_ports.comports())))
-    l.sort()
-    return l
-
-
-
-def fixme(message) -
-
-

Raise an exception for things that needs to be fixed

-
- -Expand source code - -
def fixme(message):
-    """Raise an exception for things that needs to be fixed"""
-    raise Exception(f"FIXME: {message}")
-
-
-
-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

-
- -Expand source code - -
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
-
-
- -Expand source code - -
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.lower() in {"t", "true", "yes"}:
-        val = True
-    elif valstr.lower() in {"f", "false", "no"}:
-        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
-
-
-
-def genPSK256() -
-
-

Generate a random preshared key

-
- -Expand source code - -
def genPSK256():
-    """Generate a random preshared key"""
-    return os.urandom(32)
-
-
-
-def our_exit(message, return_value=1) -
-
-

Print the message and return a value. -return_value defaults to 1 (non-successful)

-
- -Expand source code - -
def our_exit(message, return_value = 1):
-    """Print the message and return a value.
-       return_value defaults to 1 (non-successful)
-    """
-    print(message)
-    sys.exit(return_value)
-
-
-
-def pskToString(psk: bytes) -
-
-

Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string

-
- -Expand source code - -
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"
-
-
-
-def quoteBooleans(a_string) -
-
-

Quote booleans -given a string that contains ": true", replace with ": 'true'" (or false)

-
- -Expand source code - -
def quoteBooleans(a_string):
-    """Quote booleans
-        given a string that contains ": true", replace with ": 'true'" (or false)
-    """
-    tmp = a_string.replace(": true", ": 'true'")
-    tmp = tmp.replace(": false", ": 'false'")
-    return tmp
-
-
-
-def stripnl(s) -
-
-

Remove newlines from a string (and remove extra whitespace)

-
- -Expand source code - -
def stripnl(s):
-    """Remove newlines from a string (and remove extra whitespace)"""
-    s = str(s).replace("\n", " ")
-    return ' '.join(s.split())
-
-
-
-def support_info() -
-
-

Print out info that helps troubleshooting of the cli.

-
- -Expand source code - -
def support_info():
-    """Print out info that helps troubleshooting of the cli."""
-    print('')
-    print('If having issues with meshtastic cli or python library')
-    print('or wish to make feature requests, visit:')
-    print('https://github.com/meshtastic/Meshtastic-python/issues')
-    print('When adding an issue, be sure to include the following info:')
-    print(' System: {0}'.format(platform.system()))
-    print('   Platform: {0}'.format(platform.platform()))
-    print('   Release: {0}'.format(platform.uname().release))
-    print('   Machine: {0}'.format(platform.uname().machine))
-    print('   Encoding (stdin): {0}'.format(sys.stdin.encoding))
-    print('   Encoding (stdout): {0}'.format(sys.stdout.encoding))
-    print(' meshtastic: v{0}'.format(pkg_resources.require('meshtastic')[0].version))
-    print(' Executable: {0}'.format(sys.argv[0]))
-    print(' Python: {0} {1} {2}'.format(platform.python_version(),
-          platform.python_implementation(), platform.python_compiler()))
-    print('')
-    print('Please add the output from the command: meshtastic --info')
-
-
-
-
-
-

Classes

-
-
-class DeferredExecution -(name=None) -
-
-

A thread that accepts closures to run, and runs them as they are received

-
- -Expand source code - -
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):
-        """ Queue up the work"""
-        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())
-
-

Methods

-
-
-def _run(self) -
-
-
-
- -Expand source code - -
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())
-
-
-
-def queueWork(self, runnable) -
-
-

Queue up the work

-
- -Expand source code - -
def queueWork(self, runnable):
-    """ Queue up the work"""
-    self.queue.put(runnable)
-
-
-
-
-
-class Timeout -(maxSecs=20) -
-
-

Timeout class

-
- -Expand source code - -
class Timeout:
-    """Timeout class"""
-    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
-
-

Methods

-
-
-def reset(self) -
-
-

Restart the waitForSet timer

-
- -Expand source code - -
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.

-
- -Expand source code - -
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 dotdict -(*args, **kwargs) -
-
-

dot.notation access to dictionary attributes

-
- -Expand source code - -
class dotdict(dict):
-    """dot.notation access to dictionary attributes"""
-    __getattr__ = dict.get
-    __setattr__ = dict.__setitem__
-    __delattr__ = dict.__delitem__
-
-

Ancestors

-
    -
  • builtins.dict
  • -
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/python-cmd-guide.md b/docs/python-cmd-guide.md deleted file mode 100644 index 77a881a..0000000 --- a/docs/python-cmd-guide.md +++ /dev/null @@ -1,389 +0,0 @@ -# Python API Commands Guide - -The python pip package installs a "meshtastic" command line executable, which displays packets sent over the network as JSON and lets you see serial debugging information from the meshtastic devices. This command is not run inside of python, you run it from your operating system shell prompt directly. If when you type "meshtastic" it doesn't find the command and you are using Windows: Check that the python "scripts" directory is in your path. - -## Optional Arguments - -### -h or --help - -Shows a help message that describes the arguments. - -**Usage** - -``` shell -meshtastic -h -``` - -### --port PORT - -The port the Meshtastic device is connected to, i.e. /dev/ttyUSB0 or COM4. if unspecified, meshtastic will try to find it. Important to use when multiple devices are connected to ensure you call the command for the correct device. - -**Usage** - -``` shell -meshtastic --port /dev/ttyUSB0 --info -meshtastic --port COM4 --info -``` - -### --host HOST - -The hostname/ipaddr of the device to connect to (over TCP). - -**Usage** - -``` shell -meshtastic --host HOST -``` - -### --seriallog SERIALLOG - -Logs device serial output to either 'stdout', 'none' or a filename to append to. - -**Usage** - -``` shell -meshtastic --port /dev/ttyUSB0 --seriallog -``` - -### --info - -Read and display the radio config information. - -**Usage** - -``` shell -meshtastic --port /dev/ttyUSB0 --info -``` - -### --nodes - -Prints a node list in a pretty, formatted table. - -**Usage** - -``` shell -meshtastic --nodes -``` - -### --qr - -Displays the QR code that corresponds to the current channel. - -**Usage** - -``` shell -meshtastic --qr -``` - -### --get GET - -Gets a preferences field. - -**Usage** - -``` shell -meshtastic --get modem_config -``` - -### --set SET SET - -Sets a preferences field. - -**Usage** - -``` shell -meshtastic --set region Unset -``` - -### --seturl SETURL - -Set a channel URL. - -**Usage** - -``` shell -meshtastic --seturl https://www.meshtastic.org/c/GAMiIE67C6zsNmlWQ-KE1tKt0fRKFciHka-DShI6G7ElvGOiKgZzaGFyZWQ= -``` - -### --ch-index CH_INDEX - -Set the specified channel index - -**Usage** - -``` shell -meshtastic --ch-index 1 --ch-disable -``` - -### --ch-add CH_ADD - -Add a secondary channel, you must specify a channel name. - -**Usage** - -``` shell -meshtastic --ch-add testing-channel -``` -### --ch-del - -Delete the ch-index channel. - -**Usage** - -``` shell -meshtastic --ch-index 1 --ch-del -``` - -### --ch-enable - -Enable the specified channel. - -**Usage** - -``` shell -meshtastic --ch-index 1 --ch-enable -``` - -### --ch-disable - -Disable the specified channel. - -**Usage** - -``` shell -meshtastic --ch-index 1 --ch-disable -``` - -### --ch-set CH_SET CH_SET - -Set a channel parameter. - -**Usage** - -``` shell -meshtastic --ch-set id 1234 -``` - -### --ch-longslow - -Change to the standard long-range (but slow) channel. - -**Usage** - -``` shell -meshtastic --ch-longslow -``` - -### --ch-shortfast - -Change to the standard fast (but short range) channel. - -**Usage** - -``` shell -meshtastic --ch-shortfast -``` - -### --set-owner SET_OWNER - -Set device owner name. - -**Usage** - -``` shell -meshtastic --dest \!28979058 --set-owner "MeshyJohn" -``` - -### --set-ham SET_HAM - -Set licensed Ham ID and turn off encryption. - -**Usage** - -``` shell -meshtastic --set-ham KI1345 -``` - -### --dest DEST - -The destination node id for any sent commands - -**Usage** - -``` shell -meshtastic --dest \!28979058 --set-owner "MeshyJohn" -``` - -### --sendtext SENDTEXT - -Send a text message. - -**Usage** - -``` shell -meshtastic --sendtext "Hello Mesh!" -``` - -### --sendping - -Send a ping message (which requests a reply). - -**Usage** - -``` shell -meshtastic --sendping -``` - -### --reboot - -Tell the destination node to reboot. - -**Usage** - -``` shell -meshtastic --dest \!28979058 --reboot -``` - -### --reply - -Reply to received messages. - -**Usage** - -``` shell -meshtastic --reply -``` - -### --gpio-wrb GPIO_WRB GPIO_WRB - -Set a particular GPIO # to 1 or 0. - -**Usage** - -``` shell -meshtastic --port /dev/ttyUSB0 --gpio-wrb 4 1 --dest \!28979058 -``` - -### --gpio-rd GPIO_RD - -Read from a GPIO mask. - -**Usage** - -``` shell -meshtastic --port /dev/ttyUSB0 --gpio-rd 0x10 --dest \!28979058 -``` - -### --gpio-watch GPIO_WATCH - -Start watching a GPIO mask for changes. - -**Usage** - -``` shell -meshtastic --port /dev/ttyUSB0 --gpio-watch 0x10 --dest \!28979058 -``` - -### --no-time - -Suppress sending the current time to the mesh. - -**Usage** - -``` shell -meshtastic --port /dev/ttyUSB0 --no-time -``` - -### --setalt SETALT - -Set device altitude (allows use without GPS). - -**Usage** - -``` shell -meshtastic --setalt 120 -``` - -### --setlat SETLAT - -Set device latitude (allows use without GPS). - -**Usage** - -``` shell -meshtastic --setlat 25.2 -``` - -### --setlon SETLON - -Set device longitude (allows use without GPS). - -**Usage** - -``` shell -meshtastic --setlon -16.8 -``` - -### --debug - -Show API library debug log messages. - -**Usage** - -``` shell -meshtastic --debug --info -``` - -### --test - -Run stress test against all connected Meshtastic devices. - -**Usage** - -``` shell -meshtastic --test -``` - -### --ble BLE - -BLE mac address to connect to (BLE is not yet supported for this tool). - -**Usage** - -``` shell -meshtastic --ble "83:38:92:32:37:48" -``` - -### --noproto - -Don't start the API, just function as a dumb serial terminal. - -**Usage** - -``` shell -meshtastic --noproto -``` - -### --version - -Show program's version number and exit. - -**Usage** - -``` shell -meshtastic --version -``` - -## Deprecated Arguments - -### --setchan - -Deprecated - use "--ch-set param value" instead. - -### --set-router - -Deprecated - use "--set is_router true" instead. - -### --unset-router - -Deprecated - use "--set is_router false" instead. diff --git a/docs/stream-protocol.md b/docs/stream-protocol.md deleted file mode 100644 index 9552033..0000000 --- a/docs/stream-protocol.md +++ /dev/null @@ -1,17 +0,0 @@ -# Stream protocol - -Documentation on how out protobufs get encoded when placed onto any stream transport (i.e. TCP or serial, but not UDP or BLE) - -## Wire encoding - -When sending protobuf packets over serial or TCP each packet is preceded by uint32 sent in network byte order (big endian). -The upper 16 bits must be 0x94C3. The lower 16 bits are packet length (this encoding gives room to eventually allow quite large packets). - -Implementations validate length against the maximum possible size of a BLE packet (our lowest common denominator) of 512 bytes. If the -length provided is larger than that we assume the packet is corrupted and begin again looking for 0x4403 framing. - -The packets flowing towards the device are ToRadio protobufs, the packets flowing from the device are FromRadio protobufs. -The 0x94C3 marker can be used as framing to (eventually) resync if packets are corrupted over the wire. - -Note: the 0x94C3 framing was chosen to prevent confusion with the 7 bit ascii character set. It also doesn't collide with any valid utf8 encoding. This makes it a bit easier to start a device outputting regular debug output on its serial port and then only after it has received a valid packet from the PC, turn off unencoded debug printing and switch to this -packet encoding.