Compare commits

..

35 Commits

Author SHA1 Message Date
mkinney
36bed11959 Update publish_to_pypi.yml 2021-12-30 09:12:53 -08:00
mkinney
6f9bcfaaff Merge pull request #196 from mkinney/bump_version
bump version for testing pub to pypi
2021-12-30 09:06:54 -08:00
Mike Kinney
f17b66c872 bump version for testing pub to pypi 2021-12-30 09:05:26 -08:00
Sacha Weatherstone
040cb9bf34 Update and rename publish_to_pypi.py to publish_to_pypi.yml 2021-12-31 04:00:03 +11:00
mkinney
3269b3018f Merge pull request #195 from mkinney/publish_to_pypi
add workflow to publish to pypi
2021-12-30 08:49:26 -08:00
Mike Kinney
aab10b0912 add workflow to publish to pypi 2021-12-30 08:46:34 -08:00
mkinney
e2e9b7d55e Merge pull request #194 from mkinney/remove_raw_from_nodes_display
remove the raw key from the nodes dict
2021-12-30 08:28:36 -08:00
Mike Kinney
cecc5c3b25 remove the raw key from the nodes dict 2021-12-30 08:26:13 -08:00
mkinney
54bb846d00 Merge pull request #193 from mkinney/temp_disable_reply_unittest
need to comment out unittest with change to --reply
2021-12-30 07:51:15 -08:00
Mike Kinney
1a5f525632 need to comment out unittest with change to --reply 2021-12-30 07:49:34 -08:00
mkinney
8ba3d26d63 Merge pull request #191 from Beiri22/patch-1
Update __main__.py
2021-12-30 07:46:40 -08:00
Beiri22
b341b6cfdb Update __main__.py
Main loop also in reply mode.
2021-12-30 10:31:27 +01:00
mkinney
38e7972191 Merge pull request #189 from mkinney/master
bump version; remove docs dir; no need to regen docs on release
2021-12-29 22:02:01 -08:00
mkinney
5be70328fe Merge branch 'meshtastic:master' into master 2021-12-29 21:58:15 -08:00
Mike Kinney
dfe798dbdf no longer gen docs 2021-12-29 21:57:16 -08:00
Mike Kinney
d98b23dba0 remove docs dir 2021-12-29 21:56:03 -08:00
Mike Kinney
4fbe5c7863 bump version 2021-12-29 21:55:41 -08:00
Sacha Weatherstone
69bb8bcca2 Fx protobuf path 2021-12-30 16:52:25 +11:00
Sacha Weatherstone
bb2ea17371 Fix action name 2021-12-30 16:49:55 +11:00
Sacha Weatherstone
b51af8a070 Create update_protobufs 2021-12-30 16:48:20 +11:00
mkinney
aede26694c Merge pull request #188 from mkinney/work_on_serial_issue
revamp the serial connection to avoid Tbeam reboots
2021-12-29 21:41:59 -08:00
Mike Kinney
8e1010e9f2 ignore two checks for Windows smoke1 testing 2021-12-29 21:21:24 -08:00
Mike Kinney
ce2d9f5728 yet another attempt 2021-12-29 21:13:20 -08:00
Mike Kinney
9879e9f2df try yet another way 2021-12-29 21:00:22 -08:00
Mike Kinney
e79faf93d0 try a different way 2021-12-29 20:57:57 -08:00
Mike Kinney
181c04716a accidentally dropped an arg 2021-12-29 20:55:31 -08:00
Mike Kinney
c7d981ec35 fix typo 2021-12-29 20:51:46 -08:00
Mike Kinney
75fe7622a4 deal with windows on the serial issue 2021-12-29 20:49:19 -08:00
Mike Kinney
5dc800f9a3 deal with windows on smoke1 test 2021-12-29 20:39:34 -08:00
Mike Kinney
c7d3f9f787 close seriallog if we have one 2021-12-29 20:35:50 -08:00
Mike Kinney
c70d36d2cd revamp the serial connection to avoid Tbeam reboots 2021-12-29 19:24:26 -08:00
Jm Casler
f239c23d9a Merge branch 'master' of https://github.com/meshtastic/Meshtastic-python 2021-12-29 14:18:35 -08:00
Jm Casler
ac5d729cdf Bumped to 1.2.47. 2021-12-29 14:18:34 -08:00
mkinney
047f43534f Merge pull request #187 from mkinney/bump_version
bump version
2021-12-29 13:29:28 -08:00
Mike Kinney
c26e030f1f bump version 2021-12-29 13:28:08 -08:00
18 changed files with 467 additions and 182 deletions

35
.github/workflows/publish_to_pypi.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Publish PyPI
on: workflow_dispatch
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install pypa/build
run: >-
python -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
.
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
username: __token__
password: ${{ secrets.PYPI_API_TOKEN }}

24
.github/workflows/update_protobufs.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: "Update protobufs"
on: workflow_dispatch
jobs:
update-protobufs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: true
- name: Update Submodule
run: |
git pull --recurse-submodules
git submodule update --remote --recursive
- name: Commit update
run: |
git config --global user.name 'github-actions'
git config --global user.email 'bot@noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git add proto
git commit -m "Update protobuf submodule" && git push || echo "No changes to commit"

View File

@@ -23,7 +23,7 @@ ignore-patterns=mqtt_pb2.py,channel_pb2.py,environmental_measurement_pb2.py,admi
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
#
disable=invalid-name,fixme,logging-fstring-interpolation,too-many-statements,too-many-branches,too-many-locals,no-member,f-string-without-interpolation,protected-access,no-self-use,pointless-string-statement,too-few-public-methods,consider-using-f-string,broad-except,no-else-return,unused-argument,global-statement,global-variable-not-assigned,too-many-boolean-expressions,no-else-raise,bare-except
disable=invalid-name,fixme,logging-fstring-interpolation,too-many-statements,too-many-branches,too-many-locals,no-member,f-string-without-interpolation,protected-access,no-self-use,pointless-string-statement,too-few-public-methods,consider-using-f-string,broad-except,no-else-return,unused-argument,global-statement,global-variable-not-assigned,too-many-boolean-expressions,no-else-raise,bare-except,c-extension-no-member
[BASIC]

View File

@@ -1,7 +1,5 @@
rm dist/*
set -e
bin/regen-docs.sh
pandoc --from=markdown --to=rst --output=README README.md
python3 setup.py sdist bdist_wheel
python3 -m twine upload dist/*
python3 -m twine upload dist/*

View File

@@ -567,6 +567,7 @@ def export_config(interface):
def common():
"""Shared code for all of our command line wrappers"""
logfile = None
our_globals = Globals.getInstance()
args = our_globals.get_args()
parser = our_globals.get_parser()
@@ -621,8 +622,8 @@ def common():
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')
logfile = open(args.seriallog, 'w+', buffering=1, encoding='utf8')
our_globals.set_logfile(logfile)
subscribe()
if args.ble:
@@ -636,8 +637,10 @@ def common():
# We assume client is fully connected now
onConnected(client)
#if logfile:
#logfile.close()
if args.noproto or (have_tunnel and args.tunnel): # loop until someone presses ctrlc
if args.noproto or args.reply or (have_tunnel and args.tunnel): # loop until someone presses ctrlc
while True:
time.sleep(1000)
@@ -828,6 +831,10 @@ def main():
our_globals.set_parser(parser)
initParser()
common()
logfile = our_globals.get_logfile()
if logfile:
logfile.close()
def tunnelMain():

View File

@@ -12,8 +12,8 @@ _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
from . import mesh_pb2 as mesh__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,9 +21,9 @@ DESCRIPTOR = _descriptor.FileDescriptor(
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'
serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\rchannel.proto\x1a\x11radioconfig.proto\x1a\nmesh.proto\"\xbd\x03\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\x1b\n\x11get_owner_request\x18\x08 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\t \x01(\x0b\x32\x05.UserH\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,])
dependencies=[channel__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,mesh__pb2.DESCRIPTOR,])
@@ -85,28 +85,42 @@ _ADMINMESSAGE = _descriptor.Descriptor(
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,
name='get_owner_request', full_name='AdminMessage.get_owner_request', index=7,
number=8, 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_owner_response', full_name='AdminMessage.get_owner_response', index=8,
number=9, 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=9,
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,
name='confirm_set_radio', full_name='AdminMessage.confirm_set_radio', index=10,
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,
name='exit_simulator', full_name='AdminMessage.exit_simulator', index=11,
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,
name='reboot_seconds', full_name='AdminMessage.reboot_seconds', index=12,
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,
@@ -128,7 +142,7 @@ _ADMINMESSAGE = _descriptor.Descriptor(
index=0, containing_type=None, fields=[]),
],
serialized_start=62,
serialized_end=441,
serialized_end=507,
)
_ADMINMESSAGE.fields_by_name['set_radio'].message_type = radioconfig__pb2._RADIOCONFIG
@@ -136,6 +150,7 @@ _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.fields_by_name['get_owner_response'].message_type = mesh__pb2._USER
_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']
@@ -157,6 +172,12 @@ _ADMINMESSAGE.fields_by_name['get_channel_request'].containing_oneof = _ADMINMES
_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['get_owner_request'])
_ADMINMESSAGE.fields_by_name['get_owner_request'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_owner_response'])
_ADMINMESSAGE.fields_by_name['get_owner_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']

View File

@@ -29,6 +29,7 @@ class Globals:
self.parser = None
self.target_node = None
self.channel_index = None
self.logfile = None
def reset(self):
"""Reset all of our globals. If you add a member, add it to this method, too."""
@@ -37,6 +38,7 @@ class Globals:
self.target_node = None
self.channel_index = None
# setters
def set_args(self, args):
"""Set the args"""
self.args = args
@@ -53,6 +55,11 @@ class Globals:
"""Set the channel_index"""
self.channel_index = channel_index
def set_logfile(self, logfile):
"""Set the logfile"""
self.logfile = logfile
# getters
def get_args(self):
"""Get args"""
return self.args
@@ -68,3 +75,7 @@ class Globals:
def get_channel_index(self):
"""Get channel_index"""
return self.channel_index
def get_logfile(self):
"""Get logfile"""
return self.logfile

View File

@@ -18,7 +18,7 @@ from google.protobuf.json_format import MessageToJson
import meshtastic.node
from . import portnums_pb2, mesh_pb2
from .util import stripnl, Timeout, our_exit
from .util import stripnl, Timeout, our_exit, remove_keys_from_dict
from .__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols
class MeshInterface:
@@ -84,7 +84,10 @@ class MeshInterface:
nodes = ""
if self.nodes:
for n in self.nodes.values():
nodes = nodes + f" {stripnl(n)}"
# when the TBeam is first booted, it sometimes shows the 'raw' data
# so, we will just remove any raw keys
n2 = remove_keys_from_dict('raw', n)
nodes = nodes + f" {stripnl(n2)}"
infos = owner + myinfo + mesh + nodes
print(infos)
return infos

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,14 +1,16 @@
""" Serial interface class
"""
import logging
import time
import platform
import os
import stat
import serial
import meshtastic.util
from .stream_interface import StreamInterface
if platform.system() != 'Windows':
import termios
class SerialInterface(StreamInterface):
"""Interface class for meshtastic devices over a serial link"""
@@ -20,6 +22,7 @@ class SerialInterface(StreamInterface):
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})
"""
self.noProto = noProto
if devPath is None:
ports = meshtastic.util.findPorts()
@@ -35,43 +38,30 @@ class SerialInterface(StreamInterface):
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)
# first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR
# see https://github.com/pyserial/pyserial/issues/124
if not self.noProto:
if platform.system() != 'Windows':
with open(devPath, encoding='utf8') as f:
attrs = termios.tcgetattr(f)
attrs[2] = attrs[2] & ~termios.HUPCL
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
f.close()
time.sleep(0.1)
# rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
self.stream.port = devPath
self.stream = serial.Serial(devPath, 921600, exclusive=True, timeout=0.5, write_timeout=0)
if not self.noProto:
self.stream.flush()
time.sleep(0.1)
# 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)
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()
def close(self):
"""Close a connection to the device"""
if not self.noProto:
self.stream.flush()
time.sleep(0.1)
self.stream.flush()
time.sleep(0.1)
logging.debug("Closing Serial stream")
StreamInterface.close(self)

View File

@@ -18,7 +18,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
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'
serialized_pb=b'\n\x12storeforward.proto\"\x8a\x06\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\x12-\n\theartbeat\x18\x04 \x01(\x0b\x32\x1a.StoreAndForward.Heartbeat\x1a\xcd\x01\n\nStatistics\x12\x16\n\x0emessages_total\x18\x01 \x01(\r\x12\x16\n\x0emessages_saved\x18\x02 \x01(\r\x12\x14\n\x0cmessages_max\x18\x03 \x01(\r\x12\x0f\n\x07up_time\x18\x04 \x01(\r\x12\x10\n\x08requests\x18\x05 \x01(\r\x12\x18\n\x10requests_history\x18\x06 \x01(\r\x12\x11\n\theartbeat\x18\x07 \x01(\x08\x12\x12\n\nreturn_max\x18\x08 \x01(\r\x12\x15\n\rreturn_window\x18\t \x01(\r\x1aI\n\x07History\x12\x18\n\x10history_messages\x18\x01 \x01(\r\x12\x0e\n\x06window\x18\x02 \x01(\r\x12\x14\n\x0clast_request\x18\x03 \x01(\r\x1a.\n\tHeartbeat\x12\x0e\n\x06period\x18\x01 \x01(\r\x12\x11\n\tsecondary\x18\x02 \x01(\r\"\xf7\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\x12\n\x0eROUTER_HISTORY\x10\x06\x12\x10\n\x0c\x43LIENT_ERROR\x10\x65\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x66\x12\x10\n\x0c\x43LIENT_STATS\x10g\x12\x0f\n\x0b\x43LIENT_PING\x10h\x12\x0f\n\x0b\x43LIENT_PONG\x10i\x12\x10\n\x0c\x43LIENT_ABORT\x10jBQ\n\x13\x63om.geeksville.meshB\x15StoreAndForwardProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
)
@@ -54,30 +54,38 @@ _STOREANDFORWARD_REQUESTRESPONSE = _descriptor.EnumDescriptor(
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_ERROR', index=6, number=101,
name='ROUTER_HISTORY', index=6, number=6,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_HISTORY', index=7, number=102,
name='CLIENT_ERROR', index=7, number=101,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_STATS', index=8, number=103,
name='CLIENT_HISTORY', index=8, number=102,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_PING', index=9, number=104,
name='CLIENT_STATS', index=9, number=103,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_PONG', index=10, number=105,
name='CLIENT_PING', index=10, number=104,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_PONG', index=11, number=105,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_ABORT', index=12, number=106,
serialized_options=None,
type=None),
],
containing_type=None,
serialized_options=None,
serialized_start=429,
serialized_end=638,
serialized_start=554,
serialized_end=801,
)
_sym_db.RegisterEnumDescriptor(_STOREANDFORWARD_REQUESTRESPONSE)
@@ -90,63 +98,63 @@ _STOREANDFORWARD_STATISTICS = _descriptor.Descriptor(
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='MessagesTotal', full_name='StoreAndForward.Statistics.MessagesTotal', index=0,
name='messages_total', full_name='StoreAndForward.Statistics.messages_total', 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,
name='messages_saved', full_name='StoreAndForward.Statistics.messages_saved', 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,
name='messages_max', full_name='StoreAndForward.Statistics.messages_max', 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,
name='up_time', full_name='StoreAndForward.Statistics.up_time', 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,
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,
name='requests_history', full_name='StoreAndForward.Statistics.requests_history', 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,
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,
name='return_max', full_name='StoreAndForward.Statistics.return_max', 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,
name='return_window', full_name='StoreAndForward.Statistics.return_window', 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,
@@ -164,8 +172,8 @@ _STOREANDFORWARD_STATISTICS = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=176,
serialized_end=374,
serialized_start=223,
serialized_end=428,
)
_STOREANDFORWARD_HISTORY = _descriptor.Descriptor(
@@ -176,14 +184,58 @@ _STOREANDFORWARD_HISTORY = _descriptor.Descriptor(
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='HistoryMessages', full_name='StoreAndForward.History.HistoryMessages', index=0,
name='history_messages', full_name='StoreAndForward.History.history_messages', 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,
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),
_descriptor.FieldDescriptor(
name='last_request', full_name='StoreAndForward.History.last_request', 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),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=430,
serialized_end=503,
)
_STOREANDFORWARD_HEARTBEAT = _descriptor.Descriptor(
name='Heartbeat',
full_name='StoreAndForward.Heartbeat',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='period', full_name='StoreAndForward.Heartbeat.period', 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='secondary', full_name='StoreAndForward.Heartbeat.secondary', 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,
@@ -201,8 +253,8 @@ _STOREANDFORWARD_HISTORY = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=376,
serialized_end=426,
serialized_start=505,
serialized_end=551,
)
_STOREANDFORWARD = _descriptor.Descriptor(
@@ -233,10 +285,17 @@ _STOREANDFORWARD = _descriptor.Descriptor(
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.heartbeat', 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),
],
extensions=[
],
nested_types=[_STOREANDFORWARD_STATISTICS, _STOREANDFORWARD_HISTORY, ],
nested_types=[_STOREANDFORWARD_STATISTICS, _STOREANDFORWARD_HISTORY, _STOREANDFORWARD_HEARTBEAT, ],
enum_types=[
_STOREANDFORWARD_REQUESTRESPONSE,
],
@@ -247,14 +306,16 @@ _STOREANDFORWARD = _descriptor.Descriptor(
oneofs=[
],
serialized_start=23,
serialized_end=638,
serialized_end=801,
)
_STOREANDFORWARD_STATISTICS.containing_type = _STOREANDFORWARD
_STOREANDFORWARD_HISTORY.containing_type = _STOREANDFORWARD
_STOREANDFORWARD_HEARTBEAT.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.fields_by_name['heartbeat'].message_type = _STOREANDFORWARD_HEARTBEAT
_STOREANDFORWARD_REQUESTRESPONSE.containing_type = _STOREANDFORWARD
DESCRIPTOR.message_types_by_name['StoreAndForward'] = _STOREANDFORWARD
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
@@ -274,6 +335,13 @@ StoreAndForward = _reflection.GeneratedProtocolMessageType('StoreAndForward', (_
# @@protoc_insertion_point(class_scope:StoreAndForward.History)
})
,
'Heartbeat' : _reflection.GeneratedProtocolMessageType('Heartbeat', (_message.Message,), {
'DESCRIPTOR' : _STOREANDFORWARD_HEARTBEAT,
'__module__' : 'storeforward_pb2'
# @@protoc_insertion_point(class_scope:StoreAndForward.Heartbeat)
})
,
'DESCRIPTOR' : _STOREANDFORWARD,
'__module__' : 'storeforward_pb2'
# @@protoc_insertion_point(class_scope:StoreAndForward)
@@ -281,6 +349,7 @@ StoreAndForward = _reflection.GeneratedProtocolMessageType('StoreAndForward', (_
_sym_db.RegisterMessage(StoreAndForward)
_sym_db.RegisterMessage(StoreAndForward.Statistics)
_sym_db.RegisterMessage(StoreAndForward.History)
_sym_db.RegisterMessage(StoreAndForward.Heartbeat)
DESCRIPTOR._options = None

View File

@@ -62,7 +62,8 @@ class StreamInterface(MeshInterface):
# 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
if not self.noProto:
time.sleep(0.1) # wait 100ms to give device time to start running
self._rxThread.start()
@@ -88,6 +89,9 @@ class StreamInterface(MeshInterface):
if self.stream: # ignore writes when stream is closed
self.stream.write(b)
self.stream.flush()
# we sleep here to give the TBeam a chance to work
if not self.noProto:
time.sleep(0.1)
def _readBytes(self, length):
"""Read an array of bytes from our stream"""

View File

@@ -1289,59 +1289,61 @@ def test_main_onReceive_with_sendtext(caplog, reset_globals):
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()
# TODO: re-write this with updated
# temp disable this test as we need to have a separate thread going with '--reply'
#@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

View File

@@ -2,6 +2,7 @@
import re
import subprocess
import time
import platform
import os
# Do not like using hard coded sleeps, but it probably makes
@@ -140,8 +141,9 @@ 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)
if platform.system() != 'Windows':
assert re.search(r' User ', out, re.MULTILINE)
assert re.search(r' 1 ', out, re.MULTILINE)
assert return_value == 0

View File

@@ -7,7 +7,8 @@ import pytest
from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
support_info, genPSK256, fromStr, fromPSK,
quoteBooleans, catchAndIgnore)
quoteBooleans, catchAndIgnore,
remove_keys_from_dict)
@pytest.mark.unit
@@ -149,3 +150,27 @@ def test_catchAndIgnore(caplog):
with caplog.at_level(logging.DEBUG):
catchAndIgnore("something", some_closure)
assert re.search(r'Exception thrown in something', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_remove_keys_from_dict_empty_keys_empty_dict():
"""Test when keys and dict both are empty"""
assert not remove_keys_from_dict((), {})
@pytest.mark.unit
def test_remove_keys_from_dict_empty_dict():
"""Test when dict is empty"""
assert not remove_keys_from_dict(('a'), {})
@pytest.mark.unit
def test_remove_keys_from_dict_empty_keys():
"""Test when keys is empty"""
assert remove_keys_from_dict((), {'a':1}) == {'a':1}
@pytest.mark.unit
def test_remove_keys_from_dict():
"""Test remove_keys_from_dict()"""
assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1}

View File

@@ -201,3 +201,12 @@ def support_info():
platform.python_implementation(), platform.python_compiler()))
print('')
print('Please add the output from the command: meshtastic --info')
def remove_keys_from_dict(keys, adict):
"""Return a dictionary without some keys in it."""
newdict = adict
for key in keys:
if key in adict:
del newdict[key]
return newdict

View File

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