mirror of
https://github.com/meshtastic/python.git
synced 2025-12-26 09:27:52 -05:00
Compare commits
205 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b41baeac98 | ||
|
|
bbc458954d | ||
|
|
2f59028df3 | ||
|
|
2eda2e56d5 | ||
|
|
52f8e82ea1 | ||
|
|
b11edacee0 | ||
|
|
6dcdf7fc19 | ||
|
|
fe8ae6237e | ||
|
|
7908fda1cf | ||
|
|
fe9a367553 | ||
|
|
79d7dcc199 | ||
|
|
ddf2221797 | ||
|
|
a6f9100520 | ||
|
|
36011da918 | ||
|
|
882e160a32 | ||
|
|
958edbfdb2 | ||
|
|
484dc8007c | ||
|
|
2464bcf414 | ||
|
|
b468a0c908 | ||
|
|
de154e50ca | ||
|
|
70bb58b8ce | ||
|
|
dae63d4176 | ||
|
|
9aef3b11f1 | ||
|
|
eebaa10d13 | ||
|
|
5076119b4f | ||
|
|
e25b183c23 | ||
|
|
236d30f7c1 | ||
|
|
1f054abe47 | ||
|
|
d793ae431c | ||
|
|
d02c4af995 | ||
|
|
7123a095dc | ||
|
|
abbe9ba10e | ||
|
|
6fe40e22b6 | ||
|
|
ae648e7a82 | ||
|
|
550a5fe49a | ||
|
|
20bb1ebdfa | ||
|
|
a6d20abcdf | ||
|
|
7d9e75cf3f | ||
|
|
7c3df00b46 | ||
|
|
28e848ace6 | ||
|
|
849724a129 | ||
|
|
309b069558 | ||
|
|
cbedc982b6 | ||
|
|
dd482f2f89 | ||
|
|
29331cc3d2 | ||
|
|
4f2fbe39c0 | ||
|
|
b2f3ba11ae | ||
|
|
08c0b0e940 | ||
|
|
775cb4d650 | ||
|
|
52cc825b3e | ||
|
|
ce3065b37d | ||
|
|
d6ee815183 | ||
|
|
0192eed76e | ||
|
|
9858fa1976 | ||
|
|
95bfc0b428 | ||
|
|
130c82ae4f | ||
|
|
3698f2e4fb | ||
|
|
f58f8bdb1d | ||
|
|
e76c9852d6 | ||
|
|
0788c1fadc | ||
|
|
a8057ac670 | ||
|
|
bb067e0e1e | ||
|
|
755e68040f | ||
|
|
1687a4cb90 | ||
|
|
03398c7e3b | ||
|
|
d27be003c7 | ||
|
|
8e39a00c30 | ||
|
|
055da95b8a | ||
|
|
0c2ad5c77c | ||
|
|
0a88ca6a5c | ||
|
|
51079d4f25 | ||
|
|
4ca3b4bf58 | ||
|
|
25d42d3361 | ||
|
|
a1bffe4f26 | ||
|
|
b87630803f | ||
|
|
e2c7a2c32c | ||
|
|
9dda5d6d2d | ||
|
|
c3be392533 | ||
|
|
f63f2e3e39 | ||
|
|
c8dbac7770 | ||
|
|
959c597e33 | ||
|
|
4b0ca13ad1 | ||
|
|
811bfdcb8c | ||
|
|
79095dc243 | ||
|
|
ff9ab44796 | ||
|
|
77dea8ee67 | ||
|
|
c2eb466792 | ||
|
|
b4405dc60e | ||
|
|
dc3d43c57c | ||
|
|
be91e923ab | ||
|
|
6408d65ae7 | ||
|
|
6506a1be1a | ||
|
|
fc768fa3ea | ||
|
|
8012f979cb | ||
|
|
06b87b376c | ||
|
|
1a97dc6982 | ||
|
|
6a181ae025 | ||
|
|
97aa8a8d74 | ||
|
|
7e6f13f0a2 | ||
|
|
f0723ffbc0 | ||
|
|
78f85efe56 | ||
|
|
72510088c9 | ||
|
|
edb537947a | ||
|
|
b8b268feac | ||
|
|
c28b61fbb1 | ||
|
|
62843ea39c | ||
|
|
cb93669740 | ||
|
|
f154d223bf | ||
|
|
4980a02ef6 | ||
|
|
c65a60d22d | ||
|
|
3a6475dc9d | ||
|
|
6f91479605 | ||
|
|
bbebddea78 | ||
|
|
45be828183 | ||
|
|
e0753d4745 | ||
|
|
21f2e185f2 | ||
|
|
4bfedf6aa9 | ||
|
|
193c3faca5 | ||
|
|
1f47c225b3 | ||
|
|
1b372fca8d | ||
|
|
5e7b6027fa | ||
|
|
64bd5deb4b | ||
|
|
d0fdb9b570 | ||
|
|
f37755209e | ||
|
|
297c0dbc0e | ||
|
|
7de17f7c94 | ||
|
|
55f6494681 | ||
|
|
b2cfebc5a7 | ||
|
|
802768e0cc | ||
|
|
f68e4112e1 | ||
|
|
19b4cd65ce | ||
|
|
ef12125785 | ||
|
|
014993f058 | ||
|
|
a10da7d3ed | ||
|
|
786dca9d13 | ||
|
|
1cfd471abc | ||
|
|
9ff575c388 | ||
|
|
b973e39ef7 | ||
|
|
75cdc5a36b | ||
|
|
93441a473f | ||
|
|
7b8a83ade7 | ||
|
|
2cf1ce2898 | ||
|
|
e4890bfff2 | ||
|
|
5c6ba26334 | ||
|
|
e6e3ad121d | ||
|
|
7133c859d6 | ||
|
|
8acf0b849a | ||
|
|
42f6818a02 | ||
|
|
af043ef5c0 | ||
|
|
4f46858643 | ||
|
|
ddc47fb8de | ||
|
|
7ee134b819 | ||
|
|
254e9f4015 | ||
|
|
babd1242d5 | ||
|
|
cc99ea009e | ||
|
|
76407e11f8 | ||
|
|
57719ddd5e | ||
|
|
d0b8b9ff1b | ||
|
|
c5c9723208 | ||
|
|
9bceaafd9c | ||
|
|
7d3a9178ea | ||
|
|
2c76c0cafa | ||
|
|
82977e9ef2 | ||
|
|
7a9c25da8e | ||
|
|
342c48fb16 | ||
|
|
6bc955a403 | ||
|
|
741ba378ab | ||
|
|
c1054caf4a | ||
|
|
24b97d9277 | ||
|
|
868fb64857 | ||
|
|
8729e97e1b | ||
|
|
aaed54393e | ||
|
|
d12776bb5f | ||
|
|
7829f6afca | ||
|
|
4bd10bc102 | ||
|
|
3821e02f09 | ||
|
|
97689da0b4 | ||
|
|
5c75e74bf9 | ||
|
|
388a46abf4 | ||
|
|
6b89fc81a1 | ||
|
|
c9b5d5d697 | ||
|
|
f16dd0e737 | ||
|
|
5ed19eff73 | ||
|
|
0b3605141d | ||
|
|
f1df14ca92 | ||
|
|
83776ceec5 | ||
|
|
7aff5e9ee5 | ||
|
|
bf6be107d3 | ||
|
|
c24d1fe26b | ||
|
|
61f5468847 | ||
|
|
c713ce04b6 | ||
|
|
fe2b36e04b | ||
|
|
a720916df5 | ||
|
|
b2593e4bb1 | ||
|
|
6e3c759e5c | ||
|
|
a41f33e0bd | ||
|
|
111bf8dcbf | ||
|
|
f18abb2fe6 | ||
|
|
b7093e176a | ||
|
|
cf7d37a454 | ||
|
|
2af431e2eb | ||
|
|
3db64f7988 | ||
|
|
7ef64d4250 | ||
|
|
363aa995a2 | ||
|
|
696fa28e6f |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
* text=auto eol=lf
|
||||
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
*.{sh,[sS][hH]} text eol=lf
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -13,11 +13,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Python 3
|
||||
@@ -35,7 +34,7 @@ jobs:
|
||||
which meshtastic
|
||||
meshtastic --version
|
||||
- name: Run pylint
|
||||
run: pylint meshtastic examples/
|
||||
run: pylint meshtastic examples/ --ignore-patterns ".*_pb2.py$"
|
||||
- name: Run tests with pytest
|
||||
run: pytest --cov=meshtastic
|
||||
- name: Generate coverage report
|
||||
@@ -55,11 +54,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Python 3
|
||||
|
||||
20
.github/workflows/cleanup_artifacts.yml
vendored
Normal file
20
.github/workflows/cleanup_artifacts.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Remove old artifacts
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Every day at 1am
|
||||
- cron: "0 1 * * *"
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
remove-old-artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Remove old artifacts
|
||||
uses: c-hive/gha-remove-artifacts@v1
|
||||
with:
|
||||
age: "1 month"
|
||||
skip-tags: true
|
||||
111
.github/workflows/release.yml
vendored
111
.github/workflows/release.yml
vendored
@@ -11,76 +11,73 @@ jobs:
|
||||
new_sha: ${{ steps.commit_updated.outputs.sha }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Bump version
|
||||
run: >-
|
||||
bin/bump_version.py
|
||||
|
||||
- name: Bump version
|
||||
run: >-
|
||||
bin/bump_version.py
|
||||
- name: Commit updated version.py
|
||||
id: commit_updated
|
||||
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 setup.py
|
||||
git commit -m "bump version" && git push || echo "No changes to commit"
|
||||
git log -n 1 --pretty=format:"%H" | tail -n 1 | awk '{print "::set-output name=sha::"$0}'
|
||||
|
||||
- name: Commit updated version.py
|
||||
id: commit_updated
|
||||
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 setup.py
|
||||
git commit -m "bump version" && git push || echo "No changes to commit"
|
||||
git log -n 1 --pretty=format:"%H" | tail -n 1 | awk '{print "::set-output name=sha::"$0}'
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: >-
|
||||
bin/show_version.py
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: >-
|
||||
bin/show_version.py
|
||||
- name: Create GitHub release
|
||||
uses: actions/create-release@v1
|
||||
id: create_release
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: actions/create-release@v1
|
||||
id: create_release
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
release_name: Meshtastic Python ${{ steps.get_version.outputs.version }}
|
||||
tag_name: ${{ steps.get_version.outputs.version }}
|
||||
body: |
|
||||
Autogenerated by github action, developer should edit as required before publishing...
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
release_name: Meshtastic Python ${{ steps.get_version.outputs.version }}
|
||||
tag_name: ${{ steps.get_version.outputs.version }}
|
||||
body: |
|
||||
Autogenerated by github action, developer should edit as required before publishing...
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- 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: 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:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
- 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:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
build-and-publish-mac:
|
||||
runs-on: macos-latest
|
||||
needs: release_create
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -127,7 +124,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release_create
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -169,7 +165,6 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
needs: release_create
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
10
.github/workflows/update_protobufs.yml
vendored
10
.github/workflows/update_protobufs.yml
vendored
@@ -15,22 +15,22 @@ jobs:
|
||||
run: |
|
||||
git pull --recurse-submodules
|
||||
git submodule update --remote --recursive
|
||||
|
||||
|
||||
- name: Download nanopb
|
||||
run: |
|
||||
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.6-linux-x86.tar.gz
|
||||
tar xvzf nanopb-0.4.6-linux-x86.tar.gz
|
||||
mv nanopb-0.4.6-linux-x86 nanopb-0.4.6
|
||||
|
||||
|
||||
- name: Re-generate protocol buffers
|
||||
run: |
|
||||
./bin/regen-protos.sh
|
||||
|
||||
./bin/regen-protobufs.sh
|
||||
|
||||
- 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 add protobufs
|
||||
git add meshtastic
|
||||
git commit -m "Update protobuf submodule" && git push || echo "No changes to commit"
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "proto"]
|
||||
path = proto
|
||||
url = https://github.com/meshtastic/Meshtastic-protobufs.git
|
||||
[submodule "protobufs"]
|
||||
path = protobufs
|
||||
url = http://github.com/meshtastic/protobufs
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
# Add files or directories matching the regex patterns to the blacklist. The
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=mqtt_pb2.py,channel_pb2.py,telemetry_pb2.py,admin_pb2.py,config_pb2.py,deviceonly_pb2.py,apponly_pb2.py,remote_hardware_pb2.py,portnums_pb2.py,mesh_pb2.py,storeforward_pb2.py,cannedmessages_pb2.py,module_config_pb2.py,localonly_pb2.py,node.py
|
||||
ignore-patterns=mqtt_pb2.py,channel_pb2.py,telemetry_pb2.py,admin_pb2.py,config_pb2.py,deviceonly_pb2.py,apponly_pb2.py,remote_hardware_pb2.py,portnums_pb2.py,mesh_pb2.py,storeforward_pb2.py,cannedmessages_pb2.py,module_config_pb2.py,localonly_pb2.py,node.py,device_metadata_pb2.py
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ ignore-patterns=mqtt_pb2.py,channel_pb2.py,telemetry_pb2.py,admin_pb2.py,config_
|
||||
# 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,broad-except,no-else-return,no-else-raise,bare-except,too-many-public-methods
|
||||
disable=invalid-name,fixme,logging-fstring-interpolation,too-many-statements,too-many-branches,too-many-locals,no-member,f-string-without-interpolation,protected-access,pointless-string-statement,too-few-public-methods,broad-except,no-else-return,no-else-raise,bare-except,too-many-public-methods
|
||||
|
||||
[BASIC]
|
||||
|
||||
|
||||
8
.trunk/.gitignore
vendored
Normal file
8
.trunk/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
*out
|
||||
*logs
|
||||
*actions
|
||||
*notifications
|
||||
*tools
|
||||
plugins
|
||||
user_trunk.yaml
|
||||
user.yaml
|
||||
2
.trunk/configs/.isort.cfg
Normal file
2
.trunk/configs/.isort.cfg
Normal file
@@ -0,0 +1,2 @@
|
||||
[settings]
|
||||
profile=black
|
||||
10
.trunk/configs/.markdownlint.yaml
Normal file
10
.trunk/configs/.markdownlint.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
# Autoformatter friendly markdownlint config (all formatting rules disabled)
|
||||
default: true
|
||||
blank_lines: false
|
||||
bullet: false
|
||||
html: false
|
||||
indentation: false
|
||||
line_length: false
|
||||
spaces: false
|
||||
url: false
|
||||
whitespace: false
|
||||
7
.trunk/configs/.shellcheckrc
Normal file
7
.trunk/configs/.shellcheckrc
Normal file
@@ -0,0 +1,7 @@
|
||||
enable=all
|
||||
source-path=SCRIPTDIR
|
||||
disable=SC2154
|
||||
|
||||
# If you're having issues with shellcheck following source, disable the errors via:
|
||||
# disable=SC1090
|
||||
# disable=SC1091
|
||||
10
.trunk/configs/.yamllint.yaml
Normal file
10
.trunk/configs/.yamllint.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
rules:
|
||||
quoted-strings:
|
||||
required: only-when-needed
|
||||
extra-allowed: ["{|}"]
|
||||
empty-values:
|
||||
forbid-in-block-mappings: true
|
||||
forbid-in-flow-mappings: true
|
||||
key-duplicates: {}
|
||||
octal-values:
|
||||
forbid-implicit-octal: true
|
||||
5
.trunk/configs/ruff.toml
Normal file
5
.trunk/configs/ruff.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
# Generic, formatter-friendly config.
|
||||
select = ["B", "D3", "D4", "E", "F"]
|
||||
|
||||
# Never enforce `E501` (line length violations). This should be handled by formatters.
|
||||
ignore = ["E501"]
|
||||
46
.trunk/trunk.yaml
Normal file
46
.trunk/trunk.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
version: 0.1
|
||||
cli:
|
||||
version: 1.15.0
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
ref: v1.2.2
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
disabled:
|
||||
- bandit
|
||||
ignore:
|
||||
- linters: [ALL]
|
||||
paths:
|
||||
# Ignore generated files
|
||||
- meshtastic/*_pb2.py
|
||||
enabled:
|
||||
- actionlint@1.6.25
|
||||
- black@23.7.0
|
||||
- checkov@2.4.9
|
||||
- git-diff-check
|
||||
- gitleaks@8.18.0
|
||||
- isort@5.12.0
|
||||
- markdownlint@0.36.0
|
||||
- osv-scanner@1.3.6
|
||||
- prettier@3.0.3
|
||||
- pylint@2.17.5
|
||||
- ruff@0.0.287
|
||||
- shellcheck@0.9.0
|
||||
- shfmt@3.6.0
|
||||
- taplo@0.8.1
|
||||
- trivy@0.44.1
|
||||
- trufflehog@3.54.3
|
||||
- yamllint@1.32.0
|
||||
runtimes:
|
||||
enabled:
|
||||
- go@1.21.0
|
||||
- node@18.12.1
|
||||
- python@3.10.8
|
||||
actions:
|
||||
disabled:
|
||||
- trunk-announce
|
||||
- trunk-check-pre-push
|
||||
- trunk-fmt-pre-commit
|
||||
enabled:
|
||||
- trunk-upgrade-available
|
||||
4
Makefile
4
Makefile
@@ -26,11 +26,11 @@ lint:
|
||||
slow:
|
||||
pytest -m unit --durations=5
|
||||
|
||||
proto: FORCE
|
||||
protobufs: FORCE
|
||||
git submodule update --init --recursive
|
||||
git pull --rebase
|
||||
git submodule update --remote --merge
|
||||
./bin/regen-protos.sh
|
||||
./bin/regen-protobufs.sh
|
||||
|
||||
# run the coverage report and open results in a browser
|
||||
cov:
|
||||
|
||||
10
README.md
10
README.md
@@ -2,8 +2,8 @@
|
||||
|
||||
[](https://codecov.io/gh/meshtastic/Meshtastic-python)
|
||||

|
||||
[](https://github.com/meshtastic/Meshtastic-python/actions/workflows/ci.yml)
|
||||
[](https://cla-assistant.io/meshtastic/Meshtastic-python)
|
||||
[](https://github.com/meshtastic/python/actions/workflows/ci.yml)
|
||||
[](https://cla-assistant.io/meshtastic/python)
|
||||
[](https://opencollective.com/meshtastic/)
|
||||
[](https://vercel.com?utm_source=meshtastic&utm_campaign=oss)
|
||||
|
||||
@@ -14,12 +14,10 @@ This small library (and example application) provides an easy API for sending an
|
||||
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.
|
||||
|
||||
|
||||
**[Getting Started Guide](https://meshtastic.org/docs/software/python/python-installation)**
|
||||
**[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)**
|
||||
|
||||
**[Documentation/API Reference](https://python.meshtastic.org/)**
|
||||
|
||||
|
||||
## Stats
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -6,14 +6,14 @@ version_filename = "setup.py"
|
||||
|
||||
lines = None
|
||||
|
||||
with open(version_filename, 'r', encoding='utf-8') as f:
|
||||
with open(version_filename, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
with open(version_filename, 'w', encoding='utf-8') as f:
|
||||
with open(version_filename, "w", encoding="utf-8") as f:
|
||||
for line in lines:
|
||||
if line.lstrip().startswith("version="):
|
||||
# get rid of quotes around the version
|
||||
line = line.replace('"', '')
|
||||
line = line.replace('"', "")
|
||||
# get rid of trailing comma
|
||||
line = line.replace(",", "")
|
||||
# split on '='
|
||||
@@ -21,8 +21,10 @@ with open(version_filename, 'w', encoding='utf-8') as f:
|
||||
# split the version into parts (by period)
|
||||
v = words[1].split(".")
|
||||
build_num = re.findall(r"\d+", v[2])[0]
|
||||
new_build_num = str(int(build_num)+1)
|
||||
ver = f'{v[0]}.{v[1]}.{v[2].replace(build_num, new_build_num)}'.replace('\n', '')
|
||||
new_build_num = str(int(build_num) + 1)
|
||||
ver = f"{v[0]}.{v[1]}.{v[2].replace(build_num, new_build_num)}".replace(
|
||||
"\n", ""
|
||||
)
|
||||
f.write(f' version="{ver}",\n')
|
||||
else:
|
||||
f.write(line)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Note: Docs are generated from this command below, albeit from Vercel.
|
||||
# The docs/ dir is not used and is no longer commited.
|
||||
# The docs/ dir is not used and is no longer committed.
|
||||
# see sachaw if you have questions
|
||||
pdoc3 --html -f --output-dir docs meshtastic
|
||||
|
||||
19
bin/regen-protobufs.sh
Executable file
19
bin/regen-protobufs.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
#Uncomment to run hack
|
||||
#gsed -i 's/import "\//import ".\//g' ./protobufs/meshtastic/*
|
||||
#gsed -i 's/package meshtastic;//g' ./protobufs/meshtastic/*
|
||||
|
||||
./nanopb-0.4.7/generator-bin/protoc -I=protobufs --python_out ./ ./protobufs/meshtastic/*.proto
|
||||
|
||||
# workaround for import bug in protoc https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-690618628
|
||||
|
||||
if [[ $OSTYPE == 'darwin'* ]]; then
|
||||
sed -i '' -E 's/^(import.*_pb2)/from . \1/' meshtastic/*.py
|
||||
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/protobufs/issues/27)
|
||||
sed -i '' -E "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
|
||||
else
|
||||
sed -i -e 's/^import.*_pb2/from . \0/' meshtastic/*.py
|
||||
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/protobufs/issues/27)
|
||||
sed -i -e "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
|
||||
fi
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
./nanopb-0.4.6/generator-bin/protoc -I=proto --python_out meshtastic `ls proto/*.proto`
|
||||
|
||||
# workaround for import bug in protoc https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-690618628
|
||||
|
||||
if [[ $OSTYPE == 'darwin'* ]]; then
|
||||
sed -i '' -E 's/^(import.*_pb2)/from . \1/' meshtastic/*.py
|
||||
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
|
||||
sed -i '' -E "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
|
||||
else
|
||||
sed -i -e 's/^import.*_pb2/from . \0/' meshtastic/*.py
|
||||
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
|
||||
sed -i -e "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
|
||||
fi
|
||||
@@ -5,16 +5,16 @@ version_filename = "setup.py"
|
||||
|
||||
lines = None
|
||||
|
||||
with open(version_filename, 'r', encoding='utf-8') as f:
|
||||
with open(version_filename, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for line in lines:
|
||||
if line.lstrip().startswith("version="):
|
||||
# get rid of quotes around the version
|
||||
line2 = line.replace('"', '')
|
||||
line2 = line.replace('"', "")
|
||||
# get rid of the trailing comma
|
||||
line2 = line2.replace(',', '')
|
||||
line2 = line2.replace(",", "")
|
||||
# split on =
|
||||
words = line2.split("=")
|
||||
# Note: This format is for github actions
|
||||
print(f'::set-output name=version::{words[1].strip()}')
|
||||
print(f"::set-output name=version::{words[1].strip()}")
|
||||
|
||||
@@ -7,4 +7,4 @@ python3 setup.py sdist bdist_wheel
|
||||
python3 -m twine check dist/*
|
||||
# test the upload
|
||||
python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
|
||||
echo "view the upload at https://test.pypi.org/ it it looks good upload for real"
|
||||
echo "view the upload at https://test.pypi.org/ it it looks good upload for real"
|
||||
|
||||
@@ -11,6 +11,6 @@ location:
|
||||
|
||||
userPrefs:
|
||||
region: 1
|
||||
isAlwaysPowered: 'true'
|
||||
isAlwaysPowered: "true"
|
||||
screenOnSecs: 31536000
|
||||
waitBluetoothSecs: 31536000
|
||||
|
||||
@@ -9,7 +9,7 @@ location:
|
||||
lon: -93.88888
|
||||
alt: 304
|
||||
|
||||
config:
|
||||
config:
|
||||
bluetooth:
|
||||
enabled: true
|
||||
fixedPin: 123456
|
||||
@@ -36,7 +36,7 @@ config:
|
||||
meshSdsTimeoutSecs: 7200
|
||||
minWakeSecs: 10
|
||||
sdsSecs: 4294967295
|
||||
|
||||
|
||||
module_config:
|
||||
telemetry:
|
||||
deviceUpdateInterval: 900
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import meshtastic
|
||||
import meshtastic.serial_interface
|
||||
|
||||
@@ -15,6 +16,6 @@ if len(sys.argv) != 1:
|
||||
iface = meshtastic.serial_interface.SerialInterface()
|
||||
if iface.nodes:
|
||||
for n in iface.nodes.values():
|
||||
if n['num'] == iface.myInfo.my_node_num:
|
||||
print(n['user']['hwModel'])
|
||||
if n["num"] == iface.myInfo.my_node_num:
|
||||
print(n["user"]["hwModel"])
|
||||
iface.close()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import meshtastic
|
||||
import meshtastic.serial_interface
|
||||
|
||||
|
||||
@@ -8,16 +8,13 @@ import meshtastic.serial_interface
|
||||
iface = meshtastic.serial_interface.SerialInterface()
|
||||
|
||||
# call showInfo() just to ensure values are populated
|
||||
#info = iface.showInfo()
|
||||
# info = iface.showInfo()
|
||||
|
||||
if iface.myInfo:
|
||||
#print(f'myInfo:{iface.myInfo}')
|
||||
print(f'firmware_version:{iface.myInfo.firmware_version}')
|
||||
|
||||
if iface.nodes:
|
||||
for n in iface.nodes.values():
|
||||
if n['num'] == iface.myInfo.my_node_num:
|
||||
print(n['user']['hwModel'])
|
||||
if n["num"] == iface.myInfo.my_node_num:
|
||||
print(n["user"]["hwModel"])
|
||||
break
|
||||
|
||||
iface.close()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from pubsub import pub
|
||||
|
||||
import meshtastic
|
||||
@@ -13,10 +14,13 @@ if len(sys.argv) < 2:
|
||||
print(f"usage: {sys.argv[0]} host")
|
||||
sys.exit(1)
|
||||
|
||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||
|
||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||
"""This is called when we (re)connect to the radio."""
|
||||
print(interface.myInfo)
|
||||
interface.close()
|
||||
|
||||
|
||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||
|
||||
try:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
from pubsub import pub
|
||||
|
||||
import meshtastic
|
||||
@@ -14,15 +15,18 @@ if len(sys.argv) < 2:
|
||||
print(f"usage: {sys.argv[0]} host")
|
||||
sys.exit(1)
|
||||
|
||||
def onReceive(packet, interface): # pylint: disable=unused-argument
|
||||
|
||||
def onReceive(packet, interface): # pylint: disable=unused-argument
|
||||
"""called when a packet arrives"""
|
||||
print(f"Received: {packet}")
|
||||
|
||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||
|
||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||
"""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")
|
||||
try:
|
||||
@@ -30,6 +34,6 @@ try:
|
||||
while True:
|
||||
time.sleep(1000)
|
||||
iface.close()
|
||||
except(Exception) as ex:
|
||||
except Exception as ex:
|
||||
print(f"Error: Could not connect to {sys.argv[1]} {ex}")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
from meshtastic.util import detect_supported_devices, get_unique_vendor_ids, active_ports_on_supported_devices
|
||||
|
||||
from meshtastic.util import (
|
||||
active_ports_on_supported_devices,
|
||||
detect_supported_devices,
|
||||
get_unique_vendor_ids,
|
||||
)
|
||||
|
||||
# simple arg check
|
||||
if len(sys.argv) != 1:
|
||||
@@ -12,13 +17,13 @@ if len(sys.argv) != 1:
|
||||
sys.exit(3)
|
||||
|
||||
vids = get_unique_vendor_ids()
|
||||
print(f'Searching for all devices with these vendor ids {vids}')
|
||||
print(f"Searching for all devices with these vendor ids {vids}")
|
||||
|
||||
sds = detect_supported_devices()
|
||||
if len(sds) > 0:
|
||||
print('Detected possible devices:')
|
||||
print("Detected possible devices:")
|
||||
for d in sds:
|
||||
print(f' name:{d.name}{d.version} firmware:{d.for_firmware}')
|
||||
print(f" name:{d.name}{d.version} firmware:{d.for_firmware}")
|
||||
|
||||
ports = active_ports_on_supported_devices(sds)
|
||||
print(f'ports:{ports}')
|
||||
print(f"ports:{ports}")
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import meshtastic
|
||||
import meshtastic.serial_interface
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@ radio_hostname = "meshtastic.local" # Can also be an IP
|
||||
iface = meshtastic.tcp_interface.TCPInterface(radio_hostname)
|
||||
my_node_num = iface.myInfo.my_node_num
|
||||
pos = iface.nodesByNum[my_node_num]["position"]
|
||||
print (pos)
|
||||
print(pos)
|
||||
|
||||
iface.close()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
Primary class: SerialInterface
|
||||
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
||||
Source code on [github](https://github.com/meshtastic/Meshtastic-python)
|
||||
Source code on [github](https://github.com/meshtastic/python)
|
||||
|
||||
properties of SerialInterface:
|
||||
|
||||
@@ -62,34 +62,43 @@ import os
|
||||
import platform
|
||||
import random
|
||||
import socket
|
||||
import sys
|
||||
import stat
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from typing import *
|
||||
|
||||
import google.protobuf.json_format
|
||||
import serial
|
||||
import timeago
|
||||
import google.protobuf.json_format
|
||||
from pubsub import pub
|
||||
from dotmap import DotMap
|
||||
from tabulate import tabulate
|
||||
from google.protobuf.json_format import MessageToJson
|
||||
from meshtastic.util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
||||
from meshtastic.node import Node
|
||||
from meshtastic import (mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2,
|
||||
telemetry_pb2, remote_hardware_pb2,
|
||||
channel_pb2, config_pb2, util)
|
||||
from pubsub import pub
|
||||
from tabulate import tabulate
|
||||
|
||||
from meshtastic import (
|
||||
admin_pb2,
|
||||
apponly_pb2,
|
||||
channel_pb2,
|
||||
config_pb2,
|
||||
mesh_pb2,
|
||||
portnums_pb2,
|
||||
remote_hardware_pb2,
|
||||
telemetry_pb2,
|
||||
util,
|
||||
)
|
||||
from meshtastic.node import Node
|
||||
from meshtastic.util import DeferredExecution, Timeout, catchAndIgnore, fixme, stripnl
|
||||
|
||||
# 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_NUM = 0xFFFFFFFF
|
||||
"""if using 8 bit nodenums this will be shortened on the target"""
|
||||
|
||||
BROADCAST_ADDR = "^all"
|
||||
"""A special ID that means broadcast"""
|
||||
@@ -106,6 +115,7 @@ 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
|
||||
@@ -113,6 +123,7 @@ class ResponseHandler(NamedTuple):
|
||||
|
||||
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
|
||||
@@ -129,7 +140,7 @@ def _onTextReceive(iface, asDict):
|
||||
#
|
||||
# Usually btw this problem is caused by apps sending binary data but setting the payload type to
|
||||
# text.
|
||||
logging.debug(f'in _onTextReceive() asDict:{asDict}')
|
||||
logging.debug(f"in _onTextReceive() asDict:{asDict}")
|
||||
try:
|
||||
asBytes = asDict["decoded"]["payload"]
|
||||
asDict["decoded"]["text"] = asBytes.decode("utf-8")
|
||||
@@ -140,28 +151,28 @@ def _onTextReceive(iface, asDict):
|
||||
|
||||
def _onPositionReceive(iface, asDict):
|
||||
"""Special auto parsing for received messages"""
|
||||
logging.debug(f'in _onPositionReceive() asDict:{asDict}')
|
||||
if 'decoded' in asDict:
|
||||
if 'position' in asDict['decoded'] and 'from' in asDict:
|
||||
logging.debug(f"in _onPositionReceive() asDict:{asDict}")
|
||||
if "decoded" in asDict:
|
||||
if "position" in asDict["decoded"] and "from" in asDict:
|
||||
p = asDict["decoded"]["position"]
|
||||
logging.debug(f'p:{p}')
|
||||
logging.debug(f"p:{p}")
|
||||
p = iface._fixupPosition(p)
|
||||
logging.debug(f'after fixup p:{p}')
|
||||
logging.debug(f"after fixup p:{p}")
|
||||
# update node DB as needed
|
||||
iface._getOrCreateByNum(asDict["from"])["position"] = p
|
||||
|
||||
|
||||
def _onNodeInfoReceive(iface, asDict):
|
||||
"""Special auto parsing for received messages"""
|
||||
logging.debug(f'in _onNodeInfoReceive() asDict:{asDict}')
|
||||
if 'decoded' in asDict:
|
||||
if 'user' in asDict['decoded'] and 'from' in asDict:
|
||||
logging.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
|
||||
if "decoded" in asDict:
|
||||
if "user" in asDict["decoded"] and "from" in asDict:
|
||||
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
|
||||
# We now have a node ID, make sure it is up-to-date in that table
|
||||
iface.nodes[p["id"]] = n
|
||||
_receiveInfoUpdate(iface, asDict)
|
||||
|
||||
@@ -176,12 +187,25 @@ def _receiveInfoUpdate(iface, asDict):
|
||||
|
||||
"""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.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.TELEMETRY_APP: KnownProtocol("telemetry", telemetry_pb2.Telemetry),
|
||||
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol("remotehw", remote_hardware_pb2.HardwareMessage),
|
||||
portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed)
|
||||
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol(
|
||||
"telemetry", telemetry_pb2.Telemetry
|
||||
),
|
||||
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
|
||||
"remotehw", remote_hardware_pb2.HardwareMessage
|
||||
),
|
||||
portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed),
|
||||
portnums_pb2.PortNum.TRACEROUTE_APP: KnownProtocol(
|
||||
"traceroute", mesh_pb2.RouteDiscovery
|
||||
),
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,46 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: admin.proto
|
||||
# source: meshtastic/admin.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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 config_pb2 as config__pb2
|
||||
from . import device_metadata_pb2 as device__metadata__pb2
|
||||
from . import mesh_pb2 as mesh__pb2
|
||||
from . import module_config_pb2 as module__config__pb2
|
||||
from meshtastic import channel_pb2 as meshtastic_dot_channel__pb2
|
||||
from meshtastic import config_pb2 as meshtastic_dot_config__pb2
|
||||
from meshtastic import connection_status_pb2 as meshtastic_dot_connection__status__pb2
|
||||
from meshtastic import deviceonly_pb2 as meshtastic_dot_deviceonly__pb2
|
||||
from meshtastic import mesh_pb2 as meshtastic_dot_mesh__pb2
|
||||
from meshtastic import module_config_pb2 as meshtastic_dot_module__config__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61\x64min.proto\x1a\rchannel.proto\x1a\x0c\x63onfig.proto\x1a\x15\x64\x65vice_metadata.proto\x1a\nmesh.proto\x1a\x13module_config.proto\"\xe8\n\n\x0c\x41\x64minMessage\x12\x1d\n\x13get_channel_request\x18\x01 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x02 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x03 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\x04 \x01(\x0b\x32\x05.UserH\x00\x12\x36\n\x12get_config_request\x18\x05 \x01(\x0e\x32\x18.AdminMessage.ConfigTypeH\x00\x12&\n\x13get_config_response\x18\x06 \x01(\x0b\x32\x07.ConfigH\x00\x12\x43\n\x19get_module_config_request\x18\x07 \x01(\x0e\x32\x1e.AdminMessage.ModuleConfigTypeH\x00\x12\x33\n\x1aget_module_config_response\x18\x08 \x01(\x0b\x32\r.ModuleConfigH\x00\x12\x34\n*get_canned_message_module_messages_request\x18\n \x01(\x08H\x00\x12\x35\n+get_canned_message_module_messages_response\x18\x0b \x01(\tH\x00\x12%\n\x1bget_device_metadata_request\x18\x0c \x01(\x08H\x00\x12\x37\n\x1cget_device_metadata_response\x18\r \x01(\x0b\x32\x0f.DeviceMetadataH\x00\x12\x1a\n\tset_owner\x18 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18! \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\nset_config\x18\" \x01(\x0b\x32\x07.ConfigH\x00\x12*\n\x11set_module_config\x18# \x01(\x0b\x32\r.ModuleConfigH\x00\x12,\n\"set_canned_message_module_messages\x18$ \x01(\tH\x00\x12\x1c\n\x12\x63onfirm_set_config\x18@ \x01(\x08H\x00\x12#\n\x19\x63onfirm_set_module_config\x18\x41 \x01(\x08H\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18\x42 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18\x43 \x01(\x08H\x00\x12\x1c\n\x12reboot_ota_seconds\x18_ \x01(\x05H\x00\x12\x18\n\x0e\x65xit_simulator\x18` \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18\x61 \x01(\x05H\x00\x12\x1a\n\x10shutdown_seconds\x18\x62 \x01(\x05H\x00\x12\x17\n\rfactory_reset\x18\x63 \x01(\x05H\x00\x12\x16\n\x0cnodedb_reset\x18\x64 \x01(\x05H\x00\"\x95\x01\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x12\n\x0eNETWORK_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\x12\x14\n\x10\x42LUETOOTH_CONFIG\x10\x06\"\xa6\x01\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x42\x11\n\x0fpayload_variantBG\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_ADMINMESSAGE = DESCRIPTOR.message_types_by_name['AdminMessage']
|
||||
_ADMINMESSAGE_CONFIGTYPE = _ADMINMESSAGE.enum_types_by_name['ConfigType']
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE = _ADMINMESSAGE.enum_types_by_name['ModuleConfigType']
|
||||
AdminMessage = _reflection.GeneratedProtocolMessageType('AdminMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ADMINMESSAGE,
|
||||
'__module__' : 'admin_pb2'
|
||||
# @@protoc_insertion_point(class_scope:AdminMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(AdminMessage)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16meshtastic/admin.proto\x1a\x18meshtastic/channel.proto\x1a\x17meshtastic/config.proto\x1a\"meshtastic/connection_status.proto\x1a\x1bmeshtastic/deviceonly.proto\x1a\x15meshtastic/mesh.proto\x1a\x1emeshtastic/module_config.proto\"\x83\x0f\n\x0c\x41\x64minMessage\x12\x1d\n\x13get_channel_request\x18\x01 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x02 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x03 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\x04 \x01(\x0b\x32\x05.UserH\x00\x12\x36\n\x12get_config_request\x18\x05 \x01(\x0e\x32\x18.AdminMessage.ConfigTypeH\x00\x12&\n\x13get_config_response\x18\x06 \x01(\x0b\x32\x07.ConfigH\x00\x12\x43\n\x19get_module_config_request\x18\x07 \x01(\x0e\x32\x1e.AdminMessage.ModuleConfigTypeH\x00\x12\x33\n\x1aget_module_config_response\x18\x08 \x01(\x0b\x32\r.ModuleConfigH\x00\x12\x34\n*get_canned_message_module_messages_request\x18\n \x01(\x08H\x00\x12\x35\n+get_canned_message_module_messages_response\x18\x0b \x01(\tH\x00\x12%\n\x1bget_device_metadata_request\x18\x0c \x01(\x08H\x00\x12\x37\n\x1cget_device_metadata_response\x18\r \x01(\x0b\x32\x0f.DeviceMetadataH\x00\x12\x1e\n\x14get_ringtone_request\x18\x0e \x01(\x08H\x00\x12\x1f\n\x15get_ringtone_response\x18\x0f \x01(\tH\x00\x12.\n$get_device_connection_status_request\x18\x10 \x01(\x08H\x00\x12H\n%get_device_connection_status_response\x18\x11 \x01(\x0b\x32\x17.DeviceConnectionStatusH\x00\x12&\n\x0cset_ham_mode\x18\x12 \x01(\x0b\x32\x0e.HamParametersH\x00\x12/\n%get_node_remote_hardware_pins_request\x18\x13 \x01(\x08H\x00\x12Q\n&get_node_remote_hardware_pins_response\x18\x14 \x01(\x0b\x32\x1f.NodeRemoteHardwarePinsResponseH\x00\x12 \n\x16\x65nter_dfu_mode_request\x18\x15 \x01(\x08H\x00\x12\x1a\n\tset_owner\x18 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18! \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\nset_config\x18\" \x01(\x0b\x32\x07.ConfigH\x00\x12*\n\x11set_module_config\x18# \x01(\x0b\x32\r.ModuleConfigH\x00\x12,\n\"set_canned_message_module_messages\x18$ \x01(\tH\x00\x12\x1e\n\x14set_ringtone_message\x18% \x01(\tH\x00\x12\x1b\n\x11remove_by_nodenum\x18& \x01(\rH\x00\x12\x1d\n\x13\x62\x65gin_edit_settings\x18@ \x01(\x08H\x00\x12\x1e\n\x14\x63ommit_edit_settings\x18\x41 \x01(\x08H\x00\x12\x1c\n\x12reboot_ota_seconds\x18_ \x01(\x05H\x00\x12\x18\n\x0e\x65xit_simulator\x18` \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18\x61 \x01(\x05H\x00\x12\x1a\n\x10shutdown_seconds\x18\x62 \x01(\x05H\x00\x12\x17\n\rfactory_reset\x18\x63 \x01(\x05H\x00\x12\x16\n\x0cnodedb_reset\x18\x64 \x01(\x05H\x00\"\x95\x01\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x12\n\x0eNETWORK_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\x12\x14\n\x10\x42LUETOOTH_CONFIG\x10\x06\"\xbb\x02\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x12\x10\n\x0c\x41UDIO_CONFIG\x10\x07\x12\x19\n\x15REMOTEHARDWARE_CONFIG\x10\x08\x12\x17\n\x13NEIGHBORINFO_CONFIG\x10\t\x12\x1a\n\x16\x41MBIENTLIGHTING_CONFIG\x10\n\x12\x1a\n\x16\x44\x45TECTIONSENSOR_CONFIG\x10\x0b\x12\x15\n\x11PAXCOUNTER_CONFIG\x10\x0c\x42\x11\n\x0fpayload_variant\"[\n\rHamParameters\x12\x11\n\tcall_sign\x18\x01 \x01(\t\x12\x10\n\x08tx_power\x18\x02 \x01(\x05\x12\x11\n\tfrequency\x18\x03 \x01(\x02\x12\x12\n\nshort_name\x18\x04 \x01(\t\"[\n\x1eNodeRemoteHardwarePinsResponse\x12\x39\n\x19node_remote_hardware_pins\x18\x01 \x03(\x0b\x32\x16.NodeRemoteHardwarePinB`\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.admin_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_ADMINMESSAGE._serialized_start=101
|
||||
_ADMINMESSAGE._serialized_end=1485
|
||||
_ADMINMESSAGE_CONFIGTYPE._serialized_start=1148
|
||||
_ADMINMESSAGE_CONFIGTYPE._serialized_end=1297
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_start=1300
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_end=1466
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_ADMINMESSAGE._serialized_start=198
|
||||
_ADMINMESSAGE._serialized_end=2121
|
||||
_ADMINMESSAGE_CONFIGTYPE._serialized_start=1635
|
||||
_ADMINMESSAGE_CONFIGTYPE._serialized_end=1784
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_start=1787
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_end=2102
|
||||
_HAMPARAMETERS._serialized_start=2123
|
||||
_HAMPARAMETERS._serialized_end=2214
|
||||
_NODEREMOTEHARDWAREPINSRESPONSE._serialized_start=2216
|
||||
_NODEREMOTEHARDWAREPINSRESPONSE._serialized_end=2307
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -1,37 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: apponly.proto
|
||||
# source: meshtastic/apponly.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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 config_pb2 as config__pb2
|
||||
from meshtastic import channel_pb2 as meshtastic_dot_channel__pb2
|
||||
from meshtastic import config_pb2 as meshtastic_dot_config__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rapponly.proto\x1a\rchannel.proto\x1a\x0c\x63onfig.proto\"Y\n\nChannelSet\x12\"\n\x08settings\x18\x01 \x03(\x0b\x32\x10.ChannelSettings\x12\'\n\x0blora_config\x18\x02 \x01(\x0b\x32\x12.Config.LoRaConfigBI\n\x13\x63om.geeksville.meshB\rAppOnlyProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_CHANNELSET = DESCRIPTOR.message_types_by_name['ChannelSet']
|
||||
ChannelSet = _reflection.GeneratedProtocolMessageType('ChannelSet', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CHANNELSET,
|
||||
'__module__' : 'apponly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ChannelSet)
|
||||
})
|
||||
_sym_db.RegisterMessage(ChannelSet)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18meshtastic/apponly.proto\x1a\x18meshtastic/channel.proto\x1a\x17meshtastic/config.proto\"Y\n\nChannelSet\x12\"\n\x08settings\x18\x01 \x03(\x0b\x32\x10.ChannelSettings\x12\'\n\x0blora_config\x18\x02 \x01(\x0b\x32\x12.Config.LoRaConfigBb\n\x13\x63om.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.apponly_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_CHANNELSET._serialized_start=46
|
||||
_CHANNELSET._serialized_end=135
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_CHANNELSET._serialized_start=79
|
||||
_CHANNELSET._serialized_end=168
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -6,12 +6,11 @@ import platform
|
||||
from meshtastic.mesh_interface import MeshInterface
|
||||
from meshtastic.util import our_exit
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
if platform.system() == "Linux":
|
||||
# pylint: disable=E0401
|
||||
import pygatt
|
||||
|
||||
|
||||
|
||||
# Our standard BLE characteristics
|
||||
TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
||||
FROMRADIO_UUID = "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
|
||||
@@ -22,7 +21,7 @@ class BLEInterface(MeshInterface):
|
||||
"""A not quite ready - FIXME - BLE interface to devices"""
|
||||
|
||||
def __init__(self, address, noProto=False, debugOut=None):
|
||||
if platform.system() != 'Linux':
|
||||
if platform.system() != "Linux":
|
||||
our_exit("Linux is the only platform with experimental BLE support.", 1)
|
||||
self.address = address
|
||||
if not noProto:
|
||||
@@ -39,7 +38,7 @@ class BLEInterface(MeshInterface):
|
||||
|
||||
self._readFromRadio() # read the initial responses
|
||||
|
||||
def handle_data(handle, data): # pylint: disable=W0613
|
||||
def handle_data(handle, data): # pylint: disable=W0613
|
||||
self._handleFromRadio(data)
|
||||
|
||||
if self.device:
|
||||
@@ -47,7 +46,7 @@ class BLEInterface(MeshInterface):
|
||||
|
||||
def _sendToRadioImpl(self, toRadio):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
#logging.debug(f"Sending: {stripnl(toRadio)}")
|
||||
# logging.debug(f"Sending: {stripnl(toRadio)}")
|
||||
b = toRadio.SerializeToString()
|
||||
self.device.char_write(TORADIO_UUID, b)
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: cannedmessages.proto
|
||||
# source: meshtastic/cannedmessages.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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)
|
||||
|
||||
@@ -14,22 +13,14 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x63\x61nnedmessages.proto\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBU\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_CANNEDMESSAGEMODULECONFIG = DESCRIPTOR.message_types_by_name['CannedMessageModuleConfig']
|
||||
CannedMessageModuleConfig = _reflection.GeneratedProtocolMessageType('CannedMessageModuleConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CANNEDMESSAGEMODULECONFIG,
|
||||
'__module__' : 'cannedmessages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:CannedMessageModuleConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(CannedMessageModuleConfig)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/cannedmessages.proto\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBn\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.cannedmessages_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_CANNEDMESSAGEMODULECONFIG._serialized_start=24
|
||||
_CANNEDMESSAGEMODULECONFIG._serialized_end=69
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_CANNEDMESSAGEMODULECONFIG._serialized_start=35
|
||||
_CANNEDMESSAGEMODULECONFIG._serialized_end=80
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: channel.proto
|
||||
# source: meshtastic/channel.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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)
|
||||
|
||||
@@ -14,37 +13,20 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rchannel.proto\"\x83\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\"\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 = DESCRIPTOR.message_types_by_name['ChannelSettings']
|
||||
_CHANNEL = DESCRIPTOR.message_types_by_name['Channel']
|
||||
_CHANNEL_ROLE = _CHANNEL.enum_types_by_name['Role']
|
||||
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 = _descriptor_pool.Default().AddSerializedFile(b'\n\x18meshtastic/channel.proto\"\x83\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\"\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\x42\x62\n\x13\x63om.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.channel_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_CHANNELSETTINGS.fields_by_name['channel_num']._options = None
|
||||
_CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001'
|
||||
_CHANNELSETTINGS._serialized_start=18
|
||||
_CHANNELSETTINGS._serialized_end=149
|
||||
_CHANNEL._serialized_start=152
|
||||
_CHANNEL._serialized_end=291
|
||||
_CHANNEL_ROLE._serialized_start=243
|
||||
_CHANNEL_ROLE._serialized_end=291
|
||||
_CHANNELSETTINGS._serialized_start=29
|
||||
_CHANNELSETTINGS._serialized_end=160
|
||||
_CHANNEL._serialized_start=163
|
||||
_CHANNEL._serialized_end=302
|
||||
_CHANNEL_ROLE._serialized_start=254
|
||||
_CHANNEL_ROLE._serialized_end=302
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
27
meshtastic/clientonly_pb2.py
Normal file
27
meshtastic/clientonly_pb2.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: meshtastic/clientonly.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from meshtastic import localonly_pb2 as meshtastic_dot_localonly__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bmeshtastic/clientonly.proto\x1a\x1ameshtastic/localonly.proto\"\xf7\x01\n\rDeviceProfile\x12\x16\n\tlong_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nshort_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hannel_url\x18\x03 \x01(\tH\x02\x88\x01\x01\x12!\n\x06\x63onfig\x18\x04 \x01(\x0b\x32\x0c.LocalConfigH\x03\x88\x01\x01\x12.\n\rmodule_config\x18\x05 \x01(\x0b\x32\x12.LocalModuleConfigH\x04\x88\x01\x01\x42\x0c\n\n_long_nameB\r\n\x0b_short_nameB\x0e\n\x0c_channel_urlB\t\n\x07_configB\x10\n\x0e_module_configBe\n\x13\x63om.geeksville.meshB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.clientonly_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_DEVICEPROFILE._serialized_start=60
|
||||
_DEVICEPROFILE._serialized_end=307
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
File diff suppressed because one or more lines are too long
36
meshtastic/connection_status_pb2.py
Normal file
36
meshtastic/connection_status_pb2.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: meshtastic/connection_status.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/connection_status.proto\"\x85\x02\n\x16\x44\x65viceConnectionStatus\x12(\n\x04wifi\x18\x01 \x01(\x0b\x32\x15.WifiConnectionStatusH\x00\x88\x01\x01\x12\x30\n\x08\x65thernet\x18\x02 \x01(\x0b\x32\x19.EthernetConnectionStatusH\x01\x88\x01\x01\x12\x32\n\tbluetooth\x18\x03 \x01(\x0b\x32\x1a.BluetoothConnectionStatusH\x02\x88\x01\x01\x12,\n\x06serial\x18\x04 \x01(\x0b\x32\x17.SerialConnectionStatusH\x03\x88\x01\x01\x42\x07\n\x05_wifiB\x0b\n\t_ethernetB\x0c\n\n_bluetoothB\t\n\x07_serial\"\\\n\x14WifiConnectionStatus\x12(\n\x06status\x18\x01 \x01(\x0b\x32\x18.NetworkConnectionStatus\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\"D\n\x18\x45thernetConnectionStatus\x12(\n\x06status\x18\x01 \x01(\x0b\x32\x18.NetworkConnectionStatus\"{\n\x17NetworkConnectionStatus\x12\x12\n\nip_address\x18\x01 \x01(\x07\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x12\x19\n\x11is_mqtt_connected\x18\x03 \x01(\x08\x12\x1b\n\x13is_syslog_connected\x18\x04 \x01(\x08\"L\n\x19\x42luetoothConnectionStatus\x12\x0b\n\x03pin\x18\x01 \x01(\r\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\x12\x14\n\x0cis_connected\x18\x03 \x01(\x08\"<\n\x16SerialConnectionStatus\x12\x0c\n\x04\x62\x61ud\x18\x01 \x01(\r\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x42\x65\n\x13\x63om.geeksville.meshB\x10\x43onnStatusProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.connection_status_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_DEVICECONNECTIONSTATUS._serialized_start=39
|
||||
_DEVICECONNECTIONSTATUS._serialized_end=300
|
||||
_WIFICONNECTIONSTATUS._serialized_start=302
|
||||
_WIFICONNECTIONSTATUS._serialized_end=394
|
||||
_ETHERNETCONNECTIONSTATUS._serialized_start=396
|
||||
_ETHERNETCONNECTIONSTATUS._serialized_end=464
|
||||
_NETWORKCONNECTIONSTATUS._serialized_start=466
|
||||
_NETWORKCONNECTIONSTATUS._serialized_end=589
|
||||
_BLUETOOTHCONNECTIONSTATUS._serialized_start=591
|
||||
_BLUETOOTHCONNECTIONSTATUS._serialized_end=667
|
||||
_SERIALCONNECTIONSTATUS._serialized_start=669
|
||||
_SERIALCONNECTIONSTATUS._serialized_end=729
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: device_metadata.proto
|
||||
# source: meshtastic/device_metadata.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
@@ -12,16 +12,18 @@ from google.protobuf import symbol_database as _symbol_database
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from meshtastic import config_pb2 as meshtastic_dot_config__pb2
|
||||
from meshtastic import mesh_pb2 as meshtastic_dot_mesh__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x64\x65vice_metadata.proto\"H\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\rBP\n\x13\x63om.geeksville.meshB\x14\x44\x65viceMetadataProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/device_metadata.proto\x1a\x17meshtastic/config.proto\x1a\x15meshtastic/mesh.proto\"\xfc\x01\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\r\x12\x13\n\x0b\x63\x61nShutdown\x18\x03 \x01(\x08\x12\x0f\n\x07hasWifi\x18\x04 \x01(\x08\x12\x14\n\x0chasBluetooth\x18\x05 \x01(\x08\x12\x13\n\x0bhasEthernet\x18\x06 \x01(\x08\x12\'\n\x04role\x18\x07 \x01(\x0e\x32\x19.Config.DeviceConfig.Role\x12\x16\n\x0eposition_flags\x18\x08 \x01(\r\x12 \n\x08hw_model\x18\t \x01(\x0e\x32\x0e.HardwareModelBi\n\x13\x63om.geeksville.meshB\x14\x44\x65viceMetadataProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
|
||||
|
||||
_DEVICEMETADATA = DESCRIPTOR.message_types_by_name['DeviceMetadata']
|
||||
DeviceMetadata = _reflection.GeneratedProtocolMessageType('DeviceMetadata', (_message.Message,), {
|
||||
'DESCRIPTOR' : _DEVICEMETADATA,
|
||||
'__module__' : 'device_metadata_pb2'
|
||||
'__module__' : 'meshtastic.device_metadata_pb2'
|
||||
# @@protoc_insertion_point(class_scope:DeviceMetadata)
|
||||
})
|
||||
_sym_db.RegisterMessage(DeviceMetadata)
|
||||
@@ -29,7 +31,7 @@ _sym_db.RegisterMessage(DeviceMetadata)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\024DeviceMetadataProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_DEVICEMETADATA._serialized_start=25
|
||||
_DEVICEMETADATA._serialized_end=97
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\024DeviceMetadataProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_DEVICEMETADATA._serialized_start=85
|
||||
_DEVICEMETADATA._serialized_end=337
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -1,65 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: deviceonly.proto
|
||||
# source: meshtastic/deviceonly.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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 meshtastic import channel_pb2 as meshtastic_dot_channel__pb2
|
||||
from meshtastic import localonly_pb2 as meshtastic_dot_localonly__pb2
|
||||
from meshtastic import mesh_pb2 as meshtastic_dot_mesh__pb2
|
||||
from meshtastic import telemetry_pb2 as meshtastic_dot_telemetry__pb2
|
||||
from meshtastic import module_config_pb2 as meshtastic_dot_module__config__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x64\x65viceonly.proto\x1a\rchannel.proto\x1a\nmesh.proto\"\xe0\x01\n\x0b\x44\x65viceState\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(\x08\":\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\x84\x01\n\x08OEMStore\x12\x16\n\x0eoem_icon_width\x18\x01 \x01(\r\x12\x17\n\x0foem_icon_height\x18\x02 \x01(\r\x12\x15\n\roem_icon_bits\x18\x03 \x01(\x0c\x12\x1e\n\x08oem_font\x18\x04 \x01(\x0e\x32\x0c.ScreenFonts\x12\x10\n\x08oem_text\x18\x05 \x01(\t*>\n\x0bScreenFonts\x12\x0e\n\nFONT_SMALL\x10\x00\x12\x0f\n\x0b\x46ONT_MEDIUM\x10\x01\x12\x0e\n\nFONT_LARGE\x10\x02\x42\x46\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
_SCREENFONTS = DESCRIPTOR.enum_types_by_name['ScreenFonts']
|
||||
ScreenFonts = enum_type_wrapper.EnumTypeWrapper(_SCREENFONTS)
|
||||
FONT_SMALL = 0
|
||||
FONT_MEDIUM = 1
|
||||
FONT_LARGE = 2
|
||||
|
||||
|
||||
_DEVICESTATE = DESCRIPTOR.message_types_by_name['DeviceState']
|
||||
_CHANNELFILE = DESCRIPTOR.message_types_by_name['ChannelFile']
|
||||
_OEMSTORE = DESCRIPTOR.message_types_by_name['OEMStore']
|
||||
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)
|
||||
|
||||
OEMStore = _reflection.GeneratedProtocolMessageType('OEMStore', (_message.Message,), {
|
||||
'DESCRIPTOR' : _OEMSTORE,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:OEMStore)
|
||||
})
|
||||
_sym_db.RegisterMessage(OEMStore)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bmeshtastic/deviceonly.proto\x1a\x18meshtastic/channel.proto\x1a\x1ameshtastic/localonly.proto\x1a\x15meshtastic/mesh.proto\x1a\x1ameshtastic/telemetry.proto\x1a\x1emeshtastic/module_config.proto\"\xc6\x02\n\x0b\x44\x65viceState\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\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(\x08\x12 \n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x0b.MeshPacket\x12\x39\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32\x16.NodeRemoteHardwarePin\x12#\n\x0cnode_db_lite\x18\x0e \x03(\x0b\x32\r.NodeInfoLite\"\xab\x01\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x13\n\x04user\x18\x02 \x01(\x0b\x32\x05.User\x12\x1f\n\x08position\x18\x03 \x01(\x0b\x32\r.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12&\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\x0e.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\"\x85\x01\n\x0cPositionLite\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\x0c\n\x04time\x18\x04 \x01(\x07\x12,\n\x0flocation_source\x18\x05 \x01(\x0e\x32\x13.Position.LocSource\":\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\xf6\x01\n\x08OEMStore\x12\x16\n\x0eoem_icon_width\x18\x01 \x01(\r\x12\x17\n\x0foem_icon_height\x18\x02 \x01(\r\x12\x15\n\roem_icon_bits\x18\x03 \x01(\x0c\x12\x1e\n\x08oem_font\x18\x04 \x01(\x0e\x32\x0c.ScreenFonts\x12\x10\n\x08oem_text\x18\x05 \x01(\t\x12\x13\n\x0boem_aes_key\x18\x06 \x01(\x0c\x12&\n\x10oem_local_config\x18\x07 \x01(\x0b\x32\x0c.LocalConfig\x12\x33\n\x17oem_local_module_config\x18\x08 \x01(\x0b\x32\x12.LocalModuleConfig\"J\n\x15NodeRemoteHardwarePin\x12\x10\n\x08node_num\x18\x01 \x01(\r\x12\x1f\n\x03pin\x18\x02 \x01(\x0b\x32\x12.RemoteHardwarePin*>\n\x0bScreenFonts\x12\x0e\n\nFONT_SMALL\x10\x00\x12\x0f\n\x0b\x46ONT_MEDIUM\x10\x01\x12\x0e\n\nFONT_LARGE\x10\x02\x42_\n\x13\x63om.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.deviceonly_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_SCREENFONTS._serialized_start=469
|
||||
_SCREENFONTS._serialized_end=531
|
||||
_DEVICESTATE._serialized_start=48
|
||||
_DEVICESTATE._serialized_end=272
|
||||
_CHANNELFILE._serialized_start=274
|
||||
_CHANNELFILE._serialized_end=332
|
||||
_OEMSTORE._serialized_start=335
|
||||
_OEMSTORE._serialized_end=467
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_SCREENFONTS._serialized_start=1192
|
||||
_SCREENFONTS._serialized_end=1254
|
||||
_DEVICESTATE._serialized_start=169
|
||||
_DEVICESTATE._serialized_end=495
|
||||
_NODEINFOLITE._serialized_start=498
|
||||
_NODEINFOLITE._serialized_end=669
|
||||
_POSITIONLITE._serialized_start=672
|
||||
_POSITIONLITE._serialized_end=805
|
||||
_CHANNELFILE._serialized_start=807
|
||||
_CHANNELFILE._serialized_end=865
|
||||
_OEMSTORE._serialized_start=868
|
||||
_OEMSTORE._serialized_end=1114
|
||||
_NODEREMOTEHARDWAREPIN._serialized_start=1116
|
||||
_NODEREMOTEHARDWAREPIN._serialized_end=1190
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class Globals:
|
||||
"""Globals class is a Singleton."""
|
||||
|
||||
__instance = None
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1,47 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: localonly.proto
|
||||
# source: meshtastic/localonly.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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 config_pb2 as config__pb2
|
||||
from . import module_config_pb2 as module__config__pb2
|
||||
from meshtastic import config_pb2 as meshtastic_dot_config__pb2
|
||||
from meshtastic import module_config_pb2 as meshtastic_dot_module__config__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0flocalonly.proto\x1a\x0c\x63onfig.proto\x1a\x13module_config.proto\"\xb0\x02\n\x0bLocalConfig\x12$\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x14.Config.DeviceConfig\x12(\n\x08position\x18\x02 \x01(\x0b\x32\x16.Config.PositionConfig\x12\"\n\x05power\x18\x03 \x01(\x0b\x32\x13.Config.PowerConfig\x12&\n\x07network\x18\x04 \x01(\x0b\x32\x15.Config.NetworkConfig\x12&\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfig\x12 \n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfig\x12*\n\tbluetooth\x18\x07 \x01(\x0b\x32\x17.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\"\x9a\x03\n\x11LocalModuleConfig\x12&\n\x04mqtt\x18\x01 \x01(\x0b\x32\x18.ModuleConfig.MQTTConfig\x12*\n\x06serial\x18\x02 \x01(\x0b\x32\x1a.ModuleConfig.SerialConfig\x12G\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32(.ModuleConfig.ExternalNotificationConfig\x12\x37\n\rstore_forward\x18\x04 \x01(\x0b\x32 .ModuleConfig.StoreForwardConfig\x12\x31\n\nrange_test\x18\x05 \x01(\x0b\x32\x1d.ModuleConfig.RangeTestConfig\x12\x30\n\ttelemetry\x18\x06 \x01(\x0b\x32\x1d.ModuleConfig.TelemetryConfig\x12\x39\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32!.ModuleConfig.CannedMessageConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBK\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_LOCALCONFIG = DESCRIPTOR.message_types_by_name['LocalConfig']
|
||||
_LOCALMODULECONFIG = DESCRIPTOR.message_types_by_name['LocalModuleConfig']
|
||||
LocalConfig = _reflection.GeneratedProtocolMessageType('LocalConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LOCALCONFIG,
|
||||
'__module__' : 'localonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LocalConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(LocalConfig)
|
||||
|
||||
LocalModuleConfig = _reflection.GeneratedProtocolMessageType('LocalModuleConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LOCALMODULECONFIG,
|
||||
'__module__' : 'localonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LocalModuleConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(LocalModuleConfig)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ameshtastic/localonly.proto\x1a\x17meshtastic/config.proto\x1a\x1emeshtastic/module_config.proto\"\xb0\x02\n\x0bLocalConfig\x12$\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x14.Config.DeviceConfig\x12(\n\x08position\x18\x02 \x01(\x0b\x32\x16.Config.PositionConfig\x12\"\n\x05power\x18\x03 \x01(\x0b\x32\x13.Config.PowerConfig\x12&\n\x07network\x18\x04 \x01(\x0b\x32\x15.Config.NetworkConfig\x12&\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfig\x12 \n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfig\x12*\n\tbluetooth\x18\x07 \x01(\x0b\x32\x17.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\"\xec\x05\n\x11LocalModuleConfig\x12&\n\x04mqtt\x18\x01 \x01(\x0b\x32\x18.ModuleConfig.MQTTConfig\x12*\n\x06serial\x18\x02 \x01(\x0b\x32\x1a.ModuleConfig.SerialConfig\x12G\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32(.ModuleConfig.ExternalNotificationConfig\x12\x37\n\rstore_forward\x18\x04 \x01(\x0b\x32 .ModuleConfig.StoreForwardConfig\x12\x31\n\nrange_test\x18\x05 \x01(\x0b\x32\x1d.ModuleConfig.RangeTestConfig\x12\x30\n\ttelemetry\x18\x06 \x01(\x0b\x32\x1d.ModuleConfig.TelemetryConfig\x12\x39\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32!.ModuleConfig.CannedMessageConfig\x12(\n\x05\x61udio\x18\t \x01(\x0b\x32\x19.ModuleConfig.AudioConfig\x12;\n\x0fremote_hardware\x18\n \x01(\x0b\x32\".ModuleConfig.RemoteHardwareConfig\x12\x37\n\rneighbor_info\x18\x0b \x01(\x0b\x32 .ModuleConfig.NeighborInfoConfig\x12=\n\x10\x61mbient_lighting\x18\x0c \x01(\x0b\x32#.ModuleConfig.AmbientLightingConfig\x12=\n\x10\x64\x65tection_sensor\x18\r \x01(\x0b\x32#.ModuleConfig.DetectionSensorConfig\x12\x32\n\npaxcounter\x18\x0e \x01(\x0b\x32\x1e.ModuleConfig.PaxcounterConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBd\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.localonly_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_LOCALCONFIG._serialized_start=55
|
||||
_LOCALCONFIG._serialized_end=359
|
||||
_LOCALMODULECONFIG._serialized_start=362
|
||||
_LOCALMODULECONFIG._serialized_end=772
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_LOCALCONFIG._serialized_start=88
|
||||
_LOCALCONFIG._serialized_end=392
|
||||
_LOCALMODULECONFIG._serialized_start=395
|
||||
_LOCALMODULECONFIG._serialized_end=1143
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,36 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: mqtt.proto
|
||||
# source: meshtastic/mqtt.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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
|
||||
from meshtastic import mesh_pb2 as meshtastic_dot_mesh__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(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')
|
||||
|
||||
|
||||
|
||||
_SERVICEENVELOPE = DESCRIPTOR.message_types_by_name['ServiceEnvelope']
|
||||
ServiceEnvelope = _reflection.GeneratedProtocolMessageType('ServiceEnvelope', (_message.Message,), {
|
||||
'DESCRIPTOR' : _SERVICEENVELOPE,
|
||||
'__module__' : 'mqtt_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ServiceEnvelope)
|
||||
})
|
||||
_sym_db.RegisterMessage(ServiceEnvelope)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15meshtastic/mqtt.proto\x1a\x15meshtastic/mesh.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(\tB_\n\x13\x63om.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.mqtt_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_SERVICEENVELOPE._serialized_start=26
|
||||
_SERVICEENVELOPE._serialized_end=112
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_SERVICEENVELOPE._serialized_start=48
|
||||
_SERVICEENVELOPE._serialized_end=134
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
"""Node class
|
||||
"""
|
||||
|
||||
import logging
|
||||
import base64
|
||||
import logging
|
||||
import time
|
||||
|
||||
from google.protobuf.json_format import MessageToJson
|
||||
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2, localonly_pb2
|
||||
from meshtastic.util import pskToString, stripnl, Timeout, our_exit, fromPSK
|
||||
|
||||
from meshtastic import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, portnums_pb2
|
||||
from meshtastic.util import (
|
||||
Timeout,
|
||||
camel_to_snake,
|
||||
fromPSK,
|
||||
our_exit,
|
||||
pskToString,
|
||||
stripnl,
|
||||
)
|
||||
|
||||
|
||||
class Node:
|
||||
@@ -26,8 +35,9 @@ class Node:
|
||||
self.partialChannels = None
|
||||
self.noProto = noProto
|
||||
self.cannedPluginMessage = None
|
||||
|
||||
self.cannedPluginMessageMessages = None
|
||||
self.ringtone = None
|
||||
self.ringtonePart = None
|
||||
|
||||
self.gotResponse = None
|
||||
|
||||
@@ -35,13 +45,15 @@ class Node:
|
||||
"""Show human readable description of our channels."""
|
||||
print("Channels:")
|
||||
if self.channels:
|
||||
logging.debug(f'self.channels:{self.channels}')
|
||||
logging.debug(f"self.channels:{self.channels}")
|
||||
for c in self.channels:
|
||||
#print('c.settings.psk:', c.settings.psk)
|
||||
# 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}")
|
||||
# don't show disabled channels
|
||||
if channel_pb2.Channel.Role.Name(c.role) != "DISABLED":
|
||||
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}")
|
||||
@@ -60,126 +72,76 @@ class Node:
|
||||
print(f"Module preferences: {prefs}\n")
|
||||
self.showChannels()
|
||||
|
||||
def requestConfig(self):
|
||||
"""Send regular MeshPackets to ask for settings and channels."""
|
||||
logging.debug(f"requestConfig for nodeNum:{self.nodeNum}")
|
||||
def requestChannels(self):
|
||||
"""Send regular MeshPackets to ask channels."""
|
||||
logging.debug(f"requestChannels for nodeNum:{self.nodeNum}")
|
||||
self.channels = None
|
||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||
|
||||
self._requestChannel(0)
|
||||
|
||||
def onResponseRequestSettings(self, p):
|
||||
"""Handle the response packets for requesting settings _requestSettings()"""
|
||||
logging.debug(f"onResponseRequestSetting() p:{p}")
|
||||
if "routing" in p["decoded"]:
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
|
||||
self.iface._acknowledgment.receivedNak = True
|
||||
else:
|
||||
self.iface._acknowledgment.receivedAck = True
|
||||
print("")
|
||||
adminMessage = p["decoded"]["admin"]
|
||||
if "getConfigResponse" in adminMessage:
|
||||
resp = adminMessage["getConfigResponse"]
|
||||
field = list(resp.keys())[0]
|
||||
config_type = self.localConfig.DESCRIPTOR.fields_by_name.get(
|
||||
camel_to_snake(field)
|
||||
)
|
||||
config_values = getattr(self.localConfig, config_type.name)
|
||||
elif "getModuleConfigResponse" in adminMessage:
|
||||
resp = adminMessage["getModuleConfigResponse"]
|
||||
field = list(resp.keys())[0]
|
||||
config_type = self.moduleConfig.DESCRIPTOR.fields_by_name.get(
|
||||
camel_to_snake(field)
|
||||
)
|
||||
config_values = getattr(self.moduleConfig, config_type.name)
|
||||
else:
|
||||
print(
|
||||
"Did not receive a valid response. Make sure to have a shared channel named 'admin'."
|
||||
)
|
||||
return
|
||||
for key, value in resp[field].items():
|
||||
setattr(config_values, camel_to_snake(key), value)
|
||||
print(f"{str(camel_to_snake(field))}:\n{str(config_values)}")
|
||||
|
||||
def requestConfig(self, configType):
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onResponseRequestSettings
|
||||
print("Requesting current config from remote node (this can take a while).")
|
||||
|
||||
msgIndex = configType.index
|
||||
if configType.containing_type.full_name == "LocalConfig":
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_config_request = msgIndex
|
||||
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
|
||||
else:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_module_config_request = msgIndex
|
||||
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
|
||||
if onResponse:
|
||||
self.iface.waitForAckNak()
|
||||
|
||||
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, attribute='channels'):
|
||||
def waitForConfig(self, attribute="channels"):
|
||||
"""Block until radio config is received. Returns True if config has been received."""
|
||||
return self._timeout.waitForSet(self, attrs=('localConfig', attribute))
|
||||
|
||||
def writeConfig(self):
|
||||
"""Write the current (edited) localConfig to the device"""
|
||||
if self.localConfig is None:
|
||||
our_exit("Error: No localConfig has been read")
|
||||
|
||||
if self.localConfig.device:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.device.CopyFrom(self.localConfig.device)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote device")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.position:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.position.CopyFrom(self.localConfig.position)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote position")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.power:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.power.CopyFrom(self.localConfig.power)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote power")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.network:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.network.CopyFrom(self.localConfig.network)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote network")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.display:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.display.CopyFrom(self.localConfig.display)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote display")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.lora:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.lora.CopyFrom(self.localConfig.lora)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote lora")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.bluetooth:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.bluetooth.CopyFrom(self.localConfig.bluetooth)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote bluetooth")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.mqtt:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.mqtt.CopyFrom(self.moduleConfig.mqtt)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: mqtt")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.serial:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.serial.CopyFrom(self.moduleConfig.serial)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: serial")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.external_notification:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.external_notification.CopyFrom(self.moduleConfig.external_notification)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: external_notification")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.store_forward:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.store_forward.CopyFrom(self.moduleConfig.store_forward)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: store_forward")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.range_test:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.range_test.CopyFrom(self.moduleConfig.range_test)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: range_test")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.telemetry:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.telemetry.CopyFrom(self.moduleConfig.telemetry)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: telemetry")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.canned_message:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.canned_message.CopyFrom(self.moduleConfig.canned_message)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: canned_message")
|
||||
time.sleep(0.3)
|
||||
return self._timeout.waitForSet(self, attrs=("localConfig", attribute))
|
||||
|
||||
def writeConfig(self, config_name):
|
||||
"""Write the current (edited) localConfig to the device"""
|
||||
@@ -188,39 +150,59 @@ class Node:
|
||||
|
||||
p = admin_pb2.AdminMessage()
|
||||
|
||||
if config_name == 'device':
|
||||
if config_name == "device":
|
||||
p.set_config.device.CopyFrom(self.localConfig.device)
|
||||
elif config_name == 'position':
|
||||
elif config_name == "position":
|
||||
p.set_config.position.CopyFrom(self.localConfig.position)
|
||||
elif config_name == 'power':
|
||||
elif config_name == "power":
|
||||
p.set_config.power.CopyFrom(self.localConfig.power)
|
||||
elif config_name == 'network':
|
||||
elif config_name == "network":
|
||||
p.set_config.network.CopyFrom(self.localConfig.network)
|
||||
elif config_name == 'display':
|
||||
elif config_name == "display":
|
||||
p.set_config.display.CopyFrom(self.localConfig.display)
|
||||
elif config_name == 'lora':
|
||||
elif config_name == "lora":
|
||||
p.set_config.lora.CopyFrom(self.localConfig.lora)
|
||||
elif config_name == 'bluetooth':
|
||||
elif config_name == "bluetooth":
|
||||
p.set_config.bluetooth.CopyFrom(self.localConfig.bluetooth)
|
||||
elif config_name == 'mqtt':
|
||||
elif config_name == "mqtt":
|
||||
p.set_module_config.mqtt.CopyFrom(self.moduleConfig.mqtt)
|
||||
elif config_name == 'serial':
|
||||
elif config_name == "serial":
|
||||
p.set_module_config.serial.CopyFrom(self.moduleConfig.serial)
|
||||
elif config_name == 'external_notification':
|
||||
p.set_module_config.external_notification.CopyFrom(self.moduleConfig.external_notification)
|
||||
elif config_name == 'store_forward':
|
||||
elif config_name == "external_notification":
|
||||
p.set_module_config.external_notification.CopyFrom(
|
||||
self.moduleConfig.external_notification
|
||||
)
|
||||
elif config_name == "store_forward":
|
||||
p.set_module_config.store_forward.CopyFrom(self.moduleConfig.store_forward)
|
||||
elif config_name == 'range_test':
|
||||
elif config_name == "range_test":
|
||||
p.set_module_config.range_test.CopyFrom(self.moduleConfig.range_test)
|
||||
elif config_name == 'telemetry':
|
||||
elif config_name == "telemetry":
|
||||
p.set_module_config.telemetry.CopyFrom(self.moduleConfig.telemetry)
|
||||
elif config_name == 'canned_message':
|
||||
p.set_module_config.canned_message.CopyFrom(self.moduleConfig.canned_message)
|
||||
elif config_name == "canned_message":
|
||||
p.set_module_config.canned_message.CopyFrom(
|
||||
self.moduleConfig.canned_message
|
||||
)
|
||||
elif config_name == "audio":
|
||||
p.set_module_config.audio.CopyFrom(self.moduleConfig.audio)
|
||||
elif config_name == "remote_hardware":
|
||||
p.set_module_config.remote_hardware.CopyFrom(
|
||||
self.moduleConfig.remote_hardware
|
||||
)
|
||||
elif config_name == "neighbor_info":
|
||||
p.set_module_config.neighbor_info.CopyFrom(self.moduleConfig.neighbor_info)
|
||||
elif config_name == "detection_sensor":
|
||||
p.set_module_config.detection_sensor.CopyFrom(self.moduleConfig.detection_sensor)
|
||||
elif config_name == "ambient_lighting":
|
||||
p.set_module_config.ambient_lighting.CopyFrom(self.moduleConfig.ambient_lighting)
|
||||
else:
|
||||
our_exit(f"Error: No valid config with name {config_name}")
|
||||
|
||||
|
||||
logging.debug(f"Wrote: {config_name}")
|
||||
self._sendAdmin(p)
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def writeChannel(self, channelIndex, adminIndex=0):
|
||||
"""Write the current (edited) channel to the device"""
|
||||
@@ -232,8 +214,8 @@ class Node:
|
||||
|
||||
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
|
||||
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):
|
||||
@@ -241,9 +223,12 @@ class Node:
|
||||
return ch
|
||||
|
||||
def deleteChannel(self, channelIndex):
|
||||
"""Delete the specifed channelIndex and shift other channels up"""
|
||||
"""Delete the specified 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):
|
||||
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
|
||||
@@ -254,7 +239,7 @@ class Node:
|
||||
self._fixupChannels() # expand back to 8 channels
|
||||
|
||||
index = channelIndex
|
||||
while index < self.iface.myInfo.max_channels:
|
||||
while index < 8:
|
||||
self.writeChannel(index, adminIndex=adminIndex)
|
||||
index += 1
|
||||
|
||||
@@ -267,7 +252,7 @@ class Node:
|
||||
|
||||
def getChannelByName(self, name):
|
||||
"""Try to find the named channel or return None"""
|
||||
for c in (self.channels or []):
|
||||
for c in self.channels or []:
|
||||
if c.settings and c.settings.name == name:
|
||||
return c
|
||||
return None
|
||||
@@ -281,47 +266,38 @@ class Node:
|
||||
|
||||
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
|
||||
for c in self.channels or []:
|
||||
if c.settings and c.settings.name.lower() == "admin":
|
||||
return c.index
|
||||
return 0
|
||||
|
||||
def setOwner(self, long_name=None, short_name=None, is_licensed=False):
|
||||
"""Set device owner name"""
|
||||
logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
|
||||
nChars = 3
|
||||
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()
|
||||
|
||||
nChars = 4
|
||||
if long_name is not None:
|
||||
long_name = long_name.strip()
|
||||
p.set_owner.long_name = long_name
|
||||
p.set_owner.is_licensed = is_licensed
|
||||
if short_name is not None:
|
||||
short_name = short_name.strip()
|
||||
if len(short_name) > nChars:
|
||||
short_name = short_name[:nChars]
|
||||
print(f"Maximum is 4 characters, truncated to {short_name}")
|
||||
p.set_owner.short_name = short_name
|
||||
p.set_owner.is_licensed = is_licensed
|
||||
|
||||
# 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}')
|
||||
return self._sendAdmin(p)
|
||||
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}")
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def getURL(self, includeAll: bool = True):
|
||||
"""The sharable URL that describes the current channel"""
|
||||
@@ -329,21 +305,23 @@ class Node:
|
||||
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):
|
||||
if c.role == channel_pb2.Channel.Role.PRIMARY or (
|
||||
includeAll and c.role == channel_pb2.Channel.Role.SECONDARY
|
||||
):
|
||||
channelSet.settings.append(c.settings)
|
||||
|
||||
channelSet.lora_config.CopyFrom(self.localConfig.lora)
|
||||
some_bytes = channelSet.SerializeToString()
|
||||
s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
|
||||
s = base64.urlsafe_b64encode(some_bytes).decode("ascii")
|
||||
s = s.replace("=", "").replace("+", "-").replace("/", "_")
|
||||
return f"https://www.meshtastic.org/e/#{s}"
|
||||
return f"https://meshtastic.org/e/#{s}"
|
||||
|
||||
def setURL(self, url):
|
||||
"""Set mesh network URL"""
|
||||
if self.localConfig is None:
|
||||
our_exit("Warning: No Config has been read")
|
||||
|
||||
# URLs are of the form https://www.meshtastic.org/d/#{base64_channel_set}
|
||||
# URLs are of the form https://meshtastic.org/d/#{base64_channel_set}
|
||||
# Split on '/#' to find the base64 encoded channel settings
|
||||
splitURL = url.split("/#")
|
||||
b64 = splitURL[-1]
|
||||
@@ -353,24 +331,27 @@ class Node:
|
||||
# per https://stackoverflow.com/a/9807138
|
||||
missing_padding = len(b64) % 4
|
||||
if missing_padding:
|
||||
b64 += '=' * (4 - 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.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}')
|
||||
logging.debug(f"Channel i:{i} ch:{ch}")
|
||||
self.writeChannel(ch.index)
|
||||
i = i + 1
|
||||
|
||||
@@ -378,9 +359,9 @@ class Node:
|
||||
p.set_config.lora.CopyFrom(channelSet.lora_config)
|
||||
self._sendAdmin(p)
|
||||
|
||||
def onResponseRequestCannedMessagePluginMessageMessages(self, p):
|
||||
"""Handle the response packet for requesting canned message plugin message part 1"""
|
||||
logging.debug(f'onResponseRequestCannedMessagePluginMessageMessages() p:{p}')
|
||||
def onResponseRequestRingtone(self, p):
|
||||
"""Handle the response packet for requesting ringtone part 1"""
|
||||
logging.debug(f"onResponseRequestRingtone() p:{p}")
|
||||
errorFound = False
|
||||
if "routing" in p["decoded"]:
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
@@ -390,31 +371,109 @@ class Node:
|
||||
if "decoded" in p:
|
||||
if "admin" in p["decoded"]:
|
||||
if "raw" in p["decoded"]["admin"]:
|
||||
self.cannedPluginMessageMessages = p["decoded"]["admin"]["raw"].get_canned_message_module_messages_response
|
||||
logging.debug(f'self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}')
|
||||
self.ringtonePart = p["decoded"]["admin"][
|
||||
"raw"
|
||||
].get_ringtone_response
|
||||
logging.debug(f"self.ringtonePart:{self.ringtonePart}")
|
||||
self.gotResponse = True
|
||||
|
||||
|
||||
def get_canned_message(self):
|
||||
"""Get the canned message string. Concatenate all pieces together and return a single string."""
|
||||
logging.debug(f'in get_canned_message()')
|
||||
if not self.cannedPluginMessage:
|
||||
|
||||
def get_ringtone(self):
|
||||
"""Get the ringtone. Concatenate all pieces together and return a single string."""
|
||||
logging.debug(f"in get_ringtone()")
|
||||
if not self.ringtone:
|
||||
p1 = admin_pb2.AdminMessage()
|
||||
p1.get_canned_message_module_messages_request = True
|
||||
p1.get_ringtone_request = True
|
||||
self.gotResponse = False
|
||||
self._sendAdmin(p1, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessageMessages)
|
||||
self._sendAdmin(
|
||||
p1, wantResponse=True, onResponse=self.onResponseRequestRingtone
|
||||
)
|
||||
while self.gotResponse is False:
|
||||
time.sleep(0.1)
|
||||
|
||||
logging.debug(f'self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}')
|
||||
logging.debug(f"self.ringtone:{self.ringtone}")
|
||||
|
||||
self.ringtone = ""
|
||||
if self.ringtonePart:
|
||||
self.ringtone += self.ringtonePart
|
||||
|
||||
print(f"ringtone:{self.ringtone}")
|
||||
logging.debug(f"ringtone:{self.ringtone}")
|
||||
return self.ringtone
|
||||
|
||||
def set_ringtone(self, ringtone):
|
||||
"""Set the ringtone. The ringtone length must be less than 230 character."""
|
||||
|
||||
if len(ringtone) > 230:
|
||||
our_exit("Warning: The ringtone must be less than 230 characters.")
|
||||
|
||||
# split into chunks
|
||||
chunks = []
|
||||
chunks_size = 230
|
||||
for i in range(0, len(ringtone), chunks_size):
|
||||
chunks.append(ringtone[i : i + chunks_size])
|
||||
|
||||
# for each chunk, send a message to set the values
|
||||
# for i in range(0, len(chunks)):
|
||||
for i, chunk in enumerate(chunks):
|
||||
p = admin_pb2.AdminMessage()
|
||||
|
||||
# TODO: should be a way to improve this
|
||||
if i == 0:
|
||||
p.set_ringtone_message = chunk
|
||||
|
||||
logging.debug(f"Setting ringtone '{chunk}' part {i+1}")
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def onResponseRequestCannedMessagePluginMessageMessages(self, p):
|
||||
"""Handle the response packet for requesting canned message plugin message part 1"""
|
||||
logging.debug(f"onResponseRequestCannedMessagePluginMessageMessages() 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:
|
||||
if "decoded" in p:
|
||||
if "admin" in p["decoded"]:
|
||||
if "raw" in p["decoded"]["admin"]:
|
||||
self.cannedPluginMessageMessages = p["decoded"]["admin"][
|
||||
"raw"
|
||||
].get_canned_message_module_messages_response
|
||||
logging.debug(
|
||||
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
|
||||
)
|
||||
self.gotResponse = True
|
||||
|
||||
def get_canned_message(self):
|
||||
"""Get the canned message string. Concatenate all pieces together and return a single string."""
|
||||
logging.debug(f"in get_canned_message()")
|
||||
if not self.cannedPluginMessage:
|
||||
p1 = admin_pb2.AdminMessage()
|
||||
p1.get_canned_message_module_messages_request = True
|
||||
self.gotResponse = False
|
||||
self._sendAdmin(
|
||||
p1,
|
||||
wantResponse=True,
|
||||
onResponse=self.onResponseRequestCannedMessagePluginMessageMessages,
|
||||
)
|
||||
while self.gotResponse is False:
|
||||
time.sleep(0.1)
|
||||
|
||||
logging.debug(
|
||||
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
|
||||
)
|
||||
|
||||
self.cannedPluginMessage = ""
|
||||
if self.cannedPluginMessageMessages:
|
||||
self.cannedPluginMessage += self.cannedPluginMessageMessages
|
||||
|
||||
print(f'canned_plugin_message:{self.cannedPluginMessage}')
|
||||
logging.debug(f'canned_plugin_message:{self.cannedPluginMessage}')
|
||||
print(f"canned_plugin_message:{self.cannedPluginMessage}")
|
||||
logging.debug(f"canned_plugin_message:{self.cannedPluginMessage}")
|
||||
return self.cannedPluginMessage
|
||||
|
||||
def set_canned_message(self, message):
|
||||
@@ -427,10 +486,10 @@ class Node:
|
||||
chunks = []
|
||||
chunks_size = 200
|
||||
for i in range(0, len(message), chunks_size):
|
||||
chunks.append(message[i: i + chunks_size])
|
||||
chunks.append(message[i : i + chunks_size])
|
||||
|
||||
# for each chunk, send a message to set the values
|
||||
#for i in range(0, len(chunks)):
|
||||
# for i in range(0, len(chunks)):
|
||||
for i, chunk in enumerate(chunks):
|
||||
p = admin_pb2.AdminMessage()
|
||||
|
||||
@@ -439,14 +498,19 @@ class Node:
|
||||
p.set_canned_message_module_messages = chunk
|
||||
|
||||
logging.debug(f"Setting canned message '{chunk}' part {i+1}")
|
||||
self._sendAdmin(p)
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def exitSimulator(self):
|
||||
"""Tell a simulator node to exit (this message
|
||||
is ignored for other nodes)"""
|
||||
is ignored for other nodes)"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.exit_simulator = True
|
||||
logging.debug('in exitSimulator()')
|
||||
logging.debug("in exitSimulator()")
|
||||
|
||||
return self._sendAdmin(p)
|
||||
|
||||
@@ -456,7 +520,38 @@ class Node:
|
||||
p.reboot_seconds = secs
|
||||
logging.info(f"Telling node to reboot in {secs} seconds")
|
||||
|
||||
return self._sendAdmin(p)
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def beginSettingsTransaction(self):
|
||||
"""Tell the node to open a transaction to edit settings."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.begin_edit_settings = True
|
||||
logging.info(f"Telling open a transaction to edit settings")
|
||||
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def commitSettingsTransaction(self):
|
||||
"""Tell the node to commit the open transaction for editing settings."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.commit_edit_settings = True
|
||||
logging.info(f"Telling node to commit open transaction for editing settings")
|
||||
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def rebootOTA(self, secs: int = 10):
|
||||
"""Tell the node to reboot into factory firmware."""
|
||||
@@ -464,7 +559,12 @@ class Node:
|
||||
p.reboot_ota_seconds = secs
|
||||
logging.info(f"Telling node to reboot to OTA in {secs} seconds")
|
||||
|
||||
return self._sendAdmin(p)
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def shutdown(self, secs: int = 10):
|
||||
"""Tell the node to shutdown."""
|
||||
@@ -472,15 +572,22 @@ class Node:
|
||||
p.shutdown_seconds = secs
|
||||
logging.info(f"Telling node to shutdown in {secs} seconds")
|
||||
|
||||
return self._sendAdmin(p)
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def getMetadata(self, secs: int = 10):
|
||||
"""Tell the node to shutdown."""
|
||||
def getMetadata(self):
|
||||
"""Get the node's metadata."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_device_metadata_request = True
|
||||
logging.info(f"Requesting device metadata")
|
||||
|
||||
return self._sendAdmin(p, wantResponse=True, onResponse=self.onRequestGetMetadata)
|
||||
return self._sendAdmin(
|
||||
p, wantResponse=True, onResponse=self.onRequestGetMetadata
|
||||
)
|
||||
|
||||
def factoryReset(self):
|
||||
"""Tell the node to factory reset."""
|
||||
@@ -488,7 +595,12 @@ class Node:
|
||||
p.factory_reset = True
|
||||
logging.info(f"Telling node to factory reset")
|
||||
|
||||
return self._sendAdmin(p)
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def resetNodeDb(self):
|
||||
"""Tell the node to reset its list of nodes."""
|
||||
@@ -496,7 +608,12 @@ class Node:
|
||||
p.nodedb_reset = True
|
||||
logging.info(f"Telling node to reset the NodeDB")
|
||||
|
||||
return self._sendAdmin(p)
|
||||
# If sending to a remote node, wait for ACK/NAK
|
||||
if self == self.iface.localNode:
|
||||
onResponse = None
|
||||
else:
|
||||
onResponse = self.onAckNak
|
||||
return self._sendAdmin(p, onResponse=onResponse)
|
||||
|
||||
def _fixupChannels(self):
|
||||
"""Fixup indexes and add disabled channels as needed"""
|
||||
@@ -513,52 +630,59 @@ class Node:
|
||||
|
||||
# Add extra disabled channels as needed
|
||||
index = len(self.channels)
|
||||
while index < self.iface.myInfo.max_channels:
|
||||
while index < 8:
|
||||
ch = channel_pb2.Channel()
|
||||
ch.role = channel_pb2.Channel.Role.DISABLED
|
||||
ch.index = index
|
||||
self.channels.append(ch)
|
||||
index += 1
|
||||
|
||||
|
||||
def onRequestGetMetadata(self, p):
|
||||
"""Handle the response packet for requesting device metadata getMetadata()"""
|
||||
logging.debug(f'onRequestGetMetadata() p:{p}')
|
||||
logging.debug(f"onRequestGetMetadata() p:{p}")
|
||||
|
||||
if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name(portnums_pb2.PortNum.ROUTING_APP):
|
||||
if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name(
|
||||
portnums_pb2.PortNum.ROUTING_APP
|
||||
):
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
logging.warning(f'Metadata request failed, error reason: {p["decoded"]["routing"]["errorReason"]}')
|
||||
self._timeout.expireTime = time.time() # Do not wait any longer
|
||||
return # Don't try to parse this routing message
|
||||
logging.warning(
|
||||
f'Metadata request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
|
||||
)
|
||||
self._timeout.expireTime = time.time() # Do not wait any longer
|
||||
return # Don't try to parse this routing message
|
||||
logging.debug(f"Retrying metadata request.")
|
||||
self.getMetadata()
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
c = p["decoded"]["admin"]["raw"].get_device_metadata_response
|
||||
self._timeout.reset() # We made foreward progress
|
||||
self._timeout.reset() # We made forward progress
|
||||
logging.debug(f"Received metadata {stripnl(c)}")
|
||||
print(f"\nfirmware_version: {c.firmware_version}")
|
||||
print(f"device_state_version: {c.device_state_version}")
|
||||
|
||||
def onResponseRequestChannel(self, p):
|
||||
"""Handle the response packet for requesting a channel _requestChannel()"""
|
||||
logging.debug(f'onResponseRequestChannel() p:{p}')
|
||||
logging.debug(f"onResponseRequestChannel() p:{p}")
|
||||
|
||||
if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name(portnums_pb2.PortNum.ROUTING_APP):
|
||||
if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name(
|
||||
portnums_pb2.PortNum.ROUTING_APP
|
||||
):
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
logging.warning(f'Channel request failed, error reason: {p["decoded"]["routing"]["errorReason"]}')
|
||||
self._timeout.expireTime = time.time() # Do not wait any longer
|
||||
return # Don't try to parse this routing message
|
||||
logging.warning(
|
||||
f'Channel request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
|
||||
)
|
||||
self._timeout.expireTime = time.time() # Do not wait any longer
|
||||
return # Don't try to parse this routing message
|
||||
lastTried = 0
|
||||
if len(self.partialChannels) > 0:
|
||||
lastTried = self.partialChannels[-1]
|
||||
lastTried = self.partialChannels[-1].index
|
||||
logging.debug(f"Retrying previous channel request.")
|
||||
self._requestChannel(lastTried)
|
||||
return
|
||||
|
||||
c = p["decoded"]["admin"]["raw"].get_channel_response
|
||||
self.partialChannels.append(c)
|
||||
self._timeout.reset() # We made foreward progress
|
||||
self._timeout.reset() # We made forward progress
|
||||
logging.debug(f"Received channel {stripnl(c)}")
|
||||
index = c.index
|
||||
|
||||
@@ -567,9 +691,11 @@ class Node:
|
||||
|
||||
# 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
|
||||
quitEarly = (
|
||||
c.role == channel_pb2.Channel.Role.DISABLED
|
||||
) and fastChannelDownload
|
||||
|
||||
if quitEarly or index >= self.iface.myInfo.max_channels - 1:
|
||||
if quitEarly or index >= 8 - 1:
|
||||
logging.debug("Finished downloading channels")
|
||||
|
||||
self.channels = self.partialChannels
|
||||
@@ -580,37 +706,70 @@ class Node:
|
||||
else:
|
||||
self._requestChannel(index + 1)
|
||||
|
||||
def onAckNak(self, p):
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
print(
|
||||
f'Received a NAK, error reason: {p["decoded"]["routing"]["errorReason"]}'
|
||||
)
|
||||
self.iface._acknowledgment.receivedNak = True
|
||||
else:
|
||||
if int(p["from"]) == self.iface.localNode.nodeNum:
|
||||
print(
|
||||
f"Received an implicit ACK. Packet will likely arrive, but cannot be guaranteed."
|
||||
)
|
||||
self.iface._acknowledgment.receivedImplAck = True
|
||||
else:
|
||||
print(f"Received an ACK.")
|
||||
self.iface._acknowledgment.receivedAck = True
|
||||
|
||||
def _requestChannel(self, channelNum: int):
|
||||
"""Done with initial config messages, now send regular
|
||||
MeshPackets to ask for settings"""
|
||||
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)")
|
||||
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)
|
||||
|
||||
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):
|
||||
def _sendAdmin(
|
||||
self,
|
||||
p: admin_pb2.AdminMessage,
|
||||
wantResponse=True,
|
||||
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")
|
||||
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
|
||||
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}')
|
||||
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)
|
||||
return self.iface.sendData(
|
||||
p,
|
||||
self.nodeNum,
|
||||
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
||||
wantAck=False,
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=adminIndex,
|
||||
)
|
||||
|
||||
26
meshtastic/paxcount_pb2.py
Normal file
26
meshtastic/paxcount_pb2.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: meshtastic/paxcount.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19meshtastic/paxcount.proto\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBc\n\x13\x63om.geeksville.meshB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.paxcount_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_PAXCOUNT._serialized_start=29
|
||||
_PAXCOUNT._serialized_end=82
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,12 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: portnums.proto
|
||||
# source: meshtastic/portnums.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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)
|
||||
|
||||
@@ -15,36 +13,14 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eportnums.proto*\x81\x03\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42\x44\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
_PORTNUM = DESCRIPTOR.enum_types_by_name['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
|
||||
TEXT_MESSAGE_COMPRESSED_APP = 7
|
||||
WAYPOINT_APP = 8
|
||||
REPLY_APP = 32
|
||||
IP_TUNNEL_APP = 33
|
||||
SERIAL_APP = 64
|
||||
STORE_FORWARD_APP = 65
|
||||
RANGE_TEST_APP = 66
|
||||
TELEMETRY_APP = 67
|
||||
ZPS_APP = 68
|
||||
SIMULATOR_APP = 69
|
||||
PRIVATE_APP = 256
|
||||
ATAK_FORWARDER = 257
|
||||
MAX = 511
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19meshtastic/portnums.proto*\xe8\x03\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tAUDIO_APP\x10\t\x12\x18\n\x14\x44\x45TECTION_SENSOR_APP\x10\n\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x12\n\x0ePAXCOUNTER_APP\x10\"\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x12\n\x0eTRACEROUTE_APP\x10\x46\x12\x14\n\x10NEIGHBORINFO_APP\x10G\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]\n\x13\x63om.geeksville.meshB\x08PortnumsZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.portnums_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_PORTNUM._serialized_start=19
|
||||
_PORTNUM._serialized_end=404
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_PORTNUM._serialized_start=30
|
||||
_PORTNUM._serialized_end=518
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
"""Remote hardware
|
||||
"""
|
||||
import logging
|
||||
|
||||
from pubsub import pub
|
||||
|
||||
from meshtastic import portnums_pb2, remote_hardware_pb2
|
||||
from meshtastic.util import our_exit
|
||||
|
||||
|
||||
def onGPIOreceive(packet, interface):
|
||||
"""Callback for received GPIO responses
|
||||
"""
|
||||
"""Callback for received GPIO responses"""
|
||||
logging.debug(f"packet:{packet} interface:{interface}")
|
||||
gpioValue = 0
|
||||
hw = packet["decoded"]["remotehw"]
|
||||
@@ -21,9 +22,11 @@ def onGPIOreceive(packet, interface):
|
||||
# so, we set it here
|
||||
gpioValue = 0
|
||||
|
||||
#print(f'mask:{interface.mask}')
|
||||
# print(f'mask:{interface.mask}')
|
||||
value = int(gpioValue) & int(interface.mask)
|
||||
print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')
|
||||
print(
|
||||
f'Received RemoteHardware type={hw["type"]}, gpio_value={gpioValue} value={value}'
|
||||
)
|
||||
interface.gotResponse = True
|
||||
|
||||
|
||||
@@ -44,46 +47,55 @@ class RemoteHardwareClient:
|
||||
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.")
|
||||
"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)
|
||||
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}')
|
||||
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.type = 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):
|
||||
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}')
|
||||
logging.debug(f"readGPIOs nodeid:{nodeid} mask:{mask}")
|
||||
r = remote_hardware_pb2.HardwareMessage()
|
||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
|
||||
r.type = 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}')
|
||||
logging.debug(f"watchGPIOs nodeid:{nodeid} mask:{mask}")
|
||||
r = remote_hardware_pb2.HardwareMessage()
|
||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
||||
r.type = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
||||
r.gpio_mask = mask
|
||||
self.iface.mask = mask
|
||||
return self._sendHardware(nodeid, r)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: remote_hardware.proto
|
||||
# source: meshtastic/remote_hardware.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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)
|
||||
|
||||
@@ -14,25 +13,16 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15remote_hardware.proto\"\xcb\x01\n\x0fHardwareMessage\x12#\n\x04type\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 = DESCRIPTOR.message_types_by_name['HardwareMessage']
|
||||
_HARDWAREMESSAGE_TYPE = _HARDWAREMESSAGE.enum_types_by_name['Type']
|
||||
HardwareMessage = _reflection.GeneratedProtocolMessageType('HardwareMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _HARDWAREMESSAGE,
|
||||
'__module__' : 'remote_hardware_pb2'
|
||||
# @@protoc_insertion_point(class_scope:HardwareMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(HardwareMessage)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/remote_hardware.proto\"\xcb\x01\n\x0fHardwareMessage\x12#\n\x04type\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\x42\x63\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.remote_hardware_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_HARDWAREMESSAGE._serialized_start=26
|
||||
_HARDWAREMESSAGE._serialized_end=229
|
||||
_HARDWAREMESSAGE_TYPE._serialized_start=121
|
||||
_HARDWAREMESSAGE_TYPE._serialized_end=229
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_HARDWAREMESSAGE._serialized_start=37
|
||||
_HARDWAREMESSAGE._serialized_end=240
|
||||
_HARDWAREMESSAGE_TYPE._serialized_start=132
|
||||
_HARDWAREMESSAGE_TYPE._serialized_end=240
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
26
meshtastic/rtttl_pb2.py
Normal file
26
meshtastic/rtttl_pb2.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: meshtastic/rtttl.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16meshtastic/rtttl.proto\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBf\n\x13\x63om.geeksville.meshB\x11RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.rtttl_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_RTTTLCONFIG._serialized_start=26
|
||||
_RTTTLCONFIG._serialized_end=57
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,16 +1,18 @@
|
||||
""" Serial interface class
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
import platform
|
||||
import time
|
||||
|
||||
import serial
|
||||
|
||||
import meshtastic.util
|
||||
from meshtastic.stream_interface import StreamInterface
|
||||
|
||||
if platform.system() != 'Windows':
|
||||
if platform.system() != "Windows":
|
||||
import termios
|
||||
|
||||
|
||||
class SerialInterface(StreamInterface):
|
||||
"""Interface class for meshtastic devices over a serial link"""
|
||||
|
||||
@@ -42,19 +44,23 @@ class SerialInterface(StreamInterface):
|
||||
|
||||
# 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 platform.system() != 'Windows':
|
||||
with open(self.devPath, encoding='utf8') as f:
|
||||
if platform.system() != "Windows":
|
||||
with open(self.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)
|
||||
|
||||
self.stream = serial.Serial(self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0)
|
||||
self.stream = serial.Serial(
|
||||
self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0
|
||||
)
|
||||
self.stream.flush()
|
||||
time.sleep(0.1)
|
||||
|
||||
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
|
||||
StreamInterface.__init__(
|
||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow
|
||||
)
|
||||
|
||||
def close(self):
|
||||
"""Close a connection to the device"""
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: storeforward.proto
|
||||
# source: meshtastic/storeforward.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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)
|
||||
|
||||
@@ -14,58 +13,22 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(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')
|
||||
|
||||
|
||||
|
||||
_STOREANDFORWARD = DESCRIPTOR.message_types_by_name['StoreAndForward']
|
||||
_STOREANDFORWARD_STATISTICS = _STOREANDFORWARD.nested_types_by_name['Statistics']
|
||||
_STOREANDFORWARD_HISTORY = _STOREANDFORWARD.nested_types_by_name['History']
|
||||
_STOREANDFORWARD_HEARTBEAT = _STOREANDFORWARD.nested_types_by_name['Heartbeat']
|
||||
_STOREANDFORWARD_REQUESTRESPONSE = _STOREANDFORWARD.enum_types_by_name['RequestResponse']
|
||||
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)
|
||||
})
|
||||
,
|
||||
|
||||
'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)
|
||||
})
|
||||
_sym_db.RegisterMessage(StoreAndForward)
|
||||
_sym_db.RegisterMessage(StoreAndForward.Statistics)
|
||||
_sym_db.RegisterMessage(StoreAndForward.History)
|
||||
_sym_db.RegisterMessage(StoreAndForward.Heartbeat)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dmeshtastic/storeforward.proto\"\xbe\x06\n\x0fStoreAndForward\x12,\n\x02rr\x18\x01 \x01(\x0e\x32 .StoreAndForward.RequestResponse\x12,\n\x05stats\x18\x02 \x01(\x0b\x32\x1b.StoreAndForward.StatisticsH\x00\x12+\n\x07history\x18\x03 \x01(\x0b\x32\x18.StoreAndForward.HistoryH\x00\x12/\n\theartbeat\x18\x04 \x01(\x0b\x32\x1a.StoreAndForward.HeartbeatH\x00\x12\x0f\n\x05\x65mpty\x18\x05 \x01(\x08H\x00\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\"\x89\x02\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\x0cROUTER_STATS\x10\x07\x12\x10\n\x0c\x43LIENT_ERROR\x10@\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x41\x12\x10\n\x0c\x43LIENT_STATS\x10\x42\x12\x0f\n\x0b\x43LIENT_PING\x10\x43\x12\x0f\n\x0b\x43LIENT_PONG\x10\x44\x12\x10\n\x0c\x43LIENT_ABORT\x10jB\t\n\x07variantBj\n\x13\x63om.geeksville.meshB\x15StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.storeforward_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_STOREANDFORWARD._serialized_start=23
|
||||
_STOREANDFORWARD._serialized_end=801
|
||||
_STOREANDFORWARD_STATISTICS._serialized_start=223
|
||||
_STOREANDFORWARD_STATISTICS._serialized_end=428
|
||||
_STOREANDFORWARD_HISTORY._serialized_start=430
|
||||
_STOREANDFORWARD_HISTORY._serialized_end=503
|
||||
_STOREANDFORWARD_HEARTBEAT._serialized_start=505
|
||||
_STOREANDFORWARD_HEARTBEAT._serialized_end=551
|
||||
_STOREANDFORWARD_REQUESTRESPONSE._serialized_start=554
|
||||
_STOREANDFORWARD_REQUESTRESPONSE._serialized_end=801
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_STOREANDFORWARD._serialized_start=34
|
||||
_STOREANDFORWARD._serialized_end=864
|
||||
_STOREANDFORWARD_STATISTICS._serialized_start=257
|
||||
_STOREANDFORWARD_STATISTICS._serialized_end=462
|
||||
_STOREANDFORWARD_HISTORY._serialized_start=464
|
||||
_STOREANDFORWARD_HISTORY._serialized_end=537
|
||||
_STOREANDFORWARD_HEARTBEAT._serialized_start=539
|
||||
_STOREANDFORWARD_HEARTBEAT._serialized_end=585
|
||||
_STOREANDFORWARD_REQUESTRESPONSE._serialized_start=588
|
||||
_STOREANDFORWARD_REQUESTRESPONSE._serialized_end=853
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -4,15 +4,14 @@ import logging
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import serial
|
||||
|
||||
|
||||
from meshtastic.mesh_interface import MeshInterface
|
||||
from meshtastic.util import stripnl, is_windows11
|
||||
|
||||
from meshtastic.util import is_windows11, stripnl
|
||||
|
||||
START1 = 0x94
|
||||
START2 = 0xc3
|
||||
START2 = 0xC3
|
||||
HEADER_LEN = 4
|
||||
MAX_TO_FROM_RADIO_SIZE = 512
|
||||
|
||||
@@ -32,9 +31,10 @@ class StreamInterface(MeshInterface):
|
||||
Exception: [description]
|
||||
"""
|
||||
|
||||
if not hasattr(self, 'stream') and not noProto:
|
||||
if not hasattr(self, "stream") and not noProto:
|
||||
raise Exception(
|
||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)")
|
||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)"
|
||||
)
|
||||
self._rxBuf = bytes() # empty
|
||||
self._wantExit = False
|
||||
|
||||
@@ -60,7 +60,7 @@ class StreamInterface(MeshInterface):
|
||||
|
||||
# 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
|
||||
# we write enough 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)
|
||||
@@ -110,8 +110,8 @@ class StreamInterface(MeshInterface):
|
||||
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}')
|
||||
header = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
|
||||
logging.debug(f"sending header:{header} b:{b}")
|
||||
self._writeBytes(header + b)
|
||||
|
||||
def close(self):
|
||||
@@ -126,18 +126,18 @@ class StreamInterface(MeshInterface):
|
||||
|
||||
def __reader(self):
|
||||
"""The reader thread that reads bytes from our stream"""
|
||||
logging.debug('in __reader()')
|
||||
logging.debug("in __reader()")
|
||||
empty = bytes()
|
||||
|
||||
try:
|
||||
while not self._wantExit:
|
||||
#logging.debug("reading character")
|
||||
# logging.debug("reading character")
|
||||
b = self._readBytes(1)
|
||||
#logging.debug("In reader loop")
|
||||
#logging.debug(f"read returned {b}")
|
||||
# logging.debug("In reader loop")
|
||||
# logging.debug(f"read returned {b}")
|
||||
if len(b) > 0:
|
||||
c = b[0]
|
||||
#logging.debug(f'c:{c}')
|
||||
# logging.debug(f'c:{c}')
|
||||
ptr = len(self._rxBuf)
|
||||
|
||||
# Assume we want to append this byte, fixme use bytearray instead
|
||||
@@ -150,38 +150,54 @@ class StreamInterface(MeshInterface):
|
||||
try:
|
||||
self.debugOut.write(b.decode("utf-8"))
|
||||
except:
|
||||
self.debugOut.write('?')
|
||||
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')
|
||||
# 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 (
|
||||
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
|
||||
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}")
|
||||
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}")
|
||||
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}")
|
||||
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}")
|
||||
logging.error(
|
||||
f"Unexpected exception, terminating meshtastic reader... {ex}"
|
||||
)
|
||||
finally:
|
||||
logging.debug("reader is exiting")
|
||||
self._disconnected()
|
||||
|
||||
@@ -5,89 +5,224 @@
|
||||
# Goal is to detect which device and port to use from the supported devices
|
||||
# without installing any libraries that are not currently in the python meshtastic library
|
||||
|
||||
class SupportedDevice():
|
||||
|
||||
class SupportedDevice:
|
||||
"""Devices supported on Meshtastic"""
|
||||
|
||||
def __init__(self, name, version=None, for_firmware=None, device_class="esp32",
|
||||
baseport_on_linux=None, baseport_on_mac=None, baseport_on_windows="COM",
|
||||
usb_vendor_id_in_hex=None, usb_product_id_in_hex=None):
|
||||
""" constructor """
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
version=None,
|
||||
for_firmware=None,
|
||||
device_class="esp32",
|
||||
baseport_on_linux=None,
|
||||
baseport_on_mac=None,
|
||||
baseport_on_windows="COM",
|
||||
usb_vendor_id_in_hex=None,
|
||||
usb_product_id_in_hex=None,
|
||||
):
|
||||
"""constructor"""
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.for_firmware = for_firmware
|
||||
self.device_class = device_class # could be "nrf52"
|
||||
self.device_class = device_class # could be "nrf52"
|
||||
|
||||
# when you run "lsusb -d xxxx:" in linux
|
||||
self.usb_vendor_id_in_hex = usb_vendor_id_in_hex # store in lower case
|
||||
self.usb_product_id_in_hex = usb_product_id_in_hex # store in lower case
|
||||
self.usb_vendor_id_in_hex = usb_vendor_id_in_hex # store in lower case
|
||||
self.usb_product_id_in_hex = usb_product_id_in_hex # store in lower case
|
||||
|
||||
self.baseport_on_linux = baseport_on_linux # ex: ttyUSB or ttyACM
|
||||
self.baseport_on_linux = baseport_on_linux # ex: ttyUSB or ttyACM
|
||||
self.baseport_on_mac = baseport_on_mac
|
||||
self.baseport_on_windows = baseport_on_windows
|
||||
|
||||
# supported devices
|
||||
tbeam_v0_7 = SupportedDevice(name="T-Beam", version="0.7", for_firmware="tbeam0.7",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tbeam_v1_1 = SupportedDevice(name="T-Beam", version="1.1", for_firmware="tbeam",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tbeam_M8N = SupportedDevice(name="T-Beam", version="M8N", for_firmware="tbeam",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tbeam_M8N_SX1262 = SupportedDevice(name="T-Beam", version="M8N_SX1262", for_firmware="tbeam",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tlora_v1 = SupportedDevice(name="T-Lora", version="1", for_firmware="tlora-v1",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tlora_v1_3 = SupportedDevice(name="T-Lora", version="1.3", for_firmware="tlora-v1-3",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial",
|
||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
||||
tlora_v2 = SupportedDevice(name="T-Lora", version="2", for_firmware="tlora-v2",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tlora_v2_1_1_6 = SupportedDevice(name="T-Lora", version="2.1-1.6", for_firmware="tlora-v2-1-1.6",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
heltec_v1 = SupportedDevice(name="Heltec", version="1", for_firmware="heltec-v1",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
||||
heltec_v2_0 = SupportedDevice(name="Heltec", version="2.0", for_firmware="heltec-v2.0",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
||||
heltec_v2_1 = SupportedDevice(name="Heltec", version="2.1", for_firmware="heltec-v2.1",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
||||
rak11200 = SupportedDevice(name="RAK 11200", version="", for_firmware="rak11200",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="7523")
|
||||
meshtastic_diy_v1 = SupportedDevice(name="Meshtastic DIY", version="1", for_firmware="meshtastic-diy-v1",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
||||
# Note: The T-Echo reports product id in boot mode
|
||||
techo_1 = SupportedDevice(name="T-Echo", version="1", for_firmware="t-echo-1", device_class="nrf52",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
|
||||
rak4631_5005 = SupportedDevice(name="RAK 4631 5005", version="", for_firmware="rak4631_5005",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
|
||||
rak4631_5005_epaper = SupportedDevice(name="RAK 4631 5005 14000 epaper", version="", for_firmware="rak4631_5005_epaper",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
|
||||
# Note: The 19003 reports same product id as 5005 in boot mode
|
||||
rak4631_19003 = SupportedDevice(name="RAK 4631 19003", version="", for_firmware="rak4631_19003",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="8029")
|
||||
nano_g1 = SupportedDevice(name="Nano G1", version="", for_firmware="nano-g1",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
|
||||
supported_devices = [tbeam_v0_7, tbeam_v1_1, tbeam_M8N, tbeam_M8N_SX1262,
|
||||
tlora_v1, tlora_v1_3, tlora_v2, tlora_v2_1_1_6,
|
||||
heltec_v1, heltec_v2_0, heltec_v2_1,
|
||||
meshtastic_diy_v1, techo_1, rak4631_5005, rak4631_5005_epaper, rak4631_19003,
|
||||
rak11200, nano_g1]
|
||||
# supported devices
|
||||
tbeam_v0_7 = SupportedDevice(
|
||||
name="T-Beam",
|
||||
version="0.7",
|
||||
for_firmware="tbeam0.7",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
tbeam_v1_1 = SupportedDevice(
|
||||
name="T-Beam",
|
||||
version="1.1",
|
||||
for_firmware="tbeam",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
tbeam_M8N = SupportedDevice(
|
||||
name="T-Beam",
|
||||
version="M8N",
|
||||
for_firmware="tbeam",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
tbeam_M8N_SX1262 = SupportedDevice(
|
||||
name="T-Beam",
|
||||
version="M8N_SX1262",
|
||||
for_firmware="tbeam",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
tlora_v1 = SupportedDevice(
|
||||
name="T-Lora",
|
||||
version="1",
|
||||
for_firmware="tlora-v1",
|
||||
baseport_on_linux="ttyUSB",
|
||||
baseport_on_mac="cu.usbserial",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
tlora_v1_3 = SupportedDevice(
|
||||
name="T-Lora",
|
||||
version="1.3",
|
||||
for_firmware="tlora-v1-3",
|
||||
baseport_on_linux="ttyUSB",
|
||||
baseport_on_mac="cu.usbserial",
|
||||
usb_vendor_id_in_hex="10c4",
|
||||
usb_product_id_in_hex="ea60",
|
||||
)
|
||||
tlora_v2 = SupportedDevice(
|
||||
name="T-Lora",
|
||||
version="2",
|
||||
for_firmware="tlora-v2",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
tlora_v2_1_1_6 = SupportedDevice(
|
||||
name="T-Lora",
|
||||
version="2.1-1.6",
|
||||
for_firmware="tlora-v2-1-1.6",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
heltec_v1 = SupportedDevice(
|
||||
name="Heltec",
|
||||
version="1",
|
||||
for_firmware="heltec-v1",
|
||||
baseport_on_linux="ttyUSB",
|
||||
baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4",
|
||||
usb_product_id_in_hex="ea60",
|
||||
)
|
||||
heltec_v2_0 = SupportedDevice(
|
||||
name="Heltec",
|
||||
version="2.0",
|
||||
for_firmware="heltec-v2.0",
|
||||
baseport_on_linux="ttyUSB",
|
||||
baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4",
|
||||
usb_product_id_in_hex="ea60",
|
||||
)
|
||||
heltec_v2_1 = SupportedDevice(
|
||||
name="Heltec",
|
||||
version="2.1",
|
||||
for_firmware="heltec-v2.1",
|
||||
baseport_on_linux="ttyUSB",
|
||||
baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4",
|
||||
usb_product_id_in_hex="ea60",
|
||||
)
|
||||
rak11200 = SupportedDevice(
|
||||
name="RAK 11200",
|
||||
version="",
|
||||
for_firmware="rak11200",
|
||||
baseport_on_linux="ttyUSB",
|
||||
baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="7523",
|
||||
)
|
||||
meshtastic_diy_v1 = SupportedDevice(
|
||||
name="Meshtastic DIY",
|
||||
version="1",
|
||||
for_firmware="meshtastic-diy-v1",
|
||||
baseport_on_linux="ttyUSB",
|
||||
baseport_on_mac="cu.usbserial-",
|
||||
usb_vendor_id_in_hex="10c4",
|
||||
usb_product_id_in_hex="ea60",
|
||||
)
|
||||
# Note: The T-Echo reports product id in boot mode
|
||||
techo_1 = SupportedDevice(
|
||||
name="T-Echo",
|
||||
version="1",
|
||||
for_firmware="t-echo-1",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a",
|
||||
usb_product_id_in_hex="0029",
|
||||
)
|
||||
rak4631_5005 = SupportedDevice(
|
||||
name="RAK 4631 5005",
|
||||
version="",
|
||||
for_firmware="rak4631_5005",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a",
|
||||
usb_product_id_in_hex="0029",
|
||||
)
|
||||
rak4631_5005_epaper = SupportedDevice(
|
||||
name="RAK 4631 5005 14000 epaper",
|
||||
version="",
|
||||
for_firmware="rak4631_5005_epaper",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a",
|
||||
usb_product_id_in_hex="0029",
|
||||
)
|
||||
# Note: The 19003 reports same product id as 5005 in boot mode
|
||||
rak4631_19003 = SupportedDevice(
|
||||
name="RAK 4631 19003",
|
||||
version="",
|
||||
for_firmware="rak4631_19003",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a",
|
||||
usb_product_id_in_hex="8029",
|
||||
)
|
||||
nano_g1 = SupportedDevice(
|
||||
name="Nano G1",
|
||||
version="",
|
||||
for_firmware="nano-g1",
|
||||
baseport_on_linux="ttyACM",
|
||||
baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86",
|
||||
usb_product_id_in_hex="55d4",
|
||||
)
|
||||
|
||||
supported_devices = [
|
||||
tbeam_v0_7,
|
||||
tbeam_v1_1,
|
||||
tbeam_M8N,
|
||||
tbeam_M8N_SX1262,
|
||||
tlora_v1,
|
||||
tlora_v1_3,
|
||||
tlora_v2,
|
||||
tlora_v2_1_1_6,
|
||||
heltec_v1,
|
||||
heltec_v2_0,
|
||||
heltec_v2_1,
|
||||
meshtastic_diy_v1,
|
||||
techo_1,
|
||||
rak4631_5005,
|
||||
rak4631_5005_epaper,
|
||||
rak4631_19003,
|
||||
rak11200,
|
||||
nano_g1,
|
||||
]
|
||||
|
||||
@@ -6,11 +6,18 @@ from typing import AnyStr
|
||||
|
||||
from meshtastic.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):
|
||||
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:
|
||||
@@ -30,12 +37,13 @@ class TCPInterface(StreamInterface):
|
||||
else:
|
||||
self.socket = None
|
||||
|
||||
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
|
||||
connectNow=connectNow)
|
||||
StreamInterface.__init__(
|
||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow
|
||||
)
|
||||
|
||||
def _socket_shutdown(self):
|
||||
"""Shutdown the socket.
|
||||
Note: Broke out this line so the exception could be unit tested.
|
||||
Note: Broke out this line so the exception could be unit tested.
|
||||
"""
|
||||
self.socket.shutdown(socket.SHUT_RDWR)
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: telemetry.proto
|
||||
# source: meshtastic/telemetry.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
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)
|
||||
|
||||
@@ -15,57 +13,24 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ftelemetry.proto\"i\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\"\x9b\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\"\x82\x01\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12(\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x0e.DeviceMetricsH\x00\x12\x32\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x13.EnvironmentMetricsH\x00\x42\t\n\x07variant*\xa0\x01\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\nBK\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
_TELEMETRYSENSORTYPE = DESCRIPTOR.enum_types_by_name['TelemetrySensorType']
|
||||
TelemetrySensorType = enum_type_wrapper.EnumTypeWrapper(_TELEMETRYSENSORTYPE)
|
||||
SENSOR_UNSET = 0
|
||||
BME280 = 1
|
||||
BME680 = 2
|
||||
MCP9808 = 3
|
||||
INA260 = 4
|
||||
INA219 = 5
|
||||
BMP280 = 6
|
||||
SHTC3 = 7
|
||||
LPS22 = 8
|
||||
QMC6310 = 9
|
||||
QMI8658 = 10
|
||||
|
||||
|
||||
_DEVICEMETRICS = DESCRIPTOR.message_types_by_name['DeviceMetrics']
|
||||
_ENVIRONMENTMETRICS = DESCRIPTOR.message_types_by_name['EnvironmentMetrics']
|
||||
_TELEMETRY = DESCRIPTOR.message_types_by_name['Telemetry']
|
||||
DeviceMetrics = _reflection.GeneratedProtocolMessageType('DeviceMetrics', (_message.Message,), {
|
||||
'DESCRIPTOR' : _DEVICEMETRICS,
|
||||
'__module__' : 'telemetry_pb2'
|
||||
# @@protoc_insertion_point(class_scope:DeviceMetrics)
|
||||
})
|
||||
_sym_db.RegisterMessage(DeviceMetrics)
|
||||
|
||||
EnvironmentMetrics = _reflection.GeneratedProtocolMessageType('EnvironmentMetrics', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ENVIRONMENTMETRICS,
|
||||
'__module__' : 'telemetry_pb2'
|
||||
# @@protoc_insertion_point(class_scope:EnvironmentMetrics)
|
||||
})
|
||||
_sym_db.RegisterMessage(EnvironmentMetrics)
|
||||
|
||||
Telemetry = _reflection.GeneratedProtocolMessageType('Telemetry', (_message.Message,), {
|
||||
'DESCRIPTOR' : _TELEMETRY,
|
||||
'__module__' : 'telemetry_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Telemetry)
|
||||
})
|
||||
_sym_db.RegisterMessage(Telemetry)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ameshtastic/telemetry.proto\"i\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\"\x9b\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\"\x8c\x01\n\x0cPowerMetrics\x12\x13\n\x0b\x63h1_voltage\x18\x01 \x01(\x02\x12\x13\n\x0b\x63h1_current\x18\x02 \x01(\x02\x12\x13\n\x0b\x63h2_voltage\x18\x03 \x01(\x02\x12\x13\n\x0b\x63h2_current\x18\x04 \x01(\x02\x12\x13\n\x0b\x63h3_voltage\x18\x05 \x01(\x02\x12\x13\n\x0b\x63h3_current\x18\x06 \x01(\x02\"\xbf\x02\n\x11\x41irQualityMetrics\x12\x15\n\rpm10_standard\x18\x01 \x01(\r\x12\x15\n\rpm25_standard\x18\x02 \x01(\r\x12\x16\n\x0epm100_standard\x18\x03 \x01(\r\x12\x1a\n\x12pm10_environmental\x18\x04 \x01(\r\x12\x1a\n\x12pm25_environmental\x18\x05 \x01(\r\x12\x1b\n\x13pm100_environmental\x18\x06 \x01(\r\x12\x16\n\x0eparticles_03um\x18\x07 \x01(\r\x12\x16\n\x0eparticles_05um\x18\x08 \x01(\r\x12\x16\n\x0eparticles_10um\x18\t \x01(\r\x12\x16\n\x0eparticles_25um\x18\n \x01(\r\x12\x16\n\x0eparticles_50um\x18\x0b \x01(\r\x12\x17\n\x0fparticles_100um\x18\x0c \x01(\r\"\xdd\x01\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12(\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x0e.DeviceMetricsH\x00\x12\x32\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x13.EnvironmentMetricsH\x00\x12\x31\n\x13\x61ir_quality_metrics\x18\x04 \x01(\x0b\x32\x12.AirQualityMetricsH\x00\x12&\n\rpower_metrics\x18\x05 \x01(\x0b\x32\r.PowerMetricsH\x00\x42\t\n\x07variant*\xd4\x01\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\n\x12\x0c\n\x08QMC5883L\x10\x0b\x12\t\n\x05SHT31\x10\x0c\x12\x0c\n\x08PMSA003I\x10\r\x12\x0b\n\x07INA3221\x10\x0e\x42\x64\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.telemetry_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_TELEMETRYSENSORTYPE._serialized_start=418
|
||||
_TELEMETRYSENSORTYPE._serialized_end=578
|
||||
_DEVICEMETRICS._serialized_start=19
|
||||
_DEVICEMETRICS._serialized_end=124
|
||||
_ENVIRONMENTMETRICS._serialized_start=127
|
||||
_ENVIRONMENTMETRICS._serialized_end=282
|
||||
_TELEMETRY._serialized_start=285
|
||||
_TELEMETRY._serialized_end=415
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_TELEMETRYSENSORTYPE._serialized_start=985
|
||||
_TELEMETRYSENSORTYPE._serialized_end=1197
|
||||
_DEVICEMETRICS._serialized_start=30
|
||||
_DEVICEMETRICS._serialized_end=135
|
||||
_ENVIRONMENTMETRICS._serialized_start=138
|
||||
_ENVIRONMENTMETRICS._serialized_end=293
|
||||
_POWERMETRICS._serialized_start=296
|
||||
_POWERMETRICS._serialized_end=436
|
||||
_AIRQUALITYMETRICS._serialized_start=439
|
||||
_AIRQUALITYMETRICS._serialized_end=758
|
||||
_TELEMETRY._serialized_start=761
|
||||
_TELEMETRY._serialized_end=982
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
messages and report back if successful.
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from dotmap import DotMap
|
||||
from pubsub import pub
|
||||
|
||||
import meshtastic.util
|
||||
from meshtastic.__init__ import BROADCAST_NUM
|
||||
from meshtastic.serial_interface import SerialInterface
|
||||
from meshtastic.tcp_interface import TCPInterface
|
||||
|
||||
|
||||
"""The interfaces we are using for our tests"""
|
||||
interfaces = None
|
||||
|
||||
@@ -52,7 +53,9 @@ def subscribe():
|
||||
pub.subscribe(onNode, "meshtastic.node")
|
||||
|
||||
|
||||
def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False):
|
||||
def testSend(
|
||||
fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False
|
||||
):
|
||||
"""
|
||||
Sends one test packet between two nodes and then returns success or failure
|
||||
|
||||
@@ -73,19 +76,19 @@ def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, want
|
||||
else:
|
||||
toNode = toInterface.myInfo.my_node_num
|
||||
|
||||
logging.debug(
|
||||
f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
||||
logging.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
||||
# pylint: disable=W0603
|
||||
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)
|
||||
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:
|
||||
if len(receivedPackets) >= 1:
|
||||
return True
|
||||
return False # Failed to send
|
||||
|
||||
@@ -102,15 +105,18 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
|
||||
isBroadcast = True
|
||||
# asBinary=(i % 2 == 0)
|
||||
success = testSend(
|
||||
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck)
|
||||
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)")
|
||||
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")
|
||||
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far"
|
||||
)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
@@ -140,7 +146,7 @@ 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')
|
||||
return open(debugname, "w+", buffering=1, encoding="utf8")
|
||||
|
||||
|
||||
def testAll(numTests=5):
|
||||
@@ -151,14 +157,22 @@ def testAll(numTests=5):
|
||||
"""
|
||||
ports = meshtastic.util.findPorts(True)
|
||||
if len(ports) < 2:
|
||||
meshtastic.util.our_exit("Warning: Must have at least two devices connected to USB.")
|
||||
meshtastic.util.our_exit(
|
||||
"Warning: Must have at least two devices connected to USB."
|
||||
)
|
||||
|
||||
pub.subscribe(onConnection, "meshtastic.connection")
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
# pylint: disable=W0603
|
||||
global interfaces
|
||||
interfaces = list(map(lambda port: SerialInterface(
|
||||
port, debugOut=openDebugLog(port), connectNow=True), ports))
|
||||
interfaces = list(
|
||||
map(
|
||||
lambda port: SerialInterface(
|
||||
port, debugOut=openDebugLog(port), connectNow=True
|
||||
),
|
||||
ports,
|
||||
)
|
||||
)
|
||||
|
||||
logging.info("Ports opened, starting test")
|
||||
result = testThread(numTests)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"""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
|
||||
|
||||
|
||||
@@ -22,36 +23,34 @@ def reset_globals():
|
||||
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
|
||||
}
|
||||
}
|
||||
"!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
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from ..ble_interface import BLEInterface
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('platform.system', return_value='Linux')
|
||||
@patch("platform.system", return_value="Linux")
|
||||
def test_BLEInterface(mock_platform):
|
||||
"""Test that we can instantiate a BLEInterface"""
|
||||
iface = BLEInterface('foo', debugOut=True, noProto=True)
|
||||
iface = BLEInterface("foo", debugOut=True, noProto=True)
|
||||
iface.close()
|
||||
mock_platform.assert_called()
|
||||
|
||||
@@ -6,19 +6,24 @@ 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')
|
||||
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')
|
||||
return_value, _ = subprocess.getstatusoutput(
|
||||
"source venv/bin/activate; python3 examples/hello_world_serial.py hello"
|
||||
)
|
||||
assert return_value == 1
|
||||
_, err = capsys.readouterr()
|
||||
assert err == ''
|
||||
assert err == ""
|
||||
# TODO: Why does this not work?
|
||||
# assert out == 'Warning: No Meshtastic devices detected.'
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
"""Meshtastic unit tests for __init__.py"""
|
||||
|
||||
import re
|
||||
import logging
|
||||
|
||||
import re
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from meshtastic.__init__ import _onTextReceive, _onPositionReceive, _onNodeInfoReceive
|
||||
from ..serial_interface import SerialInterface
|
||||
from meshtastic.__init__ import _onNodeInfoReceive, _onPositionReceive, _onTextReceive
|
||||
|
||||
from ..globals import Globals
|
||||
from ..serial_interface import SerialInterface
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -20,8 +21,8 @@ def test_init_onTextReceive_with_exception(caplog):
|
||||
packet = {}
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
_onTextReceive(iface, packet)
|
||||
assert re.search(r'in _onTextReceive', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'Malformatted', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"in _onTextReceive", caplog.text, re.MULTILINE)
|
||||
assert re.search(r"Malformatted", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -30,15 +31,10 @@ def test_init_onPositionReceive(caplog):
|
||||
args = MagicMock()
|
||||
Globals.getInstance().set_args(args)
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
packet = {
|
||||
'from': 'foo',
|
||||
'decoded': {
|
||||
'position': {}
|
||||
}
|
||||
}
|
||||
packet = {"from": "foo", "decoded": {"position": {}}}
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
_onPositionReceive(iface, packet)
|
||||
assert re.search(r'in _onPositionReceive', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"in _onPositionReceive", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -49,13 +45,13 @@ def test_init_onNodeInfoReceive(caplog, iface_with_nodes):
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
packet = {
|
||||
'from': 'foo',
|
||||
'decoded': {
|
||||
'user': {
|
||||
'id': 'bar',
|
||||
},
|
||||
}
|
||||
}
|
||||
"from": "foo",
|
||||
"decoded": {
|
||||
"user": {
|
||||
"id": "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
_onNodeInfoReceive(iface, packet)
|
||||
assert re.search(r'in _onNodeInfoReceive', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"in _onNodeInfoReceive", caplog.text, re.MULTILINE)
|
||||
|
||||
@@ -8,39 +8,39 @@ import pytest
|
||||
@pytest.mark.int
|
||||
def test_int_meshtastic_no_args():
|
||||
"""Test meshtastic without any args"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic')
|
||||
assert re.match(r'usage: meshtastic', out)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic")
|
||||
assert re.match(r"usage: meshtastic", out)
|
||||
assert return_value == 1
|
||||
|
||||
|
||||
@pytest.mark.int
|
||||
def test_int_mesh_tunnel_no_args():
|
||||
"""Test mesh-tunnel without any args"""
|
||||
return_value, out = subprocess.getstatusoutput('mesh-tunnel')
|
||||
assert re.match(r'usage: mesh-tunnel', out)
|
||||
return_value, out = subprocess.getstatusoutput("mesh-tunnel")
|
||||
assert re.match(r"usage: mesh-tunnel", 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)
|
||||
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)
|
||||
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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --support")
|
||||
assert re.search(r"System", out)
|
||||
assert re.search(r"Python", out)
|
||||
assert return_value == 0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,18 @@
|
||||
"""Meshtastic unit tests for mesh_interface.py"""
|
||||
|
||||
import re
|
||||
import logging
|
||||
import re
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
|
||||
from .. import mesh_pb2
|
||||
from ..__init__ import BROADCAST_ADDR, LOCAL_ADDR
|
||||
from ..mesh_interface import MeshInterface
|
||||
from ..node import Node
|
||||
from .. import mesh_pb2
|
||||
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
|
||||
|
||||
# TODO
|
||||
#from ..config import Config
|
||||
# from ..config import Config
|
||||
from ..util import Timeout
|
||||
|
||||
|
||||
@@ -20,24 +21,24 @@ from ..util import Timeout
|
||||
def test_MeshInterface(capsys):
|
||||
"""Test that we can instantiate a MeshInterface"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
anode = Node('foo', 'bar')
|
||||
anode = Node("foo", "bar")
|
||||
|
||||
nodes = {
|
||||
'!9388f81c': {
|
||||
'num': 2475227164,
|
||||
'user': {
|
||||
'id': '!9388f81c',
|
||||
'longName': 'Unknown f81c',
|
||||
'shortName': '?1C',
|
||||
'macaddr': 'RBeTiPgc',
|
||||
'hwModel': 'TBEAM'
|
||||
"!9388f81c": {
|
||||
"num": 2475227164,
|
||||
"user": {
|
||||
"id": "!9388f81c",
|
||||
"longName": "Unknown f81c",
|
||||
"shortName": "?1C",
|
||||
"macaddr": "RBeTiPgc",
|
||||
"hwModel": "TBEAM",
|
||||
},
|
||||
'position': {},
|
||||
'lastHeard': 1640204888
|
||||
"position": {},
|
||||
"lastHeard": 1640204888,
|
||||
}
|
||||
}
|
||||
|
||||
iface.nodesByNum = {1: anode }
|
||||
iface.nodesByNum = {1: anode}
|
||||
iface.nodes = nodes
|
||||
|
||||
myInfo = MagicMock()
|
||||
@@ -46,15 +47,15 @@ def test_MeshInterface(capsys):
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.showNodes()
|
||||
iface.sendText('hello')
|
||||
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 == ''
|
||||
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
|
||||
@@ -65,7 +66,7 @@ def test_getMyUser(iface_with_nodes):
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
myuser = iface.getMyUser()
|
||||
assert myuser is not None
|
||||
assert myuser["id"] == '!9388f81c'
|
||||
assert myuser["id"] == "!9388f81c"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -75,7 +76,7 @@ def test_getLongName(iface_with_nodes):
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
mylongname = iface.getLongName()
|
||||
assert mylongname == 'Unknown f81c'
|
||||
assert mylongname == "Unknown f81c"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -85,7 +86,7 @@ def test_getShortName(iface_with_nodes):
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
myshortname = iface.getShortName()
|
||||
assert myshortname == '?1C'
|
||||
assert myshortname == "?1C"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -96,24 +97,24 @@ def test_handlePacketFromRadio_no_from(capsys):
|
||||
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 == ''
|
||||
assert re.search(r"Device returned a packet we sent, ignoring", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_globals")
|
||||
def test_handlePacketFromRadio_with_a_portnum(caplog):
|
||||
"""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.
|
||||
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.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)
|
||||
assert re.search(r"Not populating fromId", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -122,10 +123,10 @@ def test_handlePacketFromRadio_no_portnum(caplog):
|
||||
"""Test _handlePacketFromRadio without a portnum"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
meshPacket = mesh_pb2.MeshPacket()
|
||||
meshPacket.decoded.payload = b''
|
||||
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)
|
||||
assert re.search(r"Not populating fromId", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -144,10 +145,10 @@ def test_getNode_not_local(caplog):
|
||||
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')
|
||||
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)
|
||||
assert re.search(r"About to requestConfig", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -157,14 +158,14 @@ def test_getNode_not_local_timeout(capsys):
|
||||
iface = MeshInterface(noProto=True)
|
||||
anode = MagicMock(autospec=Node)
|
||||
anode.waitForConfig.return_value = False
|
||||
with patch('meshtastic.node.Node', return_value=anode):
|
||||
with patch("meshtastic.node.Node", return_value=anode):
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
iface.getNode('bar2')
|
||||
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 == ''
|
||||
assert re.match(r"Error: Timed out waiting for node config", out)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -175,13 +176,13 @@ def test_sendPosition(caplog):
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
iface.sendPosition()
|
||||
iface.close()
|
||||
assert re.search(r'p.time:', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"p.time:", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#@pytest.mark.usefixtures("reset_globals")
|
||||
#def test_close_with_heartbeatTimer(caplog):
|
||||
# @pytest.mark.unit
|
||||
# @pytest.mark.usefixtures("reset_globals")
|
||||
# def test_close_with_heartbeatTimer(caplog):
|
||||
# """Test close() with heartbeatTimer"""
|
||||
# iface = MeshInterface(noProto=True)
|
||||
# anode = Node('foo', 'bar')
|
||||
@@ -197,9 +198,9 @@ def test_sendPosition(caplog):
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#@pytest.mark.usefixtures("reset_globals")
|
||||
#def test_handleFromRadio_empty_payload(caplog):
|
||||
# @pytest.mark.unit
|
||||
# @pytest.mark.usefixtures("reset_globals")
|
||||
# def test_handleFromRadio_empty_payload(caplog):
|
||||
# """Test _handleFromRadio"""
|
||||
# iface = MeshInterface(noProto=True)
|
||||
# with caplog.at_level(logging.DEBUG):
|
||||
@@ -224,13 +225,13 @@ def test_handleFromRadio_with_my_info(caplog):
|
||||
# max_channels: 8
|
||||
# has_wifi: true
|
||||
# }
|
||||
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'
|
||||
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'max_channels: 8', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"Received myinfo", caplog.text, re.MULTILINE)
|
||||
assert re.search(r"max_channels: 8", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -257,16 +258,16 @@ def test_handleFromRadio_with_node_info(caplog, capsys):
|
||||
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)
|
||||
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 == ''
|
||||
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()
|
||||
|
||||
|
||||
@@ -281,16 +282,16 @@ def test_handleFromRadio_with_node_info_tbeam1(caplog, capsys):
|
||||
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)
|
||||
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 == ''
|
||||
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()
|
||||
|
||||
|
||||
@@ -312,8 +313,8 @@ def test_MeshInterface_sendToRadioImpl(caplog):
|
||||
"""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._sendToRadioImpl("foo")
|
||||
assert re.search(r"Subclass must provide toradio", caplog.text, re.MULTILINE)
|
||||
iface.close()
|
||||
|
||||
|
||||
@@ -323,8 +324,8 @@ def test_MeshInterface_sendToRadio_no_proto(caplog):
|
||||
"""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._sendToRadioImpl("foo")
|
||||
assert re.search(r"Subclass must provide toradio", caplog.text, re.MULTILINE)
|
||||
iface.close()
|
||||
|
||||
|
||||
@@ -333,22 +334,22 @@ def test_MeshInterface_sendToRadio_no_proto(caplog):
|
||||
def test_sendData_too_long(caplog):
|
||||
"""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.'
|
||||
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 re.search("Data payload too big", caplog.text, re.MULTILINE)
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
iface.close()
|
||||
|
||||
@@ -359,10 +360,10 @@ def test_sendData_unknown_app(capsys):
|
||||
"""Test sendData when unknown app"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
iface.sendData(b'hello', portNum=0)
|
||||
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 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
|
||||
|
||||
@@ -374,9 +375,9 @@ def test_sendPosition_with_a_position(caplog):
|
||||
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)
|
||||
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
|
||||
@@ -385,10 +386,10 @@ def test_sendPacket_with_no_destination(capsys):
|
||||
"""Test _sendPacket()"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
iface._sendPacket(b'', destinationId=None)
|
||||
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 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
|
||||
|
||||
@@ -401,7 +402,7 @@ def test_sendPacket_with_destination_as_int(caplog):
|
||||
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)
|
||||
assert re.search(r"Not sending packet", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -411,8 +412,8 @@ def test_sendPacket_with_destination_starting_with_a_bang(caplog):
|
||||
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)
|
||||
iface._sendPacket(meshPacket, destinationId="!1234")
|
||||
assert re.search(r"Not sending packet", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -423,7 +424,7 @@ def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog):
|
||||
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)
|
||||
assert re.search(r"Not sending packet", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -435,8 +436,8 @@ def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys):
|
||||
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 re.search(r"Warning: No myInfo", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 1
|
||||
|
||||
@@ -452,7 +453,7 @@ def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog):
|
||||
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)
|
||||
assert re.search(r"Not sending packet", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -462,12 +463,12 @@ def test_sendPacket_with_destination_is_blank_with_nodes(capsys, iface_with_node
|
||||
iface = iface_with_nodes
|
||||
meshPacket = mesh_pb2.MeshPacket()
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
iface._sendPacket(meshPacket, destinationId='')
|
||||
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 == ''
|
||||
assert re.match(r"Warning: NodeId not found in DB", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -478,8 +479,8 @@ def test_sendPacket_with_destination_is_blank_without_nodes(caplog, iface_with_n
|
||||
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)
|
||||
iface._sendPacket(meshPacket, destinationId="")
|
||||
assert re.search(r"Warning: There were no self.nodes.", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -488,7 +489,7 @@ def test_getMyNodeInfo():
|
||||
"""Test getMyNodeInfo()"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
anode = iface.getNode(LOCAL_ADDR)
|
||||
iface.nodesByNum = {1: anode }
|
||||
iface.nodesByNum = {1: anode}
|
||||
assert iface.nodesByNum.get(1) == anode
|
||||
myInfo = MagicMock()
|
||||
iface.myInfo = myInfo
|
||||
@@ -508,8 +509,10 @@ def test_generatePacketId(capsys):
|
||||
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 re.search(
|
||||
r"Not connected yet, can not generate packet", out, re.MULTILINE
|
||||
)
|
||||
assert err == ""
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
|
||||
|
||||
@@ -540,10 +543,12 @@ def test_fixupPosition():
|
||||
iface = MeshInterface(noProto=True)
|
||||
pos = {"latitudeI": 1010000000, "longitudeI": 1020000000}
|
||||
newpos = iface._fixupPosition(pos)
|
||||
assert newpos == {"latitude": 101.0,
|
||||
"latitudeI": 1010000000,
|
||||
"longitude": 102.0,
|
||||
"longitudeI": 1020000000}
|
||||
assert newpos == {
|
||||
"latitude": 101.0,
|
||||
"latitudeI": 1010000000,
|
||||
"longitude": 102.0,
|
||||
"longitudeI": 1020000000,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -553,7 +558,7 @@ def test_nodeNumToId(iface_with_nodes):
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
someid = iface._nodeNumToId(2475227164)
|
||||
assert someid == '!9388f81c'
|
||||
assert someid == "!9388f81c"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -572,8 +577,8 @@ def test_nodeNumToId_to_all(iface_with_nodes):
|
||||
"""Test _nodeNumToId()"""
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
someid = iface._nodeNumToId(0xffffffff)
|
||||
assert someid == '^all'
|
||||
someid = iface._nodeNumToId(0xFFFFFFFF)
|
||||
assert someid == "^all"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -583,7 +588,7 @@ def test_getOrCreateByNum_minimal(iface_with_nodes):
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
tmp = iface._getOrCreateByNum(123)
|
||||
assert tmp == {'num': 123}
|
||||
assert tmp == {"num": 123}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -593,7 +598,7 @@ def test_getOrCreateByNum_not_found(iface_with_nodes):
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||
iface._getOrCreateByNum(0xffffffff)
|
||||
iface._getOrCreateByNum(0xFFFFFFFF)
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
|
||||
|
||||
@@ -604,12 +609,12 @@ def test_getOrCreateByNum(iface_with_nodes):
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
tmp = iface._getOrCreateByNum(2475227164)
|
||||
assert tmp['num'] == 2475227164
|
||||
assert tmp["num"] == 2475227164
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_enter():
|
||||
# @pytest.mark.unit
|
||||
# def test_enter():
|
||||
# """Test __enter__()"""
|
||||
# iface = MeshInterface(noProto=True)
|
||||
# assert iface == iface.__enter__()
|
||||
@@ -620,9 +625,13 @@ def test_exit_with_exception(caplog):
|
||||
"""Test __exit__()"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
with caplog.at_level(logging.ERROR):
|
||||
iface.__exit__('foo', 'bar', 'baz')
|
||||
assert re.search(r'An exception of type foo with value bar has occurred', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'Traceback: baz', caplog.text, re.MULTILINE)
|
||||
iface.__exit__("foo", "bar", "baz")
|
||||
assert re.search(
|
||||
r"An exception of type foo with value bar has occurred",
|
||||
caplog.text,
|
||||
re.MULTILINE,
|
||||
)
|
||||
assert re.search(r"Traceback: baz", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -646,8 +655,10 @@ def test_waitForConfig(capsys):
|
||||
iface.waitForConfig()
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Exception: Timed out waiting for interface config', err, re.MULTILINE)
|
||||
assert out == ''
|
||||
assert re.search(
|
||||
r"Exception: Timed out waiting for interface config", err, re.MULTILINE
|
||||
)
|
||||
assert out == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -659,8 +670,8 @@ def test_waitConnected_raises_an_exception(capsys):
|
||||
iface._waitConnected(0.01)
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'warn about something', err, re.MULTILINE)
|
||||
assert out == ''
|
||||
assert re.search(r"warn about something", err, re.MULTILINE)
|
||||
assert out == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -671,5 +682,5 @@ def test_waitConnected_isConnected_timeout(capsys):
|
||||
iface._waitConnected(0.01)
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'warn about something', err, re.MULTILINE)
|
||||
assert out == ''
|
||||
assert re.search(r"warn about something", err, re.MULTILINE)
|
||||
assert out == ""
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
"""Meshtastic unit tests for node.py"""
|
||||
|
||||
import re
|
||||
import logging
|
||||
import re
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
|
||||
# from ..admin_pb2 import AdminMessage
|
||||
from ..channel_pb2 import Channel
|
||||
from ..node import Node
|
||||
from ..serial_interface import SerialInterface
|
||||
#from ..admin_pb2 import AdminMessage
|
||||
from ..channel_pb2 import Channel
|
||||
#from ..config_pb2 import Config
|
||||
#from ..cannedmessages_pb2 import (CannedMessagePluginMessagePart1, CannedMessagePluginMessagePart2,
|
||||
|
||||
# from ..config_pb2 import Config
|
||||
# from ..cannedmessages_pb2 import (CannedMessagePluginMessagePart1, CannedMessagePluginMessagePart2,
|
||||
# CannedMessagePluginMessagePart3, CannedMessagePluginMessagePart4,
|
||||
# CannedMessagePluginMessagePart5)
|
||||
#from ..util import Timeout
|
||||
# from ..util import Timeout
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_node(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_node(capsys):
|
||||
# """Test that we can instantiate a Node"""
|
||||
# anode = Node('foo', 'bar')
|
||||
# radioConfig = RadioConfig()
|
||||
@@ -34,8 +35,8 @@ from ..channel_pb2 import Channel
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_node_requestConfig(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_requestConfig(capsys):
|
||||
# """Test run requestConfig"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# amesg = MagicMock(autospec=AdminMessage)
|
||||
@@ -48,8 +49,8 @@ from ..channel_pb2 import Channel
|
||||
# assert err == ''
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_node_get_canned_message_with_all_parts(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_get_canned_message_with_all_parts(capsys):
|
||||
# """Test run get_canned_message()"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# amesg = MagicMock(autospec=AdminMessage)
|
||||
@@ -69,8 +70,8 @@ from ..channel_pb2 import Channel
|
||||
# assert err == ''
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_node_get_canned_message_with_some_parts(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_get_canned_message_with_some_parts(capsys):
|
||||
# """Test run get_canned_message()"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# amesg = MagicMock(autospec=AdminMessage)
|
||||
@@ -86,8 +87,8 @@ from ..channel_pb2 import Channel
|
||||
# assert err == ''
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_node_set_canned_message_one_part(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_set_canned_message_one_part(caplog):
|
||||
# """Test run set_canned_message()"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# amesg = MagicMock(autospec=AdminMessage)
|
||||
@@ -100,8 +101,8 @@ from ..channel_pb2 import Channel
|
||||
# assert not re.search(r"Setting canned message '' part 2", caplog.text, re.MULTILINE)
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_node_set_canned_message_200(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_set_canned_message_200(caplog):
|
||||
# """Test run set_canned_message() 200 characters long"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# amesg = MagicMock(autospec=AdminMessage)
|
||||
@@ -115,8 +116,8 @@ from ..channel_pb2 import Channel
|
||||
# assert not re.search(r"Setting canned message '' part 2", caplog.text, re.MULTILINE)
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_node_set_canned_message_201(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_set_canned_message_201(caplog):
|
||||
# """Test run set_canned_message() 201 characters long"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# amesg = MagicMock(autospec=AdminMessage)
|
||||
@@ -130,8 +131,8 @@ from ..channel_pb2 import Channel
|
||||
# assert re.search(r"Setting canned message 'a' part 2", caplog.text, re.MULTILINE)
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_node_set_canned_message_1000(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_set_canned_message_1000(caplog):
|
||||
# """Test run set_canned_message() 1000 characters long"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# amesg = MagicMock(autospec=AdminMessage)
|
||||
@@ -148,8 +149,8 @@ from ..channel_pb2 import Channel
|
||||
# assert re.search(r" part 5", caplog.text, re.MULTILINE)
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_node_set_canned_message_1001(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_node_set_canned_message_1001(capsys):
|
||||
# """Test run set_canned_message() 1001 characters long"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
@@ -165,8 +166,8 @@ from ..channel_pb2 import Channel
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_setOwnerShort(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_setOwnerShort(caplog):
|
||||
# """Test setOwner"""
|
||||
# anode = Node('foo', 'bar', noProto=True)
|
||||
# with caplog.at_level(logging.DEBUG):
|
||||
@@ -175,8 +176,8 @@ from ..channel_pb2 import Channel
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_setOwner_no_short_name(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_setOwner_no_short_name(caplog):
|
||||
# """Test setOwner"""
|
||||
# anode = Node('foo', 'bar', noProto=True)
|
||||
# with caplog.at_level(logging.DEBUG):
|
||||
@@ -187,8 +188,8 @@ from ..channel_pb2 import Channel
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_setOwner_no_short_name_and_long_name_is_short(caplog):
|
||||
# @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):
|
||||
@@ -199,8 +200,8 @@ from ..channel_pb2 import Channel
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_setOwner_no_short_name_and_long_name_has_words(caplog):
|
||||
# @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):
|
||||
@@ -211,8 +212,8 @@ from ..channel_pb2 import Channel
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_setOwner_long_name_no_short(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_setOwner_long_name_no_short(caplog):
|
||||
# """Test setOwner"""
|
||||
# anode = Node('foo', 'bar', noProto=True)
|
||||
# with caplog.at_level(logging.DEBUG):
|
||||
@@ -224,46 +225,46 @@ from ..channel_pb2 import Channel
|
||||
@pytest.mark.unit
|
||||
def test_exitSimulator(caplog):
|
||||
"""Test exitSimulator"""
|
||||
anode = Node('foo', 'bar', noProto=True)
|
||||
anode = Node("foo", "bar", noProto=True)
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
anode.exitSimulator()
|
||||
assert re.search(r'in exitSimulator', caplog.text, re.MULTILINE)
|
||||
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)
|
||||
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)
|
||||
assert re.search(r"Telling node to reboot", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_shutdown(caplog):
|
||||
"""Test shutdown"""
|
||||
anode = Node('foo', 'bar', noProto=True)
|
||||
anode = Node("foo", "bar", noProto=True)
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
anode.shutdown()
|
||||
assert re.search(r'Telling node to shutdown', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"Telling node to shutdown", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_setURL_empty_url(capsys):
|
||||
"""Test reboot"""
|
||||
anode = Node('foo', 'bar', noProto=True)
|
||||
anode = Node("foo", "bar", noProto=True)
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
anode.setURL('')
|
||||
anode.setURL("")
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 1
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Warning: No RadioConfig has been read', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
assert re.search(r"Warning: No RadioConfig has been read", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_setURL_valid_URL(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_setURL_valid_URL(caplog):
|
||||
# """Test setURL"""
|
||||
# iface = MagicMock(autospec=SerialInterface)
|
||||
# url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
|
||||
@@ -285,19 +286,19 @@ def test_setURL_valid_URL_but_no_settings(capsys):
|
||||
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 = Node(iface, "bar", noProto=True)
|
||||
anode.radioConfig = "baz"
|
||||
anode.setURL(url)
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 1
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Warning: There were no settings', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
assert re.search(r"Warning: There were no settings", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_showChannels(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_showChannels(capsys):
|
||||
# """Test showChannels"""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -340,10 +341,10 @@ def test_setURL_valid_URL_but_no_settings(capsys):
|
||||
@pytest.mark.unit
|
||||
def test_getChannelByChannelIndex():
|
||||
"""Test getChannelByChannelIndex()"""
|
||||
anode = Node('foo', 'bar')
|
||||
anode = Node("foo", "bar")
|
||||
|
||||
channel1 = Channel(index=1, role=1) # primary channel
|
||||
channel2 = Channel(index=2, role=2) # secondary channel
|
||||
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)
|
||||
@@ -351,7 +352,16 @@ def test_getChannelByChannelIndex():
|
||||
channel7 = Channel(index=7, role=0)
|
||||
channel8 = Channel(index=8, role=0)
|
||||
|
||||
channels = [ channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 ]
|
||||
channels = [
|
||||
channel1,
|
||||
channel2,
|
||||
channel3,
|
||||
channel4,
|
||||
channel5,
|
||||
channel6,
|
||||
channel7,
|
||||
channel8,
|
||||
]
|
||||
|
||||
anode.channels = channels
|
||||
|
||||
@@ -367,8 +377,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_deleteChannel_try_to_delete_primary_channel(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_deleteChannel_try_to_delete_primary_channel(capsys):
|
||||
# """Try to delete primary channel."""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -398,8 +408,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_deleteChannel_secondary():
|
||||
# @pytest.mark.unit
|
||||
# def test_deleteChannel_secondary():
|
||||
# """Try to delete a secondary channel."""
|
||||
#
|
||||
# channel1 = Channel(index=1, role=1)
|
||||
@@ -451,8 +461,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_deleteChannel_secondary_with_admin_channel_after_testing():
|
||||
# @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)
|
||||
@@ -511,8 +521,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_deleteChannel_secondary_with_admin_channel_before_testing():
|
||||
# @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)
|
||||
@@ -565,8 +575,8 @@ def test_getChannelByChannelIndex():
|
||||
# assert channels[7].settings.name == ''
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_getChannelByName():
|
||||
# @pytest.mark.unit
|
||||
# def test_getChannelByName():
|
||||
# """Get a channel by the name."""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -593,8 +603,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_getChannelByName_invalid_name():
|
||||
# @pytest.mark.unit
|
||||
# def test_getChannelByName_invalid_name():
|
||||
# """Get a channel by the name but one that is not present."""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -620,8 +630,8 @@ def test_getChannelByChannelIndex():
|
||||
# assert ch is None
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_getDisabledChannel():
|
||||
# @pytest.mark.unit
|
||||
# def test_getDisabledChannel():
|
||||
# """Get the first disabled channel."""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -651,8 +661,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_getDisabledChannel_where_all_channels_are_used():
|
||||
# @pytest.mark.unit
|
||||
# def test_getDisabledChannel_where_all_channels_are_used():
|
||||
# """Get the first disabled channel."""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -676,8 +686,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_getAdminChannelIndex():
|
||||
# @pytest.mark.unit
|
||||
# def test_getAdminChannelIndex():
|
||||
# """Get the 'admin' channel index."""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -704,8 +714,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_getAdminChannelIndex_when_no_admin_named_channel():
|
||||
# @pytest.mark.unit
|
||||
# def test_getAdminChannelIndex_when_no_admin_named_channel():
|
||||
# """Get the 'admin' channel when there is not one."""
|
||||
# anode = Node('foo', 'bar')
|
||||
#
|
||||
@@ -730,8 +740,8 @@ def test_getChannelByChannelIndex():
|
||||
|
||||
# TODO
|
||||
# TODO: should we check if we need to turn it off?
|
||||
#@pytest.mark.unit
|
||||
#def test_turnOffEncryptionOnPrimaryChannel(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_turnOffEncryptionOnPrimaryChannel(capsys):
|
||||
# """Turn off encryption when there is a psk."""
|
||||
# anode = Node('foo', 'bar', noProto=True)
|
||||
#
|
||||
@@ -760,20 +770,20 @@ def test_getChannelByChannelIndex():
|
||||
@pytest.mark.unit
|
||||
def test_writeConfig_with_no_radioConfig(capsys):
|
||||
"""Test writeConfig with no radioConfig."""
|
||||
anode = Node('foo', 'bar', noProto=True)
|
||||
anode = Node("foo", "bar", noProto=True)
|
||||
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
anode.writeConfig()
|
||||
anode.writeConfig('foo')
|
||||
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 == ''
|
||||
assert re.search(r"Error: No RadioConfig has been read", out)
|
||||
assert err == ""
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_writeConfig(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_writeConfig(caplog):
|
||||
# """Test writeConfig"""
|
||||
# anode = Node('foo', 'bar', noProto=True)
|
||||
# radioConfig = RadioConfig()
|
||||
@@ -788,38 +798,40 @@ def test_writeConfig_with_no_radioConfig(capsys):
|
||||
def test_requestChannel_not_localNode(caplog, capsys):
|
||||
"""Test _requestChannel()"""
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||
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 = 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)
|
||||
assert re.search(
|
||||
r"Requesting channel 0 info from remote node", caplog.text, re.MULTILINE
|
||||
)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Requesting channel 0 info', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
assert re.search(r"Requesting channel 0 info", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@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:
|
||||
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 = 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)
|
||||
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_onResponseRequestCannedMessagePluginMesagePart1(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart1(caplog):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart1()"""
|
||||
#
|
||||
# part1 = CannedMessagePluginMessagePart1()
|
||||
@@ -861,8 +873,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert anode.cannedPluginMessagePart1 == 'foo1'
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart2(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart2(caplog):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart2()"""
|
||||
#
|
||||
# part2 = CannedMessagePluginMessagePart2()
|
||||
@@ -904,8 +916,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert anode.cannedPluginMessagePart2 == 'foo2'
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart3(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart3(caplog):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart3()"""
|
||||
#
|
||||
# part3 = CannedMessagePluginMessagePart3()
|
||||
@@ -947,8 +959,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert anode.cannedPluginMessagePart3 == 'foo3'
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart4(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart4(caplog):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart4()"""
|
||||
#
|
||||
# part4 = CannedMessagePluginMessagePart4()
|
||||
@@ -990,8 +1002,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert anode.cannedPluginMessagePart4 == 'foo4'
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart5(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart5(caplog):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart5()"""
|
||||
#
|
||||
# part5 = CannedMessagePluginMessagePart5()
|
||||
@@ -1034,8 +1046,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert anode.cannedPluginMessagePart5 == 'foo5'
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart1_error(caplog, capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart1_error(caplog, capsys):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart1() with error"""
|
||||
#
|
||||
# packet = {
|
||||
@@ -1060,8 +1072,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert err == ''
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart2_error(caplog, capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart2_error(caplog, capsys):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart2() with error"""
|
||||
#
|
||||
# packet = {
|
||||
@@ -1086,8 +1098,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert err == ''
|
||||
|
||||
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart3_error(caplog, capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart3_error(caplog, capsys):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart3() with error"""
|
||||
#
|
||||
# packet = {
|
||||
@@ -1112,8 +1124,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert err == ''
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart4_error(caplog, capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart4_error(caplog, capsys):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart4() with error"""
|
||||
#
|
||||
# packet = {
|
||||
@@ -1138,8 +1150,8 @@ def test_requestChannel_localNode(caplog):
|
||||
# assert err == ''
|
||||
#
|
||||
#
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestCannedMessagePluginMesagePart5_error(caplog, capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestCannedMessagePluginMesagePart5_error(caplog, capsys):
|
||||
# """Test onResponseRequestCannedMessagePluginMessagePart5() with error"""
|
||||
#
|
||||
# packet = {
|
||||
@@ -1165,8 +1177,8 @@ def test_requestChannel_localNode(caplog):
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestChannel(caplog):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestChannel(caplog):
|
||||
# """Test onResponseRequestChannel()"""
|
||||
#
|
||||
# channel1 = Channel(index=1, role=1)
|
||||
@@ -1264,8 +1276,8 @@ def test_requestChannel_localNode(caplog):
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestSetting(caplog):
|
||||
# @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
|
||||
@@ -1278,7 +1290,7 @@ def test_requestChannel_localNode(caplog):
|
||||
# position_broadcast_smart: true
|
||||
# position_flags: 35
|
||||
# }
|
||||
#}"""
|
||||
# }"""
|
||||
# packet = {
|
||||
# 'from': 2475227164,
|
||||
# 'to': 2475227164,
|
||||
@@ -1324,8 +1336,8 @@ def test_requestChannel_localNode(caplog):
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_onResponseRequestSetting_with_error(capsys):
|
||||
# @pytest.mark.unit
|
||||
# def test_onResponseRequestSetting_with_error(capsys):
|
||||
# """Test onResponseRequestSetting() with an error"""
|
||||
# packet = {
|
||||
# 'from': 2475227164,
|
||||
@@ -1374,8 +1386,8 @@ def test_requestChannel_localNode(caplog):
|
||||
|
||||
|
||||
# TODO
|
||||
#@pytest.mark.unitslow
|
||||
#def test_waitForConfig():
|
||||
# @pytest.mark.unitslow
|
||||
# def test_waitForConfig():
|
||||
# """Test waitForConfig()"""
|
||||
# anode = Node('foo', 'bar')
|
||||
# radioConfig = RadioConfig()
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import logging
|
||||
import re
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
|
||||
from ..remote_hardware import RemoteHardwareClient, onGPIOreceive
|
||||
@@ -23,25 +23,25 @@ def test_RemoteHardwareClient():
|
||||
def test_onGPIOreceive(capsys):
|
||||
"""Test onGPIOreceive"""
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
packet = {'decoded': {'remotehw': {'typ': 'foo', 'gpioValue': '4096' }}}
|
||||
packet = {"decoded": {"remotehw": {"type": "foo", "gpioValue": "4096"}}}
|
||||
onGPIOreceive(packet, iface)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Received RemoteHardware', out)
|
||||
assert err == ''
|
||||
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:
|
||||
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 re.search(r"Warning: No channel named", out)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ def test_readGPIOs(caplog):
|
||||
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)
|
||||
rhw.readGPIOs("0x10", 123)
|
||||
assert re.search(r"readGPIOs", caplog.text, re.MULTILINE)
|
||||
iface.close()
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ def test_writeGPIOs(caplog):
|
||||
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)
|
||||
rhw.writeGPIOs("0x10", 123, 1)
|
||||
assert re.search(r"writeGPIOs", caplog.text, re.MULTILINE)
|
||||
iface.close()
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ def test_watchGPIOs(caplog):
|
||||
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)
|
||||
rhw.watchGPIOs("0x10", 123)
|
||||
assert re.search(r"watchGPIOs", caplog.text, re.MULTILINE)
|
||||
iface.close()
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ def test_watchGPIOs(caplog):
|
||||
def test_sendHardware_no_nodeid(capsys):
|
||||
"""Test sending no nodeid to _sendHardware()"""
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||
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
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Warning: Must use a destination node ID', out)
|
||||
assert err == ''
|
||||
assert re.search(r"Warning: Must use a destination node ID", out)
|
||||
assert err == ""
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
"""Meshtastic unit tests for serial_interface.py"""
|
||||
|
||||
import re
|
||||
from unittest.mock import mock_open, patch
|
||||
|
||||
|
||||
from unittest.mock import patch, mock_open
|
||||
import pytest
|
||||
|
||||
from ..serial_interface import SerialInterface
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("time.sleep")
|
||||
@patch("termios.tcsetattr")
|
||||
@patch("termios.tcgetattr")
|
||||
@patch("builtins.open", new_callable=mock_open, read_data="data")
|
||||
@patch('serial.Serial')
|
||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
|
||||
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_sleep, capsys):
|
||||
@patch("serial.Serial")
|
||||
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
|
||||
def test_SerialInterface_single_port(
|
||||
mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_sleep, capsys
|
||||
):
|
||||
"""Test that we can instantiate a SerialInterface with a single port"""
|
||||
iface = SerialInterface(noProto=True)
|
||||
iface.showInfo()
|
||||
@@ -28,15 +30,15 @@ def test_SerialInterface_single_port(mocked_findPorts, mocked_serial, mocked_ope
|
||||
mock_set.assert_called()
|
||||
mock_sleep.assert_called()
|
||||
out, err = capsys.readouterr()
|
||||
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 channel', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
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 channel", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('meshtastic.util.findPorts', return_value=[])
|
||||
@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:
|
||||
@@ -45,12 +47,14 @@ def test_SerialInterface_no_ports(mocked_findPorts, capsys):
|
||||
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 == ''
|
||||
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'])
|
||||
@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:
|
||||
@@ -59,5 +63,5 @@ def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
|
||||
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 == ''
|
||||
assert re.search(r"Warning: Multiple serial ports were detected", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"""Meshtastic smoke tests with a single device via USB"""
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
import platform
|
||||
import os
|
||||
|
||||
# Do not like using hard coded sleeps, but it probably makes
|
||||
# sense to pause for the radio at apprpriate times
|
||||
# sense to pause for the radio at appropriate times
|
||||
import pytest
|
||||
|
||||
from ..util import findPorts
|
||||
@@ -19,7 +19,7 @@ PAUSE_AFTER_REBOOT = 7
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_reboot():
|
||||
"""Test reboot"""
|
||||
return_value, _ = subprocess.getstatusoutput('meshtastic --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)
|
||||
@@ -28,94 +28,100 @@ def test_smoke1_reboot():
|
||||
@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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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.
|
||||
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)
|
||||
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)
|
||||
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'
|
||||
filename = "tmpoutput.txt"
|
||||
if os.path.exists(f"{filename}"):
|
||||
os.remove(f"{filename}")
|
||||
return_value, _ = subprocess.getstatusoutput(f'meshtastic --info --seriallog {filename}')
|
||||
return_value, _ = subprocess.getstatusoutput(
|
||||
f"meshtastic --info --seriallog {filename}"
|
||||
)
|
||||
assert os.path.exists(f"{filename}")
|
||||
assert return_value == 0
|
||||
os.remove(f"{filename}")
|
||||
@@ -124,10 +130,10 @@ def test_smoke1_seriallog_to_file():
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_qr():
|
||||
"""Test --qr"""
|
||||
filename = 'tmpqr'
|
||||
filename = "tmpqr"
|
||||
if os.path.exists(f"{filename}"):
|
||||
os.remove(f"{filename}")
|
||||
return_value, _ = subprocess.getstatusoutput(f'meshtastic --qr > {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
|
||||
@@ -139,20 +145,20 @@ def test_smoke1_qr():
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_nodes():
|
||||
"""Test --nodes"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --nodes')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
if platform.system() != 'Windows':
|
||||
assert re.search(r' User ', out, re.MULTILINE)
|
||||
assert re.search(r' 1 ', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --nodes")
|
||||
assert re.match(r"Connected to radio", out)
|
||||
if platform.system() != "Windows":
|
||||
assert re.search(r" 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)
|
||||
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
|
||||
|
||||
|
||||
@@ -164,27 +170,29 @@ def test_smoke1_port():
|
||||
# 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)
|
||||
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_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)
|
||||
"""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)
|
||||
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
|
||||
|
||||
|
||||
@@ -192,76 +200,80 @@ def test_smoke1_set_location_info():
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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_ch_set_modem_config():
|
||||
"""Test --ch-set modem_config"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config MedFast')
|
||||
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --ch-set modem_config MedFast"
|
||||
)
|
||||
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'MedFast', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||
assert not re.search(r"MedFast", 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 MedFast --ch-index 0')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Set modem_config to MedFast', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --ch-set modem_config MedFast --ch-index 0"
|
||||
)
|
||||
assert re.match(r"Connected to radio", out)
|
||||
assert re.search(r"^Set modem_config to MedFast", 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'MedFast', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||
assert re.search(r"MedFast", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_ch_values():
|
||||
"""Test --ch-vlongslow --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
|
||||
--ch-shortslow, and --ch-shortfast arguments
|
||||
--ch-shortslow, and --ch-shortfast arguments
|
||||
"""
|
||||
exp = {
|
||||
'--ch-vlongslow': '{ "psk": "AQ==" }',
|
||||
'--ch-longslow': 'LongSlow',
|
||||
'--ch-longfast': 'LongFast',
|
||||
'--ch-medslow': 'MedSlow',
|
||||
'--ch-medfast': 'MedFast',
|
||||
'--ch-shortslow': 'ShortSlow',
|
||||
'--ch-shortfast': 'ShortFast'
|
||||
}
|
||||
"--ch-vlongslow": '{ "psk": "AQ==" }',
|
||||
"--ch-longslow": "LongSlow",
|
||||
"--ch-longfast": "LongFast",
|
||||
"--ch-medslow": "MedSlow",
|
||||
"--ch-medfast": "MedFast",
|
||||
"--ch-shortslow": "ShortSlow",
|
||||
"--ch-shortfast": "ShortFast",
|
||||
}
|
||||
|
||||
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)
|
||||
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')
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||
assert re.search(val, out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
@@ -271,132 +283,144 @@ def test_smoke1_ch_values():
|
||||
@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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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')
|
||||
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')
|
||||
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)
|
||||
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')
|
||||
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)
|
||||
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')
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 1")
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
@@ -405,32 +429,32 @@ def test_smoke1_ch_enable_and_disable():
|
||||
@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)
|
||||
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)
|
||||
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')
|
||||
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')
|
||||
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)
|
||||
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)
|
||||
@@ -439,8 +463,8 @@ def test_smoke1_ch_del_a_disabled_non_primary_channel():
|
||||
@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)
|
||||
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)
|
||||
@@ -449,8 +473,10 @@ def test_smoke1_attempt_to_delete_primary_channel():
|
||||
@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)
|
||||
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)
|
||||
@@ -459,8 +485,10 @@ def test_smoke1_attempt_to_disable_primary_channel():
|
||||
@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)
|
||||
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)
|
||||
@@ -469,42 +497,42 @@ def test_smoke1_attempt_to_enable_primary_channel():
|
||||
@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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
@@ -513,42 +541,42 @@ def test_smoke1_ensure_ch_del_second_of_three_channels():
|
||||
@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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
@@ -558,22 +586,24 @@ def test_smoke1_ensure_ch_del_third_of_three_channels():
|
||||
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')
|
||||
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)
|
||||
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 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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||
assert re.search("CgUYAyIBAQ", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@@ -583,8 +613,8 @@ def test_smoke1_seturl_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 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)
|
||||
@@ -593,18 +623,18 @@ def test_smoke1_seturl_invalid_url():
|
||||
@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 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)
|
||||
_, 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 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)
|
||||
|
||||
@@ -612,41 +642,47 @@ def test_smoke1_configure():
|
||||
@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.
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -8,16 +8,16 @@ 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)
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -12,12 +12,14 @@ 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)
|
||||
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
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
This smoke test runs against that localhost.
|
||||
|
||||
"""
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
import platform
|
||||
import os
|
||||
|
||||
# Do not like using hard coded sleeps, but it probably makes
|
||||
# sense to pause for the radio at apprpriate times
|
||||
# sense to pause for the radio at appropriate times
|
||||
import pytest
|
||||
|
||||
from ..util import findPorts
|
||||
@@ -24,10 +24,10 @@ PAUSE_AFTER_COMMAND = 0.1
|
||||
PAUSE_AFTER_REBOOT = 0.2
|
||||
|
||||
|
||||
#TODO: need to fix the virtual device to have a reboot. When you issue the command
|
||||
# TODO: need to fix the virtual device to have a reboot. When you issue the command
|
||||
# below, you get "FIXME implement reboot for this platform"
|
||||
#@pytest.mark.smokevirt
|
||||
#def test_smokevirt_reboot():
|
||||
# @pytest.mark.smokevirt
|
||||
# def test_smokevirt_reboot():
|
||||
# """Test reboot"""
|
||||
# return_value, _ = subprocess.getstatusoutput('meshtastic --host localhost --reboot')
|
||||
# assert return_value == 0
|
||||
@@ -38,94 +38,110 @@ PAUSE_AFTER_REBOOT = 0.2
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_info():
|
||||
"""Test --info"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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.smokevirt
|
||||
def test_smokevirt_sendping():
|
||||
"""Test --sendping"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --sendping')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Sending ping message', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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.smokevirt
|
||||
def test_get_with_invalid_setting():
|
||||
"""Test '--get a_bad_setting'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --get a_bad_setting')
|
||||
assert re.search(r'Choices in sorted order', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --get a_bad_setting"
|
||||
)
|
||||
assert re.search(r"Choices in sorted order", out)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_set_with_invalid_setting():
|
||||
"""Test '--set a_bad_setting'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set a_bad_setting foo')
|
||||
assert re.search(r'Choices in sorted order', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --set a_bad_setting foo"
|
||||
)
|
||||
assert re.search(r"Choices in sorted order", out)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_ch_set_with_invalid_settingpatch_find_ports():
|
||||
"""Test '--ch-set with a_bad_setting'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set invalid_setting foo --ch-index 0')
|
||||
assert re.search(r'Choices in sorted order', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-set invalid_setting foo --ch-index 0"
|
||||
)
|
||||
assert re.search(r"Choices in sorted order", out)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_pos_fields():
|
||||
"""Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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.smokevirt
|
||||
def test_smokevirt_test_with_arg_but_no_hardware():
|
||||
"""Test --test
|
||||
Note: Since only one device is connected, it will not do much.
|
||||
Note: Since only one device is connected, it will not do much.
|
||||
"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --test')
|
||||
assert re.search(r'^Warning: Must have at least two devices', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --test")
|
||||
assert re.search(r"^Warning: Must have at least two devices", out, re.MULTILINE)
|
||||
assert return_value == 1
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_debug():
|
||||
"""Test --debug"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info --debug')
|
||||
assert re.search(r'^Owner', out, re.MULTILINE)
|
||||
assert re.search(r'^DEBUG file', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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.smokevirt
|
||||
def test_smokevirt_seriallog_to_file():
|
||||
"""Test --seriallog to a file creates a file"""
|
||||
filename = 'tmpoutput.txt'
|
||||
filename = "tmpoutput.txt"
|
||||
if os.path.exists(f"{filename}"):
|
||||
os.remove(f"{filename}")
|
||||
return_value, _ = subprocess.getstatusoutput(f'meshtastic --host localhost --info --seriallog {filename}')
|
||||
return_value, _ = subprocess.getstatusoutput(
|
||||
f"meshtastic --host localhost --info --seriallog {filename}"
|
||||
)
|
||||
assert os.path.exists(f"{filename}")
|
||||
assert return_value == 0
|
||||
os.remove(f"{filename}")
|
||||
@@ -134,10 +150,12 @@ def test_smokevirt_seriallog_to_file():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_qr():
|
||||
"""Test --qr"""
|
||||
filename = 'tmpqr'
|
||||
filename = "tmpqr"
|
||||
if os.path.exists(f"{filename}"):
|
||||
os.remove(f"{filename}")
|
||||
return_value, _ = subprocess.getstatusoutput(f'meshtastic --host localhost --qr > {filename}')
|
||||
return_value, _ = subprocess.getstatusoutput(
|
||||
f"meshtastic --host localhost --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
|
||||
@@ -149,20 +167,24 @@ def test_smokevirt_qr():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_nodes():
|
||||
"""Test --nodes"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --nodes')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
if platform.system() != 'Windows':
|
||||
assert re.search(r' User ', out, re.MULTILINE)
|
||||
assert re.search(r' 1 ', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --nodes"
|
||||
)
|
||||
assert re.match(r"Connected to radio", out)
|
||||
if platform.system() != "Windows":
|
||||
assert re.search(r" User ", out, re.MULTILINE)
|
||||
assert re.search(r" 1 ", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_send_hello():
|
||||
"""Test --sendtext hello"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --sendtext hello')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Sending text message hello to \^all', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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
|
||||
|
||||
|
||||
@@ -177,19 +199,23 @@ def test_smokevirt_port():
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_location_info():
|
||||
"""Test --setlat, --setlon and --setalt """
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
"""Test --setlat, --setlon and --setalt"""
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out2 = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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
|
||||
|
||||
|
||||
@@ -197,50 +223,58 @@ def test_smokevirt_set_location_info():
|
||||
def test_smokevirt_set_owner():
|
||||
"""Test --set-owner name"""
|
||||
# make sure the owner is not Joe
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-owner Bob')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Setting device owner to Bob', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert not re.search(r'Owner: Joe', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --set-owner Joe')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Setting device owner to Joe', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.search(r'Owner: Joe', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info")
|
||||
assert re.search(r"Owner: Joe", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_values():
|
||||
"""Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
|
||||
--ch-shortslow, and --ch-shortfast arguments
|
||||
--ch-shortslow, and --ch-shortfast arguments
|
||||
"""
|
||||
exp = {
|
||||
'--ch-longslow': 'LongSlow',
|
||||
'--ch-longfast': 'LongFast',
|
||||
'--ch-medslow': 'MedSlow',
|
||||
'--ch-medfast': 'MedFast',
|
||||
'--ch-shortslow': 'ShortSlow',
|
||||
'--ch-shortfast': 'ShortFast'
|
||||
}
|
||||
"--ch-longslow": "LongSlow",
|
||||
"--ch-longfast": "LongFast",
|
||||
"--ch-medslow": "MedSlow",
|
||||
"--ch-medfast": "MedFast",
|
||||
"--ch-shortslow": "ShortSlow",
|
||||
"--ch-shortfast": "ShortFast",
|
||||
}
|
||||
|
||||
for key, val in exp.items():
|
||||
return_value, out = subprocess.getstatusoutput(f'meshtastic --host localhost {key}')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
f"meshtastic --host localhost {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 --host localhost --info')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --info"
|
||||
)
|
||||
assert re.search(val, out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
@@ -250,144 +284,172 @@ def test_smokevirt_ch_values():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_set_name():
|
||||
"""Test --ch-set name"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
|
||||
assert not re.search(r'MyChannel', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-set name MyChannel')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.search(r'MyChannel', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info")
|
||||
assert re.search(r"MyChannel", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_set_downlink_and_uplink():
|
||||
"""Test -ch-set downlink_enabled X and --ch-set uplink_enabled X"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
# pylint: disable=C0301
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set downlink_enabled false --ch-set uplink_enabled false --ch-index 0')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert not re.search(r'uplinkEnabled', out, re.MULTILINE)
|
||||
assert not re.search(r'downlinkEnabled', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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)
|
||||
# pylint: disable=C0301
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.search(r'uplinkEnabled', out, re.MULTILINE)
|
||||
assert re.search(r'downlinkEnabled', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info")
|
||||
assert re.search(r"uplinkEnabled", out, re.MULTILINE)
|
||||
assert re.search(r"downlinkEnabled", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_add_and_ch_del():
|
||||
"""Test --ch-add"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
|
||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
|
||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-index 1 --ch-del')
|
||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 secondary channel is not there
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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.smokevirt
|
||||
def test_smokevirt_ch_enable_and_disable():
|
||||
"""Test --ch-enable and --ch-disable"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
|
||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
|
||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-disable')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-disable"
|
||||
)
|
||||
assert return_value == 1
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-disable --ch-index 1')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-disable --ch-index 1"
|
||||
)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-enable --ch-index 1')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-enable --ch-index 1"
|
||||
)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-del --ch-index 1')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-del --ch-index 1"
|
||||
)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
@@ -396,37 +458,45 @@ def test_smokevirt_ch_enable_and_disable():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_del_a_disabled_non_primary_channel():
|
||||
"""Test --ch-del will work on a disabled non-primary channel."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
|
||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
|
||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-disable')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-disable"
|
||||
)
|
||||
assert return_value == 1
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 1')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-del --ch-index 1"
|
||||
)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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)
|
||||
@@ -435,8 +505,10 @@ def test_smokevirt_ch_del_a_disabled_non_primary_channel():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_attempt_to_delete_primary_channel():
|
||||
"""Test that we cannot delete the PRIMARY channel."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 0')
|
||||
assert re.search(r'Warning: Cannot delete primary channel', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
@@ -445,8 +517,10 @@ def test_smokevirt_attempt_to_delete_primary_channel():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_attempt_to_disable_primary_channel():
|
||||
"""Test that we cannot disable the PRIMARY channel."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-disable --ch-index 0')
|
||||
assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
@@ -455,8 +529,10 @@ def test_smokevirt_attempt_to_disable_primary_channel():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_attempt_to_enable_primary_channel():
|
||||
"""Test that we cannot enable the PRIMARY channel."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-enable --ch-index 0')
|
||||
assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
@@ -465,42 +541,50 @@ def test_smokevirt_attempt_to_enable_primary_channel():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_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 --host localhost --ch-add testing1')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-add testing2')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'testing2', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-del --ch-index 1')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'testing2', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-del --ch-index 1')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
@@ -509,42 +593,50 @@ def test_smokevirt_ensure_ch_del_second_of_three_channels():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_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 --host localhost --ch-add testing1')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-add testing2')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'testing2', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-del --ch-index 2')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'testing1', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-del --ch-index 1')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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)
|
||||
@@ -553,24 +645,28 @@ def test_smokevirt_ensure_ch_del_third_of_three_channels():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_set_modem_config():
|
||||
"""Test --ch-set modem_config"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set modem_config Bw31_25Cr48Sf512')
|
||||
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --ch-set modem_config MidSlow --ch-index 0')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Set modem_config to MidSlow', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --ch-set modem_config MidSlow --ch-index 0"
|
||||
)
|
||||
assert re.match(r"Connected to radio", out)
|
||||
assert re.search(r"^Set modem_config to MidSlow", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
|
||||
assert re.search(r'MidSlow', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info")
|
||||
assert re.search(r"MidSlow", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@@ -578,22 +674,26 @@ def test_smokevirt_ch_set_modem_config():
|
||||
def test_smokevirt_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 --host localhost --ch-set name foo --ch-index 0')
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert not re.search('CgUYAyIBAQ', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --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 --host localhost --seturl {url}")
|
||||
assert re.match(r'Connected to radio', out)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
f"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.search('CgUYAyIBAQ', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info")
|
||||
assert re.search("CgUYAyIBAQ", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@@ -602,9 +702,11 @@ def test_smokevirt_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 --host localhost --seturl {url}")
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search('Warning: There were no settings', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
f"meshtastic --host localhost --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)
|
||||
@@ -613,19 +715,21 @@ def test_smokevirt_seturl_invalid_url():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_configure():
|
||||
"""Test --configure"""
|
||||
_ , out = subprocess.getstatusoutput(f"meshtastic --host localhost --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)
|
||||
_, out = subprocess.getstatusoutput(
|
||||
f"meshtastic --host localhost --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)
|
||||
|
||||
@@ -633,41 +737,49 @@ def test_smokevirt_configure():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_ham():
|
||||
"""Test --set-ham
|
||||
Note: Do a factory reset after this setting so it is very short-lived.
|
||||
Note: Do a factory reset after this setting so it is very short-lived.
|
||||
"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-ham KI1234')
|
||||
assert re.search(r'Setting Ham ID', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 --host localhost --info')
|
||||
assert re.search(r'Owner: KI1234', out, re.MULTILINE)
|
||||
return_value, out = subprocess.getstatusoutput("meshtastic --host localhost --info")
|
||||
assert re.search(r"Owner: KI1234", out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_wifi_settings():
|
||||
"""Test --set wifi_ssid and --set wifi_password"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
'meshtastic --host localhost --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 --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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.smokevirt
|
||||
def test_smokevirt_factory_reset():
|
||||
"""Test factory reset"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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)
|
||||
return_value, out = subprocess.getstatusoutput(
|
||||
"meshtastic --host localhost --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 virtual radio will not respond well after this command. Need to re-start the virtual program at this point.
|
||||
# TODO: fix?
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
"""Meshtastic unit tests for stream_interface.py"""
|
||||
|
||||
import logging
|
||||
#import re
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from ..stream_interface import StreamInterface
|
||||
|
||||
# import re
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_StreamInterface():
|
||||
@@ -22,10 +23,10 @@ def test_StreamInterface():
|
||||
@pytest.mark.usefixtures("reset_globals")
|
||||
def test_StreamInterface_with_noProto(caplog):
|
||||
"""Test that we can instantiate a StreamInterface based on nonProto
|
||||
and we can read/write bytes from a mocked stream
|
||||
and we can read/write bytes from a mocked stream
|
||||
"""
|
||||
stream = MagicMock()
|
||||
test_data = b'hello'
|
||||
test_data = b"hello"
|
||||
stream.read.return_value = test_data
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
iface = StreamInterface(noProto=True, connectNow=False)
|
||||
@@ -39,9 +40,9 @@ def test_StreamInterface_with_noProto(caplog):
|
||||
### 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
|
||||
#@pytest.mark.usefixtures("reset_globals")
|
||||
#def test_sendToRadioImpl(caplog):
|
||||
# @pytest.mark.unitslow
|
||||
# @pytest.mark.usefixtures("reset_globals")
|
||||
# def test_sendToRadioImpl(caplog):
|
||||
# """Test _sendToRadioImpl()"""
|
||||
#
|
||||
## def add_header(b):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Meshtastic unit tests for tcp_interface.py"""
|
||||
|
||||
import re
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from ..tcp_interface import TCPInterface
|
||||
@@ -11,18 +11,18 @@ 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)
|
||||
with patch("socket.socket") as mock_socket:
|
||||
iface = TCPInterface(hostname="localhost", noProto=True)
|
||||
iface.myConnect()
|
||||
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 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()
|
||||
|
||||
@@ -34,10 +34,12 @@ def test_TCPInterface_exception():
|
||||
def throw_an_exception():
|
||||
raise ValueError("Fake exception.")
|
||||
|
||||
with patch('meshtastic.tcp_interface.TCPInterface._socket_shutdown') as mock_shutdown:
|
||||
with patch(
|
||||
"meshtastic.tcp_interface.TCPInterface._socket_shutdown"
|
||||
) as mock_shutdown:
|
||||
mock_shutdown.side_effect = throw_an_exception
|
||||
with patch('socket.socket') as mock_socket:
|
||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
||||
with patch("socket.socket") as mock_socket:
|
||||
iface = TCPInterface(hostname="localhost", noProto=True)
|
||||
iface.myConnect()
|
||||
iface.close()
|
||||
assert mock_socket.called
|
||||
@@ -47,6 +49,6 @@ def test_TCPInterface_exception():
|
||||
@pytest.mark.unit
|
||||
def test_TCPInterface_without_connecting():
|
||||
"""Test that we can instantiate a TCPInterface with connectNow as false"""
|
||||
with patch('socket.socket'):
|
||||
iface = TCPInterface(hostname='localhost', noProto=True, connectNow=False)
|
||||
with patch("socket.socket"):
|
||||
iface = TCPInterface(hostname="localhost", noProto=True, connectNow=False)
|
||||
assert iface.socket is None
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
"""Meshtastic unit tests for tunnel.py"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
import sys
|
||||
import logging
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
|
||||
from ..globals import Globals
|
||||
from ..tcp_interface import TCPInterface
|
||||
from ..tunnel import Tunnel, onTunnelReceive
|
||||
from ..globals import Globals
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_Tunnel_on_non_linux_system(mock_platform_system):
|
||||
"""Test that we cannot instantiate a Tunnel on a non Linux system"""
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'notLinux'
|
||||
a_mock.return_value = "notLinux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with patch('socket.socket') as mock_socket:
|
||||
with patch("socket.socket") as mock_socket:
|
||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
||||
iface = TCPInterface(hostname="localhost", noProto=True)
|
||||
Tunnel(iface)
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
assert mock_socket.called
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_Tunnel_without_interface(mock_platform_system):
|
||||
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||
Tunnel(None)
|
||||
@@ -40,227 +40,235 @@ def test_Tunnel_without_interface(mock_platform_system):
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_Tunnel_with_interface(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.WARNING):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
assert tun == Globals.getInstance().get_tunnelInstance()
|
||||
iface.close()
|
||||
assert re.search(r'Not creating a TapDevice()', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'Not starting TUN reader', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"Not creating a TapDevice()", caplog.text, re.MULTILINE)
|
||||
assert re.search(r"Not starting TUN reader", caplog.text, re.MULTILINE)
|
||||
assert re.search(r"Not sending packet", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test onTunnelReceive"""
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
sys.argv = ['']
|
||||
sys.argv = [""]
|
||||
Globals.getInstance().set_args(sys.argv)
|
||||
packet = {'decoded': { 'payload': 'foo'}, 'from': 2475227164}
|
||||
packet = {"decoded": {"payload": "foo"}, "from": 2475227164}
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
Globals.getInstance().set_tunnelInstance(tun)
|
||||
onTunnelReceive(packet, iface)
|
||||
assert re.search(r'in onTunnelReceive', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'Ignoring message we sent', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"in onTunnelReceive", caplog.text, re.MULTILINE)
|
||||
assert re.search(r"Ignoring message we sent", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('platform.system')
|
||||
def test_onTunnelReceive_from_someone_else(mock_platform_system, caplog, iface_with_nodes):
|
||||
@patch("platform.system")
|
||||
def test_onTunnelReceive_from_someone_else(
|
||||
mock_platform_system, caplog, iface_with_nodes
|
||||
):
|
||||
"""Test onTunnelReceive"""
|
||||
iface = iface_with_nodes
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
sys.argv = ['']
|
||||
sys.argv = [""]
|
||||
Globals.getInstance().set_args(sys.argv)
|
||||
packet = {'decoded': { 'payload': 'foo'}, 'from': 123}
|
||||
packet = {"decoded": {"payload": "foo"}, "from": 123}
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
Globals.getInstance().set_tunnelInstance(tun)
|
||||
onTunnelReceive(packet, iface)
|
||||
assert re.search(r'in onTunnelReceive', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"in onTunnelReceive", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_shouldFilterPacket_random(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test _shouldFilterPacket()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
# random packet
|
||||
packet = b'1234567890123456789012345678901234567890'
|
||||
packet = b"1234567890123456789012345678901234567890"
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
ignore = tun._shouldFilterPacket(packet)
|
||||
assert not ignore
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
def test_shouldFilterPacket_in_blacklist(mock_platform_system, caplog, iface_with_nodes):
|
||||
@patch("platform.system")
|
||||
def test_shouldFilterPacket_in_blacklist(
|
||||
mock_platform_system, caplog, iface_with_nodes
|
||||
):
|
||||
"""Test _shouldFilterPacket()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
# faked IGMP
|
||||
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
ignore = tun._shouldFilterPacket(packet)
|
||||
assert ignore
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_shouldFilterPacket_icmp(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test _shouldFilterPacket()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
# faked ICMP
|
||||
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
ignore = tun._shouldFilterPacket(packet)
|
||||
assert re.search(r'forwarding ICMP message', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"forwarding ICMP message", caplog.text, re.MULTILINE)
|
||||
assert not ignore
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_shouldFilterPacket_udp(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test _shouldFilterPacket()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
# faked UDP
|
||||
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
ignore = tun._shouldFilterPacket(packet)
|
||||
assert re.search(r'forwarding udp', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"forwarding udp", caplog.text, re.MULTILINE)
|
||||
assert not ignore
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
def test_shouldFilterPacket_udp_blacklisted(mock_platform_system, caplog, iface_with_nodes):
|
||||
@patch("platform.system")
|
||||
def test_shouldFilterPacket_udp_blacklisted(
|
||||
mock_platform_system, caplog, iface_with_nodes
|
||||
):
|
||||
"""Test _shouldFilterPacket()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
# faked UDP
|
||||
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x6c\x07\x6c\x00\x00\x00'
|
||||
packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x6c\x07\x6c\x00\x00\x00"
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
# Note: custom logging level
|
||||
LOG_TRACE = 5
|
||||
with caplog.at_level(LOG_TRACE):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
ignore = tun._shouldFilterPacket(packet)
|
||||
assert re.search(r'ignoring blacklisted UDP', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"ignoring blacklisted UDP", caplog.text, re.MULTILINE)
|
||||
assert ignore
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_shouldFilterPacket_tcp(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test _shouldFilterPacket()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
# faked TCP
|
||||
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
ignore = tun._shouldFilterPacket(packet)
|
||||
assert re.search(r'forwarding tcp', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"forwarding tcp", caplog.text, re.MULTILINE)
|
||||
assert not ignore
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
def test_shouldFilterPacket_tcp_blacklisted(mock_platform_system, caplog, iface_with_nodes):
|
||||
@patch("platform.system")
|
||||
def test_shouldFilterPacket_tcp_blacklisted(
|
||||
mock_platform_system, caplog, iface_with_nodes
|
||||
):
|
||||
"""Test _shouldFilterPacket()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
# faked TCP
|
||||
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x0c\x17\x0c\x00\x00\x00'
|
||||
packet = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x0c\x17\x0c\x00\x00\x00"
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
# Note: custom logging level
|
||||
LOG_TRACE = 5
|
||||
with caplog.at_level(LOG_TRACE):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
ignore = tun._shouldFilterPacket(packet)
|
||||
assert re.search(r'ignoring blacklisted TCP', caplog.text, re.MULTILINE)
|
||||
assert re.search(r"ignoring blacklisted TCP", caplog.text, re.MULTILINE)
|
||||
assert ignore
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_ipToNodeId_none(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test _ipToNodeId()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
nodeid = tun._ipToNodeId('something not useful')
|
||||
nodeid = tun._ipToNodeId("something not useful")
|
||||
assert nodeid is None
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('platform.system')
|
||||
@patch("platform.system")
|
||||
def test_ipToNodeId_all(mock_platform_system, caplog, iface_with_nodes):
|
||||
"""Test _ipToNodeId()"""
|
||||
iface = iface_with_nodes
|
||||
iface.noProto = True
|
||||
a_mock = MagicMock()
|
||||
a_mock.return_value = 'Linux'
|
||||
a_mock.return_value = "Linux"
|
||||
mock_platform_system.side_effect = a_mock
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
with patch('socket.socket'):
|
||||
with patch("socket.socket"):
|
||||
tun = Tunnel(iface)
|
||||
nodeid = tun._ipToNodeId(b'\x00\x00\xff\xff')
|
||||
assert nodeid == '^all'
|
||||
nodeid = tun._ipToNodeId(b"\x00\x00\xff\xff")
|
||||
assert nodeid == "^all"
|
||||
|
||||
@@ -1,114 +1,131 @@
|
||||
"""Meshtastic unit tests for util.py"""
|
||||
|
||||
import re
|
||||
import logging
|
||||
|
||||
import re
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
|
||||
support_info, genPSK256, fromStr, fromPSK,
|
||||
quoteBooleans, catchAndIgnore,
|
||||
remove_keys_from_dict, Timeout, hexstr,
|
||||
ipstr, readnet_u16, findPorts, convert_mac_addr,
|
||||
snake_to_camel, camel_to_snake, eliminate_duplicate_port,
|
||||
is_windows11, active_ports_on_supported_devices)
|
||||
|
||||
from meshtastic.supported_device import SupportedDevice
|
||||
from meshtastic.util import (
|
||||
Timeout,
|
||||
active_ports_on_supported_devices,
|
||||
camel_to_snake,
|
||||
catchAndIgnore,
|
||||
convert_mac_addr,
|
||||
eliminate_duplicate_port,
|
||||
findPorts,
|
||||
fixme,
|
||||
fromPSK,
|
||||
fromStr,
|
||||
genPSK256,
|
||||
hexstr,
|
||||
ipstr,
|
||||
is_windows11,
|
||||
our_exit,
|
||||
pskToString,
|
||||
quoteBooleans,
|
||||
readnet_u16,
|
||||
remove_keys_from_dict,
|
||||
snake_to_camel,
|
||||
stripnl,
|
||||
support_info,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_genPSK256():
|
||||
"""Test genPSK256"""
|
||||
assert 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'
|
||||
assert fromStr('123456789') == 123456789
|
||||
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"
|
||||
assert fromStr("123456789") == 123456789
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
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'"
|
||||
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'
|
||||
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'
|
||||
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'
|
||||
assert pskToString("") == "unencrypted"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_string():
|
||||
"""Test pskToString string"""
|
||||
assert pskToString('hunter123') == 'secret'
|
||||
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'
|
||||
assert pskToString(bytes([0x00])) == "unencrypted"
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_pskToString_one_byte_non_zero_value():
|
||||
"""Test pskToString one byte that is non-zero"""
|
||||
assert pskToString(bytes([0x01])) == 'default'
|
||||
assert pskToString(bytes([0x01])) == "default"
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_pskToString_many_bytes():
|
||||
"""Test pskToString many bytes"""
|
||||
assert pskToString(bytes([0x02, 0x01])) == 'secret'
|
||||
assert pskToString(bytes([0x02, 0x01])) == "secret"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_simple():
|
||||
"""Test pskToString simple"""
|
||||
assert pskToString(bytes([0x03])) == 'simple2'
|
||||
assert pskToString(bytes([0x03])) == "simple2"
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@@ -117,8 +134,8 @@ def test_our_exit_zero_return_value(capsys):
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
our_exit("Warning: Some message", 0)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Warning: Some message', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
assert re.search(r"Warning: Some message", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 0
|
||||
|
||||
@@ -129,8 +146,8 @@ def test_our_exit_non_zero_return_value(capsys):
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
our_exit("Error: Some message", 1)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Error: Some message', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
assert re.search(r"Error: Some message", out, re.MULTILINE)
|
||||
assert err == ""
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 1
|
||||
|
||||
@@ -148,21 +165,23 @@ 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 == ''
|
||||
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')
|
||||
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)
|
||||
assert re.search(r"Exception thrown in something", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@@ -174,35 +193,35 @@ def test_remove_keys_from_dict_empty_keys_empty_dict():
|
||||
@pytest.mark.unitslow
|
||||
def test_remove_keys_from_dict_empty_dict():
|
||||
"""Test when dict is empty"""
|
||||
assert not remove_keys_from_dict(('a'), {})
|
||||
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}
|
||||
assert remove_keys_from_dict((), {"a": 1}) == {"a": 1}
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_remove_keys_from_dict():
|
||||
"""Test remove_keys_from_dict()"""
|
||||
assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1}
|
||||
assert remove_keys_from_dict(("b"), {"a": 1, "b": 2}) == {"a": 1}
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_remove_keys_from_dict_multiple_keys():
|
||||
"""Test remove_keys_from_dict()"""
|
||||
keys = ('a', 'b')
|
||||
adict = {'a': 1, 'b': 2, 'c': 3}
|
||||
assert remove_keys_from_dict(keys, adict) == {'c':3}
|
||||
keys = ("a", "b")
|
||||
adict = {"a": 1, "b": 2, "c": 3}
|
||||
assert remove_keys_from_dict(keys, adict) == {"c": 3}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_remove_keys_from_dict_nested():
|
||||
"""Test remove_keys_from_dict()"""
|
||||
keys = ('b')
|
||||
adict = {'a': {'b': 1}, 'b': 2, 'c': 3}
|
||||
exp = {'a': {}, 'c': 3}
|
||||
keys = "b"
|
||||
adict = {"a": {"b": 1}, "b": 2, "c": 3}
|
||||
exp = {"a": {}, "c": 3}
|
||||
assert remove_keys_from_dict(keys, adict) == exp
|
||||
|
||||
|
||||
@@ -210,8 +229,8 @@ def test_remove_keys_from_dict_nested():
|
||||
def test_Timeout_not_found():
|
||||
"""Test Timeout()"""
|
||||
to = Timeout(0.2)
|
||||
attrs = ('foo')
|
||||
to.waitForSet('bar', attrs)
|
||||
attrs = "foo"
|
||||
to.waitForSet("bar", attrs)
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@@ -219,31 +238,31 @@ def test_Timeout_found():
|
||||
"""Test Timeout()"""
|
||||
to = Timeout(0.2)
|
||||
attrs = ()
|
||||
to.waitForSet('bar', attrs)
|
||||
to.waitForSet("bar", attrs)
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_hexstr():
|
||||
"""Test hexstr()"""
|
||||
assert hexstr(b'123') == '31:32:33'
|
||||
assert hexstr(b'') == ''
|
||||
assert hexstr(b"123") == "31:32:33"
|
||||
assert hexstr(b"") == ""
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_ipstr():
|
||||
"""Test ipstr()"""
|
||||
assert ipstr(b'1234') == '49.50.51.52'
|
||||
assert ipstr(b'') == ''
|
||||
assert ipstr(b"1234") == "49.50.51.52"
|
||||
assert ipstr(b"") == ""
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_readnet_u16():
|
||||
"""Test readnet_u16()"""
|
||||
assert readnet_u16(b'123456', 2) == 13108
|
||||
assert readnet_u16(b"123456", 2) == 13108
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('serial.tools.list_ports.comports', return_value=[])
|
||||
@patch("serial.tools.list_ports.comports", return_value=[])
|
||||
def test_findPorts_when_none_found(patch_comports):
|
||||
"""Test findPorts()"""
|
||||
assert not findPorts()
|
||||
@@ -251,99 +270,134 @@ def test_findPorts_when_none_found(patch_comports):
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('serial.tools.list_ports.comports')
|
||||
@patch("serial.tools.list_ports.comports")
|
||||
def test_findPorts_when_duplicate_found_and_duplicate_option_used(patch_comports):
|
||||
"""Test findPorts()"""
|
||||
|
||||
class TempPort:
|
||||
""" temp class for port"""
|
||||
"""temp class for port"""
|
||||
|
||||
def __init__(self, device=None, vid=None):
|
||||
self.device = device
|
||||
self.vid = vid
|
||||
fake1 = TempPort('/dev/cu.usbserial-1430', vid='fake1')
|
||||
fake2 = TempPort('/dev/cu.wchusbserial1430', vid='fake2')
|
||||
|
||||
fake1 = TempPort("/dev/cu.usbserial-1430", vid="fake1")
|
||||
fake2 = TempPort("/dev/cu.wchusbserial1430", vid="fake2")
|
||||
patch_comports.return_value = [fake1, fake2]
|
||||
assert findPorts(eliminate_duplicates=True) == ['/dev/cu.wchusbserial1430']
|
||||
assert findPorts(eliminate_duplicates=True) == ["/dev/cu.wchusbserial1430"]
|
||||
patch_comports.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('serial.tools.list_ports.comports')
|
||||
def test_findPorts_when_duplicate_found_and_duplicate_option_used_ports_reversed(patch_comports):
|
||||
@patch("serial.tools.list_ports.comports")
|
||||
def test_findPorts_when_duplicate_found_and_duplicate_option_used_ports_reversed(
|
||||
patch_comports,
|
||||
):
|
||||
"""Test findPorts()"""
|
||||
|
||||
class TempPort:
|
||||
""" temp class for port"""
|
||||
"""temp class for port"""
|
||||
|
||||
def __init__(self, device=None, vid=None):
|
||||
self.device = device
|
||||
self.vid = vid
|
||||
fake1 = TempPort('/dev/cu.usbserial-1430', vid='fake1')
|
||||
fake2 = TempPort('/dev/cu.wchusbserial1430', vid='fake2')
|
||||
|
||||
fake1 = TempPort("/dev/cu.usbserial-1430", vid="fake1")
|
||||
fake2 = TempPort("/dev/cu.wchusbserial1430", vid="fake2")
|
||||
patch_comports.return_value = [fake2, fake1]
|
||||
assert findPorts(eliminate_duplicates=True) == ['/dev/cu.wchusbserial1430']
|
||||
assert findPorts(eliminate_duplicates=True) == ["/dev/cu.wchusbserial1430"]
|
||||
patch_comports.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@patch('serial.tools.list_ports.comports')
|
||||
@patch("serial.tools.list_ports.comports")
|
||||
def test_findPorts_when_duplicate_found_and_duplicate_option_not_used(patch_comports):
|
||||
"""Test findPorts()"""
|
||||
|
||||
class TempPort:
|
||||
""" temp class for port"""
|
||||
"""temp class for port"""
|
||||
|
||||
def __init__(self, device=None, vid=None):
|
||||
self.device = device
|
||||
self.vid = vid
|
||||
fake1 = TempPort('/dev/cu.usbserial-1430', vid='fake1')
|
||||
fake2 = TempPort('/dev/cu.wchusbserial1430', vid='fake2')
|
||||
|
||||
fake1 = TempPort("/dev/cu.usbserial-1430", vid="fake1")
|
||||
fake2 = TempPort("/dev/cu.wchusbserial1430", vid="fake2")
|
||||
patch_comports.return_value = [fake1, fake2]
|
||||
assert findPorts() == ['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430']
|
||||
assert findPorts() == ["/dev/cu.usbserial-1430", "/dev/cu.wchusbserial1430"]
|
||||
patch_comports.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
def test_convert_mac_addr():
|
||||
"""Test convert_mac_addr()"""
|
||||
assert convert_mac_addr('/c0gFyhb') == 'fd:cd:20:17:28:5b'
|
||||
assert convert_mac_addr('fd:cd:20:17:28:5b') == 'fd:cd:20:17:28:5b'
|
||||
assert convert_mac_addr('') == ''
|
||||
assert convert_mac_addr("/c0gFyhb") == "fd:cd:20:17:28:5b"
|
||||
assert convert_mac_addr("fd:cd:20:17:28:5b") == "fd:cd:20:17:28:5b"
|
||||
assert convert_mac_addr("") == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_snake_to_camel():
|
||||
"""Test snake_to_camel"""
|
||||
assert snake_to_camel('') == ''
|
||||
assert snake_to_camel('foo') == 'foo'
|
||||
assert snake_to_camel('foo_bar') == 'fooBar'
|
||||
assert snake_to_camel('fooBar') == 'fooBar'
|
||||
assert snake_to_camel("") == ""
|
||||
assert snake_to_camel("foo") == "foo"
|
||||
assert snake_to_camel("foo_bar") == "fooBar"
|
||||
assert snake_to_camel("fooBar") == "fooBar"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_camel_to_snake():
|
||||
"""Test camel_to_snake"""
|
||||
assert camel_to_snake('') == ''
|
||||
assert camel_to_snake('foo') == 'foo'
|
||||
assert camel_to_snake('Foo') == 'foo'
|
||||
assert camel_to_snake('fooBar') == 'foo_bar'
|
||||
assert camel_to_snake('fooBarBaz') == 'foo_bar_baz'
|
||||
assert camel_to_snake("") == ""
|
||||
assert camel_to_snake("foo") == "foo"
|
||||
assert camel_to_snake("Foo") == "foo"
|
||||
assert camel_to_snake("fooBar") == "foo_bar"
|
||||
assert camel_to_snake("fooBarBaz") == "foo_bar_baz"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_eliminate_duplicate_port():
|
||||
"""Test eliminate_duplicate_port()"""
|
||||
assert not eliminate_duplicate_port([])
|
||||
assert eliminate_duplicate_port(['/dev/fake']) == ['/dev/fake']
|
||||
assert eliminate_duplicate_port(['/dev/fake', '/dev/fake1']) == ['/dev/fake', '/dev/fake1']
|
||||
assert eliminate_duplicate_port(['/dev/fake', '/dev/fake1', '/dev/fake2']) == ['/dev/fake', '/dev/fake1', '/dev/fake2']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430']) == ['/dev/cu.wchusbserial1430']
|
||||
assert eliminate_duplicate_port(['/dev/cu.wchusbserial1430', '/dev/cu.usbserial-1430']) == ['/dev/cu.wchusbserial1430']
|
||||
assert eliminate_duplicate_port(['/dev/cu.SLAB_USBtoUART', '/dev/cu.usbserial-0001']) == ['/dev/cu.usbserial-0001']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbserial-0001', '/dev/cu.SLAB_USBtoUART']) == ['/dev/cu.usbserial-0001']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbmodem11301', '/dev/cu.wchusbserial11301']) == ['/dev/cu.wchusbserial11301']
|
||||
assert eliminate_duplicate_port(['/dev/cu.wchusbserial11301', '/dev/cu.usbmodem11301']) == ['/dev/cu.wchusbserial11301']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbmodem53230051441', '/dev/cu.wchusbserial53230051441']) == ['/dev/cu.wchusbserial53230051441']
|
||||
assert eliminate_duplicate_port(['/dev/cu.wchusbserial53230051441', '/dev/cu.usbmodem53230051441']) == ['/dev/cu.wchusbserial53230051441']
|
||||
assert eliminate_duplicate_port(["/dev/fake"]) == ["/dev/fake"]
|
||||
assert eliminate_duplicate_port(["/dev/fake", "/dev/fake1"]) == [
|
||||
"/dev/fake",
|
||||
"/dev/fake1",
|
||||
]
|
||||
assert eliminate_duplicate_port(["/dev/fake", "/dev/fake1", "/dev/fake2"]) == [
|
||||
"/dev/fake",
|
||||
"/dev/fake1",
|
||||
"/dev/fake2",
|
||||
]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.usbserial-1430", "/dev/cu.wchusbserial1430"]
|
||||
) == ["/dev/cu.wchusbserial1430"]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.wchusbserial1430", "/dev/cu.usbserial-1430"]
|
||||
) == ["/dev/cu.wchusbserial1430"]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.SLAB_USBtoUART", "/dev/cu.usbserial-0001"]
|
||||
) == ["/dev/cu.usbserial-0001"]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.usbserial-0001", "/dev/cu.SLAB_USBtoUART"]
|
||||
) == ["/dev/cu.usbserial-0001"]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.usbmodem11301", "/dev/cu.wchusbserial11301"]
|
||||
) == ["/dev/cu.wchusbserial11301"]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.wchusbserial11301", "/dev/cu.usbmodem11301"]
|
||||
) == ["/dev/cu.wchusbserial11301"]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.usbmodem53230051441", "/dev/cu.wchusbserial53230051441"]
|
||||
) == ["/dev/cu.wchusbserial53230051441"]
|
||||
assert eliminate_duplicate_port(
|
||||
["/dev/cu.wchusbserial53230051441", "/dev/cu.usbmodem53230051441"]
|
||||
) == ["/dev/cu.wchusbserial53230051441"]
|
||||
|
||||
@patch('platform.version', return_value='10.0.22000.194')
|
||||
@patch('platform.release', return_value='10')
|
||||
@patch('platform.system', return_value='Windows')
|
||||
|
||||
@patch("platform.version", return_value="10.0.22000.194")
|
||||
@patch("platform.release", return_value="10")
|
||||
@patch("platform.system", return_value="Windows")
|
||||
def test_is_windows11_true(patched_platform, patched_release, patched_version):
|
||||
"""Test is_windows11()"""
|
||||
assert is_windows11() is True
|
||||
@@ -352,9 +406,9 @@ def test_is_windows11_true(patched_platform, patched_release, patched_version):
|
||||
patched_version.assert_called()
|
||||
|
||||
|
||||
@patch('platform.version', return_value='10.0.a2200.foo') # made up
|
||||
@patch('platform.release', return_value='10')
|
||||
@patch('platform.system', return_value='Windows')
|
||||
@patch("platform.version", return_value="10.0.a2200.foo") # made up
|
||||
@patch("platform.release", return_value="10")
|
||||
@patch("platform.system", return_value="Windows")
|
||||
def test_is_windows11_true2(patched_platform, patched_release, patched_version):
|
||||
"""Test is_windows11()"""
|
||||
assert is_windows11() is False
|
||||
@@ -363,9 +417,9 @@ def test_is_windows11_true2(patched_platform, patched_release, patched_version):
|
||||
patched_version.assert_called()
|
||||
|
||||
|
||||
@patch('platform.version', return_value='10.0.17763') # windows 10 home
|
||||
@patch('platform.release', return_value='10')
|
||||
@patch('platform.system', return_value='Windows')
|
||||
@patch("platform.version", return_value="10.0.17763") # windows 10 home
|
||||
@patch("platform.release", return_value="10")
|
||||
@patch("platform.system", return_value="Windows")
|
||||
def test_is_windows11_false(patched_platform, patched_release, patched_version):
|
||||
"""Test is_windows11()"""
|
||||
assert is_windows11() is False
|
||||
@@ -374,8 +428,8 @@ def test_is_windows11_false(patched_platform, patched_release, patched_version):
|
||||
patched_version.assert_called()
|
||||
|
||||
|
||||
@patch('platform.release', return_value='8.1')
|
||||
@patch('platform.system', return_value='Windows')
|
||||
@patch("platform.release", return_value="8.1")
|
||||
@patch("platform.system", return_value="Windows")
|
||||
def test_is_windows11_false_win8_1(patched_platform, patched_release):
|
||||
"""Test is_windows11()"""
|
||||
assert is_windows11() is False
|
||||
@@ -384,7 +438,7 @@ def test_is_windows11_false_win8_1(patched_platform, patched_release):
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('platform.system', return_value='Linux')
|
||||
@patch("platform.system", return_value="Linux")
|
||||
def test_active_ports_on_supported_devices_empty(mock_platform):
|
||||
"""Test active_ports_on_supported_devices()"""
|
||||
sds = set()
|
||||
@@ -393,66 +447,101 @@ def test_active_ports_on_supported_devices_empty(mock_platform):
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('subprocess.getstatusoutput')
|
||||
@patch('platform.system', return_value='Linux')
|
||||
@patch("subprocess.getstatusoutput")
|
||||
@patch("platform.system", return_value="Linux")
|
||||
def test_active_ports_on_supported_devices_linux(mock_platform, mock_sp):
|
||||
"""Test active_ports_on_supported_devices()"""
|
||||
mock_sp.return_value = (None, 'crw-rw-rw- 1 root wheel 0x9000000 Feb 8 22:22 /dev/ttyUSBfake')
|
||||
fake_device = SupportedDevice(name='a', for_firmware='heltec-v2.1', baseport_on_linux='ttyUSB')
|
||||
mock_sp.return_value = (
|
||||
None,
|
||||
"crw-rw-rw- 1 root wheel 0x9000000 Feb 8 22:22 /dev/ttyUSBfake",
|
||||
)
|
||||
fake_device = SupportedDevice(
|
||||
name="a", for_firmware="heltec-v2.1", baseport_on_linux="ttyUSB"
|
||||
)
|
||||
fake_supported_devices = [fake_device]
|
||||
assert active_ports_on_supported_devices(fake_supported_devices) == {'/dev/ttyUSBfake'}
|
||||
assert active_ports_on_supported_devices(fake_supported_devices) == {
|
||||
"/dev/ttyUSBfake"
|
||||
}
|
||||
mock_platform.assert_called()
|
||||
mock_sp.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('subprocess.getstatusoutput')
|
||||
@patch('platform.system', return_value='Darwin')
|
||||
@patch("subprocess.getstatusoutput")
|
||||
@patch("platform.system", return_value="Darwin")
|
||||
def test_active_ports_on_supported_devices_mac(mock_platform, mock_sp):
|
||||
"""Test active_ports_on_supported_devices()"""
|
||||
mock_sp.return_value = (None, 'crw-rw-rw- 1 root wheel 0x9000000 Feb 8 22:22 /dev/cu.usbserial-foo')
|
||||
fake_device = SupportedDevice(name='a', for_firmware='heltec-v2.1', baseport_on_linux='cu.usbserial-')
|
||||
mock_sp.return_value = (
|
||||
None,
|
||||
"crw-rw-rw- 1 root wheel 0x9000000 Feb 8 22:22 /dev/cu.usbserial-foo",
|
||||
)
|
||||
fake_device = SupportedDevice(
|
||||
name="a", for_firmware="heltec-v2.1", baseport_on_linux="cu.usbserial-"
|
||||
)
|
||||
fake_supported_devices = [fake_device]
|
||||
assert active_ports_on_supported_devices(fake_supported_devices) == {'/dev/cu.usbserial-foo'}
|
||||
assert active_ports_on_supported_devices(fake_supported_devices) == {
|
||||
"/dev/cu.usbserial-foo"
|
||||
}
|
||||
mock_platform.assert_called()
|
||||
mock_sp.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('meshtastic.util.detect_windows_port', return_value={'COM2'})
|
||||
@patch('platform.system', return_value='Windows')
|
||||
@patch("meshtastic.util.detect_windows_port", return_value={"COM2"})
|
||||
@patch("platform.system", return_value="Windows")
|
||||
def test_active_ports_on_supported_devices_win(mock_platform, mock_dwp):
|
||||
"""Test active_ports_on_supported_devices()"""
|
||||
fake_device = SupportedDevice(name='a', for_firmware='heltec-v2.1')
|
||||
fake_device = SupportedDevice(name="a", for_firmware="heltec-v2.1")
|
||||
fake_supported_devices = [fake_device]
|
||||
assert active_ports_on_supported_devices(fake_supported_devices) == {'COM2'}
|
||||
assert active_ports_on_supported_devices(fake_supported_devices) == {"COM2"}
|
||||
mock_platform.assert_called()
|
||||
mock_dwp.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('subprocess.getstatusoutput')
|
||||
@patch('platform.system', return_value='Darwin')
|
||||
def test_active_ports_on_supported_devices_mac_no_duplicates_check(mock_platform, mock_sp):
|
||||
@patch("subprocess.getstatusoutput")
|
||||
@patch("platform.system", return_value="Darwin")
|
||||
def test_active_ports_on_supported_devices_mac_no_duplicates_check(
|
||||
mock_platform, mock_sp
|
||||
):
|
||||
"""Test active_ports_on_supported_devices()"""
|
||||
mock_sp.return_value = (None, ('crw-rw-rw- 1 root wheel 0x9000005 Mar 8 10:05 /dev/cu.usbmodem53230051441\n'
|
||||
'crw-rw-rw- 1 root wheel 0x9000003 Mar 8 10:06 /dev/cu.wchusbserial53230051441'))
|
||||
fake_device = SupportedDevice(name='a', for_firmware='tbeam', baseport_on_mac='cu.usbmodem')
|
||||
mock_sp.return_value = (
|
||||
None,
|
||||
(
|
||||
"crw-rw-rw- 1 root wheel 0x9000005 Mar 8 10:05 /dev/cu.usbmodem53230051441\n"
|
||||
"crw-rw-rw- 1 root wheel 0x9000003 Mar 8 10:06 /dev/cu.wchusbserial53230051441"
|
||||
),
|
||||
)
|
||||
fake_device = SupportedDevice(
|
||||
name="a", for_firmware="tbeam", baseport_on_mac="cu.usbmodem"
|
||||
)
|
||||
fake_supported_devices = [fake_device]
|
||||
assert active_ports_on_supported_devices(fake_supported_devices, False) == {'/dev/cu.usbmodem53230051441', '/dev/cu.wchusbserial53230051441'}
|
||||
assert active_ports_on_supported_devices(fake_supported_devices, False) == {
|
||||
"/dev/cu.usbmodem53230051441",
|
||||
"/dev/cu.wchusbserial53230051441",
|
||||
}
|
||||
mock_platform.assert_called()
|
||||
mock_sp.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('subprocess.getstatusoutput')
|
||||
@patch('platform.system', return_value='Darwin')
|
||||
@patch("subprocess.getstatusoutput")
|
||||
@patch("platform.system", return_value="Darwin")
|
||||
def test_active_ports_on_supported_devices_mac_duplicates_check(mock_platform, mock_sp):
|
||||
"""Test active_ports_on_supported_devices()"""
|
||||
mock_sp.return_value = (None, ('crw-rw-rw- 1 root wheel 0x9000005 Mar 8 10:05 /dev/cu.usbmodem53230051441\n'
|
||||
'crw-rw-rw- 1 root wheel 0x9000003 Mar 8 10:06 /dev/cu.wchusbserial53230051441'))
|
||||
fake_device = SupportedDevice(name='a', for_firmware='tbeam', baseport_on_mac='cu.usbmodem')
|
||||
mock_sp.return_value = (
|
||||
None,
|
||||
(
|
||||
"crw-rw-rw- 1 root wheel 0x9000005 Mar 8 10:05 /dev/cu.usbmodem53230051441\n"
|
||||
"crw-rw-rw- 1 root wheel 0x9000003 Mar 8 10:06 /dev/cu.wchusbserial53230051441"
|
||||
),
|
||||
)
|
||||
fake_device = SupportedDevice(
|
||||
name="a", for_firmware="tbeam", baseport_on_mac="cu.usbmodem"
|
||||
)
|
||||
fake_supported_devices = [fake_device]
|
||||
assert active_ports_on_supported_devices(fake_supported_devices, True) == {'/dev/cu.wchusbserial53230051441'}
|
||||
assert active_ports_on_supported_devices(fake_supported_devices, True) == {
|
||||
"/dev/cu.wchusbserial53230051441"
|
||||
}
|
||||
mock_platform.assert_called()
|
||||
mock_sp.assert_called()
|
||||
|
||||
@@ -16,20 +16,20 @@
|
||||
"""
|
||||
|
||||
import logging
|
||||
import threading
|
||||
import platform
|
||||
from pubsub import pub
|
||||
import threading
|
||||
|
||||
from pubsub import pub
|
||||
from pytap2 import TapDevice
|
||||
|
||||
from meshtastic import portnums_pb2
|
||||
from meshtastic.util import ipstr, readnet_u16
|
||||
from meshtastic.globals import Globals
|
||||
from meshtastic.util import ipstr, readnet_u16
|
||||
|
||||
|
||||
def onTunnelReceive(packet, interface): # pylint: disable=W0613
|
||||
def onTunnelReceive(packet, interface): # pylint: disable=W0613
|
||||
"""Callback for received tunneled messages from mesh."""
|
||||
logging.debug(f'in onTunnelReceive()')
|
||||
logging.debug(f"in onTunnelReceive()")
|
||||
our_globals = Globals.getInstance()
|
||||
tunnelInstance = our_globals.get_tunnelInstance()
|
||||
tunnelInstance.onReceive(packet)
|
||||
@@ -38,7 +38,7 @@ def onTunnelReceive(packet, interface): # pylint: disable=W0613
|
||||
class Tunnel:
|
||||
"""A TUN based IP tunnel over meshtastic"""
|
||||
|
||||
def __init__(self, iface, subnet='10.115', netmask="255.255.0.0"):
|
||||
def __init__(self, iface, subnet="10.115", netmask="255.255.0.0"):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
@@ -49,10 +49,16 @@ class Tunnel:
|
||||
if not iface:
|
||||
raise Exception("Tunnel() must have a interface")
|
||||
|
||||
if not subnet:
|
||||
raise Exception("Tunnel() must have a subnet")
|
||||
|
||||
if not netmask:
|
||||
raise Exception("Tunnel() must have a netmask")
|
||||
|
||||
self.iface = iface
|
||||
self.subnetPrefix = subnet
|
||||
|
||||
if platform.system() != 'Linux':
|
||||
if platform.system() != "Linux":
|
||||
raise Exception("Tunnel() can only be run instantiated on a Linux system")
|
||||
|
||||
our_globals = Globals.getInstance()
|
||||
@@ -63,6 +69,8 @@ class Tunnel:
|
||||
self.udpBlacklist = {
|
||||
1900, # SSDP
|
||||
5353, # multicast DNS
|
||||
9001, # Yggdrasil multicast discovery
|
||||
64512, # cjdns beacon
|
||||
}
|
||||
|
||||
"""A list of TCP services to block"""
|
||||
@@ -80,8 +88,10 @@ class Tunnel:
|
||||
self.LOG_TRACE = 5
|
||||
|
||||
# TODO: check if root?
|
||||
logging.info("Starting IP to mesh tunnel (you must be root for this *pre-alpha* "\
|
||||
"feature to work). Mesh members:")
|
||||
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)
|
||||
@@ -96,7 +106,9 @@ class Tunnel:
|
||||
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
|
||||
self.tun = None
|
||||
if self.iface.noProto:
|
||||
logging.warning(f"Not creating a TapDevice() because it is disabled by noProto")
|
||||
logging.warning(
|
||||
f"Not creating a TapDevice() because it is disabled by noProto"
|
||||
)
|
||||
else:
|
||||
self.tun = TapDevice(name="mesh")
|
||||
self.tun.up()
|
||||
@@ -104,10 +116,14 @@ class Tunnel:
|
||||
|
||||
self._rxThread = None
|
||||
if self.iface.noProto:
|
||||
logging.warning(f"Not starting TUN reader because it is disabled by noProto")
|
||||
logging.warning(
|
||||
f"Not starting TUN reader because it is disabled by noProto"
|
||||
)
|
||||
else:
|
||||
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
|
||||
self._rxThread = threading.Thread(target=self.__tunReader, args=(), daemon=True)
|
||||
self._rxThread = threading.Thread(
|
||||
target=self.__tunReader, args=(), daemon=True
|
||||
)
|
||||
self._rxThread.start()
|
||||
|
||||
def onReceive(self, packet):
|
||||
@@ -132,15 +148,19 @@ class Tunnel:
|
||||
ignore = False # Assume we will be forwarding the packet
|
||||
if protocol in self.protocolBlacklist:
|
||||
ignore = True
|
||||
logging.log(self.LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}")
|
||||
logging.log(
|
||||
self.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}")
|
||||
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:]
|
||||
# pingback = p[:12]+p[16:20]+p[12:16]+p[20:]
|
||||
# tap.write(pingback)
|
||||
elif protocol == 0x11: # UDP
|
||||
srcport = readnet_u16(p, subheader)
|
||||
@@ -159,8 +179,10 @@ class Tunnel:
|
||||
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)}")
|
||||
logging.warning(
|
||||
f"forwarding unexpected protocol 0x{protocol:02x}, "
|
||||
"src={ipstr(srcaddr)}, dest={ipstr(destAddr)}"
|
||||
)
|
||||
|
||||
return ignore
|
||||
|
||||
@@ -169,7 +191,7 @@ class Tunnel:
|
||||
logging.debug("TUN reader running")
|
||||
while True:
|
||||
p = tap.read()
|
||||
#logging.debug(f"IP packet received on TUN interface, type={type(p)}")
|
||||
# logging.debug(f"IP packet received on TUN interface, type={type(p)}")
|
||||
destAddr = p[16:20]
|
||||
|
||||
if not self._shouldFilterPacket(p):
|
||||
@@ -179,11 +201,11 @@ class Tunnel:
|
||||
# We only consider the last 16 bits of the nodenum for IP address matching
|
||||
ipBits = ipAddr[2] * 256 + ipAddr[3]
|
||||
|
||||
if ipBits == 0xffff:
|
||||
if ipBits == 0xFFFF:
|
||||
return "^all"
|
||||
|
||||
for node in self.iface.nodes.values():
|
||||
nodeNum = node["num"] & 0xffff
|
||||
nodeNum = node["num"] & 0xFFFF
|
||||
# logging.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}")
|
||||
if (nodeNum) == ipBits:
|
||||
return node["user"]["id"]
|
||||
@@ -196,11 +218,14 @@ class Tunnel:
|
||||
"""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)
|
||||
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)}")
|
||||
logging.warning(
|
||||
f"Dropping packet because no node found for destIP={ipstr(destAddr)}"
|
||||
)
|
||||
|
||||
def close(self):
|
||||
"""Close"""
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
"""Utility functions.
|
||||
"""
|
||||
import base64
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
from queue import Queue
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import base64
|
||||
import time
|
||||
import platform
|
||||
import logging
|
||||
import threading
|
||||
import subprocess
|
||||
|
||||
import pkg_resources
|
||||
import requests
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import pkg_resources
|
||||
|
||||
from meshtastic.supported_device import supported_devices
|
||||
|
||||
@@ -23,12 +25,13 @@ blacklistVids = dict.fromkeys([0x1366])
|
||||
|
||||
def quoteBooleans(a_string):
|
||||
"""Quote booleans
|
||||
given a string that contains ": true", replace with ": 'true'" (or false)
|
||||
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)
|
||||
@@ -61,10 +64,10 @@ def fromStr(valstr):
|
||||
"""
|
||||
if len(valstr) == 0: # Treat an emptystring as an empty bytes
|
||||
val = bytes()
|
||||
elif valstr.startswith('0x'):
|
||||
elif valstr.startswith("0x"):
|
||||
# if needed convert to string with asBytes.decode('utf-8')
|
||||
val = bytes.fromhex(valstr[2:])
|
||||
elif valstr.startswith('base64:'):
|
||||
elif valstr.startswith("base64:"):
|
||||
val = base64.b64decode(valstr[7:])
|
||||
elif valstr.lower() in {"t", "true", "yes"}:
|
||||
val = True
|
||||
@@ -100,7 +103,7 @@ def pskToString(psk: bytes):
|
||||
def stripnl(s):
|
||||
"""Remove newlines from a string (and remove extra whitespace)"""
|
||||
s = str(s).replace("\n", " ")
|
||||
return ' '.join(s.split())
|
||||
return " ".join(s.split())
|
||||
|
||||
|
||||
def fixme(message):
|
||||
@@ -123,9 +126,15 @@ def findPorts(eliminate_duplicates=False):
|
||||
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 = 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()
|
||||
if eliminate_duplicates:
|
||||
l = eliminate_duplicate_port(l)
|
||||
@@ -134,6 +143,7 @@ def findPorts(eliminate_duplicates=False):
|
||||
|
||||
class dotdict(dict):
|
||||
"""dot.notation access to dictionary attributes"""
|
||||
|
||||
__getattr__ = dict.get
|
||||
__setattr__ = dict.__setitem__
|
||||
__delattr__ = dict.__delitem__
|
||||
@@ -141,6 +151,7 @@ class dotdict(dict):
|
||||
|
||||
class Timeout:
|
||||
"""Timeout class"""
|
||||
|
||||
def __init__(self, maxSecs=20):
|
||||
self.expireTime = 0
|
||||
self.sleepInterval = 0.1
|
||||
@@ -159,8 +170,60 @@ class Timeout:
|
||||
time.sleep(self.sleepInterval)
|
||||
return False
|
||||
|
||||
def waitForAckNak(
|
||||
self, acknowledgment, attrs=("receivedAck", "receivedNak", "receivedImplAck")
|
||||
):
|
||||
"""Block until an ACK or NAK has been received. Returns True if ACK or NAK has been received."""
|
||||
self.reset()
|
||||
while time.time() < self.expireTime:
|
||||
if any(map(lambda a: getattr(acknowledgment, a, None), attrs)):
|
||||
acknowledgment.reset()
|
||||
return True
|
||||
time.sleep(self.sleepInterval)
|
||||
return False
|
||||
|
||||
class DeferredExecution():
|
||||
def waitForTraceRoute(self, waitFactor, acknowledgment, attr="receivedTraceRoute"):
|
||||
"""Block until traceroute response is received. Returns True if traceroute response has been received."""
|
||||
self.expireTimeout *= waitFactor
|
||||
self.reset()
|
||||
while time.time() < self.expireTime:
|
||||
if getattr(acknowledgment, attr, None):
|
||||
acknowledgment.reset()
|
||||
return True
|
||||
time.sleep(self.sleepInterval)
|
||||
return False
|
||||
|
||||
def waitForTelemetry(self, acknowledgment):
|
||||
"""Block until telemetry response is received. Returns True if telemetry response has been received."""
|
||||
self.reset()
|
||||
while time.time() < self.expireTime:
|
||||
if getattr(acknowledgment, "receivedTelemetry", None):
|
||||
acknowledgment.reset()
|
||||
return True
|
||||
time.sleep(self.sleepInterval)
|
||||
return False
|
||||
|
||||
class Acknowledgment:
|
||||
"A class that records which type of acknowledgment was just received, if any."
|
||||
|
||||
def __init__(self):
|
||||
"""initialize"""
|
||||
self.receivedAck = False
|
||||
self.receivedNak = False
|
||||
self.receivedImplAck = False
|
||||
self.receivedTraceRoute = False
|
||||
self.receivedTelemetry = False
|
||||
|
||||
def reset(self):
|
||||
"""reset"""
|
||||
self.receivedAck = False
|
||||
self.receivedNak = False
|
||||
self.receivedImplAck = False
|
||||
self.receivedTraceRoute = False
|
||||
self.receivedTelemetry = False
|
||||
|
||||
|
||||
class DeferredExecution:
|
||||
"""A thread that accepts closures to run, and runs them as they are received"""
|
||||
|
||||
def __init__(self, name=None):
|
||||
@@ -170,7 +233,7 @@ class DeferredExecution():
|
||||
self.thread.start()
|
||||
|
||||
def queueWork(self, runnable):
|
||||
""" Queue up the work"""
|
||||
"""Queue up the work"""
|
||||
self.queue.put(runnable)
|
||||
|
||||
def _run(self):
|
||||
@@ -179,13 +242,15 @@ class DeferredExecution():
|
||||
o = self.queue.get()
|
||||
o()
|
||||
except:
|
||||
logging.error(f"Unexpected error in deferred execution {sys.exc_info()[0]}")
|
||||
logging.error(
|
||||
f"Unexpected error in deferred execution {sys.exc_info()[0]}"
|
||||
)
|
||||
print(traceback.format_exc())
|
||||
|
||||
|
||||
def our_exit(message, return_value = 1):
|
||||
def our_exit(message, return_value=1):
|
||||
"""Print the message and return a value.
|
||||
return_value defaults to 1 (non-successful)
|
||||
return_value defaults to 1 (non-successful)
|
||||
"""
|
||||
print(message)
|
||||
sys.exit(return_value)
|
||||
@@ -193,28 +258,36 @@ def our_exit(message, return_value = 1):
|
||||
|
||||
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(f' System: {platform.system()}')
|
||||
print(f' Platform: {platform.platform()}')
|
||||
print(f' Release: {platform.uname().release}')
|
||||
print(f' Machine: {platform.uname().machine}')
|
||||
print(f' Encoding (stdin): {sys.stdin.encoding}')
|
||||
print(f' Encoding (stdout): {sys.stdout.encoding}')
|
||||
print("")
|
||||
print("If having issues with meshtastic cli or python library")
|
||||
print("or wish to make feature requests, visit:")
|
||||
print("https://github.com/meshtastic/python/issues")
|
||||
print("When adding an issue, be sure to include the following info:")
|
||||
print(f" System: {platform.system()}")
|
||||
print(f" Platform: {platform.platform()}")
|
||||
print(f" Release: {platform.uname().release}")
|
||||
print(f" Machine: {platform.uname().machine}")
|
||||
print(f" Encoding (stdin): {sys.stdin.encoding}")
|
||||
print(f" Encoding (stdout): {sys.stdout.encoding}")
|
||||
the_version = pkg_resources.get_distribution("meshtastic").version
|
||||
print(f' meshtastic: v{the_version}')
|
||||
print(f' Executable: {sys.argv[0]}')
|
||||
print(f' Python: {platform.python_version()} {platform.python_implementation()} {platform.python_compiler()}')
|
||||
print('')
|
||||
print('Please add the output from the command: meshtastic --info')
|
||||
pypi_version = check_if_newer_version()
|
||||
if pypi_version:
|
||||
print(
|
||||
f" meshtastic: v{the_version} (*** newer version v{pypi_version} available ***)"
|
||||
)
|
||||
else:
|
||||
print(f" meshtastic: v{the_version}")
|
||||
print(f" Executable: {sys.argv[0]}")
|
||||
print(
|
||||
f" Python: {platform.python_version()} {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.
|
||||
Will removed nested keys.
|
||||
Will removed nested keys.
|
||||
"""
|
||||
for key in keys:
|
||||
try:
|
||||
@@ -229,12 +302,12 @@ def remove_keys_from_dict(keys, adict):
|
||||
|
||||
def hexstr(barray):
|
||||
"""Print a string of hex digits"""
|
||||
return ":".join(f'{x:02x}' for x in barray)
|
||||
return ":".join(f"{x:02x}" for x in barray)
|
||||
|
||||
|
||||
def ipstr(barray):
|
||||
"""Print a string of ip digits"""
|
||||
return ".".join(f'{x}' for x in barray)
|
||||
return ".".join(f"{x}" for x in barray)
|
||||
|
||||
|
||||
def readnet_u16(p, offset):
|
||||
@@ -244,8 +317,8 @@ def readnet_u16(p, offset):
|
||||
|
||||
def convert_mac_addr(val):
|
||||
"""Convert the base 64 encoded value to a mac address
|
||||
val - base64 encoded value (ex: '/c0gFyhb'))
|
||||
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
|
||||
val - base64 encoded value (ex: '/c0gFyhb'))
|
||||
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
|
||||
"""
|
||||
if not re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", val):
|
||||
val_as_bytes = base64.b64decode(val)
|
||||
@@ -256,21 +329,23 @@ def convert_mac_addr(val):
|
||||
def snake_to_camel(a_string):
|
||||
"""convert snake_case to camelCase"""
|
||||
# split underscore using split
|
||||
temp = a_string.split('_')
|
||||
temp = a_string.split("_")
|
||||
# joining result
|
||||
result = temp[0] + ''.join(ele.title() for ele in temp[1:])
|
||||
result = temp[0] + "".join(ele.title() for ele in temp[1:])
|
||||
return result
|
||||
|
||||
|
||||
def camel_to_snake(a_string):
|
||||
"""convert camelCase to snake_case"""
|
||||
return ''.join(['_'+i.lower() if i.isupper() else i for i in a_string]).lstrip('_')
|
||||
return "".join(["_" + i.lower() if i.isupper() else i for i in a_string]).lstrip(
|
||||
"_"
|
||||
)
|
||||
|
||||
|
||||
def detect_supported_devices():
|
||||
"""detect supported devices based on vendor id"""
|
||||
system = platform.system()
|
||||
#print(f'system:{system}')
|
||||
# print(f'system:{system}')
|
||||
|
||||
possible_devices = set()
|
||||
if system == "Linux":
|
||||
@@ -278,31 +353,33 @@ def detect_supported_devices():
|
||||
|
||||
# linux: use lsusb
|
||||
# Bus 001 Device 091: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
|
||||
_, lsusb_output = subprocess.getstatusoutput('lsusb')
|
||||
_, lsusb_output = subprocess.getstatusoutput("lsusb")
|
||||
vids = get_unique_vendor_ids()
|
||||
for vid in vids:
|
||||
#print(f'looking for {vid}...')
|
||||
search = f' {vid}:'
|
||||
#print(f'search:"{search}"')
|
||||
# print(f'looking for {vid}...')
|
||||
search = f" {vid}:"
|
||||
# print(f'search:"{search}"')
|
||||
if re.search(search, lsusb_output, re.MULTILINE):
|
||||
#print(f'Found vendor id that matches')
|
||||
# print(f'Found vendor id that matches')
|
||||
devices = get_devices_with_vendor_id(vid)
|
||||
for device in devices:
|
||||
possible_devices.add(device)
|
||||
|
||||
elif system == "Windows":
|
||||
# if windows, run Get-PnpDevice
|
||||
_, sp_output = subprocess.getstatusoutput('powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
|
||||
'Get-PnpDevice -PresentOnly | Format-List"')
|
||||
#print(f'sp_output:{sp_output}')
|
||||
_, sp_output = subprocess.getstatusoutput(
|
||||
'powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
|
||||
'Get-PnpDevice -PresentOnly | Format-List"'
|
||||
)
|
||||
# print(f'sp_output:{sp_output}')
|
||||
vids = get_unique_vendor_ids()
|
||||
for vid in vids:
|
||||
#print(f'looking for {vid.upper()}...')
|
||||
search = f'DeviceID.*{vid.upper()}&'
|
||||
#search = f'{vid.upper()}'
|
||||
#print(f'search:"{search}"')
|
||||
# print(f'looking for {vid.upper()}...')
|
||||
search = f"DeviceID.*{vid.upper()}&"
|
||||
# search = f'{vid.upper()}'
|
||||
# print(f'search:"{search}"')
|
||||
if re.search(search, sp_output, re.MULTILINE):
|
||||
#print(f'Found vendor id that matches')
|
||||
# print(f'Found vendor id that matches')
|
||||
devices = get_devices_with_vendor_id(vid)
|
||||
for device in devices:
|
||||
possible_devices.add(device)
|
||||
@@ -311,14 +388,14 @@ def detect_supported_devices():
|
||||
# run: system_profiler SPUSBDataType
|
||||
# Note: If in boot mode, the 19003 reports same product ID as 5005.
|
||||
|
||||
_, sp_output = subprocess.getstatusoutput('system_profiler SPUSBDataType')
|
||||
_, sp_output = subprocess.getstatusoutput("system_profiler SPUSBDataType")
|
||||
vids = get_unique_vendor_ids()
|
||||
for vid in vids:
|
||||
#print(f'looking for {vid}...')
|
||||
search = f'Vendor ID: 0x{vid}'
|
||||
#print(f'search:"{search}"')
|
||||
# print(f'looking for {vid}...')
|
||||
search = f"Vendor ID: 0x{vid}"
|
||||
# print(f'search:"{search}"')
|
||||
if re.search(search, sp_output, re.MULTILINE):
|
||||
#print(f'Found vendor id that matches')
|
||||
# print(f'Found vendor id that matches')
|
||||
devices = get_devices_with_vendor_id(vid)
|
||||
for device in devices:
|
||||
possible_devices.add(device)
|
||||
@@ -331,7 +408,7 @@ def detect_windows_needs_driver(sd, print_reason=False):
|
||||
|
||||
if sd:
|
||||
system = platform.system()
|
||||
#print(f'in detect_windows_needs_driver system:{system}')
|
||||
# print(f'in detect_windows_needs_driver system:{system}')
|
||||
|
||||
if system == "Windows":
|
||||
# if windows, see if we can find a DeviceId with the vendor id
|
||||
@@ -340,11 +417,11 @@ def detect_windows_needs_driver(sd, print_reason=False):
|
||||
command += f"'*{sd.usb_vendor_id_in_hex.upper()}*'"
|
||||
command += ')} | Format-List"'
|
||||
|
||||
#print(f'command:{command}')
|
||||
# print(f'command:{command}')
|
||||
_, sp_output = subprocess.getstatusoutput(command)
|
||||
#print(f'sp_output:{sp_output}')
|
||||
search = f'CM_PROB_FAILED_INSTALL'
|
||||
#print(f'search:"{search}"')
|
||||
# print(f'sp_output:{sp_output}')
|
||||
search = f"CM_PROB_FAILED_INSTALL"
|
||||
# print(f'search:"{search}"')
|
||||
if re.search(search, sp_output, re.MULTILINE):
|
||||
need_to_install_driver = True
|
||||
# if the want to see the reason
|
||||
@@ -356,30 +433,30 @@ def detect_windows_needs_driver(sd, print_reason=False):
|
||||
def eliminate_duplicate_port(ports):
|
||||
"""Sometimes we detect 2 serial ports, but we really only need to use one of the ports.
|
||||
|
||||
ports is a list of ports
|
||||
return a list with a single port to use, if it meets the duplicate port conditions
|
||||
ports is a list of ports
|
||||
return a list with a single port to use, if it meets the duplicate port conditions
|
||||
|
||||
examples:
|
||||
Ports: ['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430'] => ['/dev/cu.wchusbserial1430']
|
||||
Ports: ['/dev/cu.usbmodem11301', '/dev/cu.wchusbserial11301'] => ['/dev/cu.wchusbserial11301']
|
||||
Ports: ['/dev/cu.SLAB_USBtoUART', '/dev/cu.usbserial-0001'] => ['/dev/cu.usbserial-0001']
|
||||
examples:
|
||||
Ports: ['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430'] => ['/dev/cu.wchusbserial1430']
|
||||
Ports: ['/dev/cu.usbmodem11301', '/dev/cu.wchusbserial11301'] => ['/dev/cu.wchusbserial11301']
|
||||
Ports: ['/dev/cu.SLAB_USBtoUART', '/dev/cu.usbserial-0001'] => ['/dev/cu.usbserial-0001']
|
||||
"""
|
||||
new_ports = []
|
||||
if len(ports) != 2:
|
||||
new_ports = ports
|
||||
else:
|
||||
ports.sort()
|
||||
if 'usbserial' in ports[0] and 'wchusbserial' in ports[1]:
|
||||
if "usbserial" in ports[0] and "wchusbserial" in ports[1]:
|
||||
first = ports[0].replace("usbserial-", "")
|
||||
second = ports[1].replace("wchusbserial", "")
|
||||
if first == second:
|
||||
new_ports.append(ports[1])
|
||||
elif 'usbmodem' in ports[0] and 'wchusbserial' in ports[1]:
|
||||
elif "usbmodem" in ports[0] and "wchusbserial" in ports[1]:
|
||||
first = ports[0].replace("usbmodem", "")
|
||||
second = ports[1].replace("wchusbserial", "")
|
||||
if first == second:
|
||||
new_ports.append(ports[1])
|
||||
elif 'SLAB_USBtoUART' in ports[0] and 'usbserial' in ports[1]:
|
||||
elif "SLAB_USBtoUART" in ports[0] and "usbserial" in ports[1]:
|
||||
new_ports.append(ports[1])
|
||||
else:
|
||||
new_ports = ports
|
||||
@@ -391,14 +468,14 @@ def is_windows11():
|
||||
is_win11 = False
|
||||
if platform.system() == "Windows":
|
||||
if float(platform.release()) >= 10.0:
|
||||
patch = platform.version().split('.')[2]
|
||||
patch = platform.version().split(".")[2]
|
||||
# in case they add some number suffix later, just get first 5 chars of patch
|
||||
patch = patch[:5]
|
||||
try:
|
||||
if int(patch) >= 22000:
|
||||
is_win11 = True
|
||||
except Exception as e:
|
||||
print(f'problem detecting win11 e:{e}')
|
||||
print(f"problem detecting win11 e:{e}")
|
||||
return is_win11
|
||||
|
||||
|
||||
@@ -438,46 +515,46 @@ def active_ports_on_supported_devices(sds, eliminate_duplicates=False):
|
||||
for bp in baseports:
|
||||
if system == "Linux":
|
||||
# see if we have any devices (ignoring any stderr output)
|
||||
command = f'ls -al /dev/{bp}* 2> /dev/null'
|
||||
#print(f'command:{command}')
|
||||
command = f"ls -al /dev/{bp}* 2> /dev/null"
|
||||
# print(f'command:{command}')
|
||||
_, ls_output = subprocess.getstatusoutput(command)
|
||||
#print(f'ls_output:{ls_output}')
|
||||
# print(f'ls_output:{ls_output}')
|
||||
# if we got output, there are ports
|
||||
if len(ls_output) > 0:
|
||||
#print('got output')
|
||||
# print('got output')
|
||||
# for each line of output
|
||||
lines = ls_output.split('\n')
|
||||
#print(f'lines:{lines}')
|
||||
lines = ls_output.split("\n")
|
||||
# print(f'lines:{lines}')
|
||||
for line in lines:
|
||||
parts = line.split(' ')
|
||||
#print(f'parts:{parts}')
|
||||
parts = line.split(" ")
|
||||
# print(f'parts:{parts}')
|
||||
port = parts[-1]
|
||||
#print(f'port:{port}')
|
||||
# print(f'port:{port}')
|
||||
ports.add(port)
|
||||
elif system == "Darwin":
|
||||
# see if we have any devices (ignoring any stderr output)
|
||||
command = f'ls -al /dev/{bp}* 2> /dev/null'
|
||||
#print(f'command:{command}')
|
||||
command = f"ls -al /dev/{bp}* 2> /dev/null"
|
||||
# print(f'command:{command}')
|
||||
_, ls_output = subprocess.getstatusoutput(command)
|
||||
#print(f'ls_output:{ls_output}')
|
||||
# print(f'ls_output:{ls_output}')
|
||||
# if we got output, there are ports
|
||||
if len(ls_output) > 0:
|
||||
#print('got output')
|
||||
# print('got output')
|
||||
# for each line of output
|
||||
lines = ls_output.split('\n')
|
||||
#print(f'lines:{lines}')
|
||||
lines = ls_output.split("\n")
|
||||
# print(f'lines:{lines}')
|
||||
for line in lines:
|
||||
parts = line.split(' ')
|
||||
#print(f'parts:{parts}')
|
||||
parts = line.split(" ")
|
||||
# print(f'parts:{parts}')
|
||||
port = parts[-1]
|
||||
#print(f'port:{port}')
|
||||
# print(f'port:{port}')
|
||||
ports.add(port)
|
||||
elif system == "Windows":
|
||||
# for each device in supported devices found
|
||||
for d in sds:
|
||||
# find the port(s)
|
||||
com_ports = detect_windows_port(d)
|
||||
#print(f'com_ports:{com_ports}')
|
||||
# print(f'com_ports:{com_ports}')
|
||||
# add all ports
|
||||
for com_port in com_ports:
|
||||
ports.add(com_port)
|
||||
@@ -496,16 +573,35 @@ def detect_windows_port(sd):
|
||||
system = platform.system()
|
||||
|
||||
if system == "Windows":
|
||||
command = ('powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
|
||||
'Get-PnpDevice -PresentOnly | Where-Object{ ($_.DeviceId -like ')
|
||||
command = (
|
||||
'powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
|
||||
"Get-PnpDevice -PresentOnly | Where-Object{ ($_.DeviceId -like "
|
||||
)
|
||||
command += f"'*{sd.usb_vendor_id_in_hex.upper()}*'"
|
||||
command += ')} | Format-List"'
|
||||
|
||||
#print(f'command:{command}')
|
||||
# print(f'command:{command}')
|
||||
_, sp_output = subprocess.getstatusoutput(command)
|
||||
#print(f'sp_output:{sp_output}')
|
||||
p = re.compile(r'\(COM(.*)\)')
|
||||
# print(f'sp_output:{sp_output}')
|
||||
p = re.compile(r"\(COM(.*)\)")
|
||||
for x in p.findall(sp_output):
|
||||
#print(f'x:{x}')
|
||||
ports.add(f'COM{x}')
|
||||
# print(f'x:{x}')
|
||||
ports.add(f"COM{x}")
|
||||
return ports
|
||||
|
||||
|
||||
def check_if_newer_version():
|
||||
"""Check pip to see if we are running the latest version."""
|
||||
pypi_version = None
|
||||
try:
|
||||
url = "https://pypi.org/pypi/meshtastic/json"
|
||||
data = requests.get(url, timeout=5).json()
|
||||
pypi_version = data["info"]["version"]
|
||||
except Exception:
|
||||
pass
|
||||
act_version = pkg_resources.get_distribution("meshtastic").version
|
||||
if pypi_version and pkg_resources.parse_version(
|
||||
pypi_version
|
||||
) <= pkg_resources.parse_version(act_version):
|
||||
return None
|
||||
return pypi_version
|
||||
|
||||
28
meshtastic/xmodem_pb2.py
Normal file
28
meshtastic/xmodem_pb2.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: meshtastic/xmodem.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17meshtastic/xmodem.proto\"\xab\x01\n\x06XModem\x12 \n\x07\x63ontrol\x18\x01 \x01(\x0e\x32\x0f.XModem.Control\x12\x0b\n\x03seq\x18\x02 \x01(\r\x12\r\n\x05\x63rc16\x18\x03 \x01(\r\x12\x0e\n\x06\x62uffer\x18\x04 \x01(\x0c\"S\n\x07\x43ontrol\x12\x07\n\x03NUL\x10\x00\x12\x07\n\x03SOH\x10\x01\x12\x07\n\x03STX\x10\x02\x12\x07\n\x03\x45OT\x10\x04\x12\x07\n\x03\x41\x43K\x10\x06\x12\x07\n\x03NAK\x10\x15\x12\x07\n\x03\x43\x41N\x10\x18\x12\t\n\x05\x43TRLZ\x10\x1a\x42\x61\n\x13\x63om.geeksville.meshB\x0cXmodemProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.xmodem_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||
_XMODEM._serialized_start=28
|
||||
_XMODEM._serialized_end=199
|
||||
_XMODEM_CONTROL._serialized_start=116
|
||||
_XMODEM_CONTROL._serialized_end=199
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
1
proto
1
proto
Submodule proto deleted from 0904d7f8ce
1
protobufs
Submodule
1
protobufs
Submodule
Submodule protobufs added at 4a1d3766e8
@@ -7,6 +7,7 @@ pyqrcode
|
||||
tabulate
|
||||
timeago
|
||||
webencodings
|
||||
requests
|
||||
pyparsing
|
||||
twine
|
||||
autopep8
|
||||
|
||||
39
setup.py
39
setup.py
@@ -1,6 +1,7 @@
|
||||
# Note: you shouldn't need to run this script manually. It is run implicitly by the pip3 install command.
|
||||
|
||||
import pathlib
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
# The directory containing this file
|
||||
@@ -12,36 +13,44 @@ with open("README.md", "r") as fh:
|
||||
# This call to setup() does all the work
|
||||
setup(
|
||||
name="meshtastic",
|
||||
version="1.3.39",
|
||||
version="2.2.17",
|
||||
description="Python API & client shell for talking to Meshtastic devices",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/meshtastic/Meshtastic-python",
|
||||
author="Kevin Hester",
|
||||
author_email="kevinh@geeksville.com",
|
||||
license="MIT",
|
||||
url="https://github.com/meshtastic/python",
|
||||
author="Meshtastic Developers",
|
||||
author_email="contact@meshtastic.org",
|
||||
license="GPL-3.0-only",
|
||||
classifiers=[
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Development Status :: 4 - Beta",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
],
|
||||
packages=["meshtastic"],
|
||||
include_package_data=True,
|
||||
install_requires=["pyserial>=3.4", "protobuf>=3.13.0",
|
||||
"pypubsub>=4.0.3", "dotmap>=1.3.14", "pexpect>=4.6.0", "pyqrcode>=1.2.1",
|
||||
"tabulate>=0.8.9", "timeago>=1.0.15", "pyyaml",
|
||||
"pygatt>=4.0.5 ; platform_system=='Linux'"],
|
||||
extras_require={
|
||||
'tunnel': ["pytap2>=2.0.0"]
|
||||
},
|
||||
python_requires='>=3.7',
|
||||
install_requires=[
|
||||
"pyserial>=3.4",
|
||||
"protobuf>=3.13.0",
|
||||
"requests>=2.25.0",
|
||||
"pypubsub>=4.0.3",
|
||||
"dotmap>=1.3.14",
|
||||
"pexpect>=4.6.0",
|
||||
"pyqrcode>=1.2.1",
|
||||
"tabulate>=0.8.9",
|
||||
"timeago>=1.0.15",
|
||||
"pyyaml",
|
||||
"pygatt>=4.0.5 ; platform_system=='Linux'",
|
||||
],
|
||||
extras_require={"tunnel": ["pytap2>=2.0.0"]},
|
||||
python_requires=">=3.7",
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"meshtastic=meshtastic.__main__:main",
|
||||
"mesh-tunnel=meshtastic.__main__:tunnelMain [tunnel]"
|
||||
"mesh-tunnel=meshtastic.__main__:tunnelMain [tunnel]",
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
readme.txt for single standalone executable zip files that can be
|
||||
downloaded from https://github.com/meshtastic/Meshtastic-python/releases
|
||||
downloaded from https://github.com/meshtastic/python/releases
|
||||
|
||||
If you do not want to install python and/or the python libraries, you can download one of these
|
||||
files to run the Meshtastic command line interface (CLI) as a standalone executable.
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
import meshtastic
|
||||
import datetime, logging
|
||||
|
||||
from pubsub import pub
|
||||
|
||||
#logging.basicConfig(level=logging.DEBUG)
|
||||
import meshtastic
|
||||
|
||||
# logging.basicConfig(level=logging.DEBUG)
|
||||
print(str(datetime.datetime.now()) + ": start")
|
||||
interface = meshtastic.TCPInterface(sys.argv[1])
|
||||
print(str(datetime.datetime.now()) + ": middle")
|
||||
interface.close()
|
||||
print(str(datetime.datetime.now()) + ": after close")
|
||||
print(str(datetime.datetime.now()) + ": after close")
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import meshtastic
|
||||
import time
|
||||
|
||||
interface = meshtastic.SerialInterface() # By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0
|
||||
import meshtastic
|
||||
|
||||
interface = (
|
||||
meshtastic.SerialInterface()
|
||||
) # By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0
|
||||
interface.sendText("hello mesh")
|
||||
interface.close()
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
|
||||
# reported by @ScriptBlock
|
||||
|
||||
import meshtastic, sys
|
||||
import sys
|
||||
|
||||
from pubsub import pub
|
||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect to the radio
|
||||
|
||||
import meshtastic
|
||||
|
||||
|
||||
def onConnection(
|
||||
interface, topic=pub.AUTO_TOPIC
|
||||
): # called when we (re)connect to the radio
|
||||
print(interface.myInfo)
|
||||
interface.close()
|
||||
|
||||
|
||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||
interface = meshtastic.TCPInterface(sys.argv[1])
|
||||
|
||||
@@ -8,15 +8,16 @@
|
||||
# select local ip address based on nodeid
|
||||
# print known node ids as IP addresses
|
||||
|
||||
from pytap2 import TapDevice
|
||||
import logging
|
||||
from _thread import start_new_thread
|
||||
|
||||
from pytap2 import TapDevice
|
||||
|
||||
"""A list of chatty UDP services we should never accidentally
|
||||
forward to our slow network"""
|
||||
udpBlacklist = {
|
||||
1900, # SSDP
|
||||
5353, # multicast DNS
|
||||
1900, # SSDP
|
||||
5353, # multicast DNS
|
||||
}
|
||||
|
||||
"""A list of TCP services to block"""
|
||||
@@ -24,22 +25,26 @@ tcpBlacklist = {}
|
||||
|
||||
"""A list of protocols we ignore"""
|
||||
protocolBlacklist = {
|
||||
0x02, # IGMP
|
||||
0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment
|
||||
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)
|
||||
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)
|
||||
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 readtest(tap):
|
||||
while True:
|
||||
p = tap.read()
|
||||
@@ -48,23 +53,23 @@ def readtest(tap):
|
||||
srcaddr = p[12:16]
|
||||
destaddr = p[16:20]
|
||||
subheader = 20
|
||||
ignore = False # Assume we will be forwarding the packet
|
||||
ignore = False # Assume we will be forwarding the packet
|
||||
if protocol in protocolBlacklist:
|
||||
ignore = True
|
||||
logging.debug(f"Ignoring blacklisted protocol 0x{protocol:02x}")
|
||||
elif protocol == 0x01: # ICMP
|
||||
elif protocol == 0x01: # ICMP
|
||||
logging.warn("Generating fake ping reply")
|
||||
# reply to pings (swap src and dest but keep rest of packet unchanged)
|
||||
pingback = p[:12]+p[16:20]+p[12:16]+p[20:]
|
||||
pingback = p[:12] + p[16:20] + p[12:16] + p[20:]
|
||||
tap.write(pingback)
|
||||
elif protocol == 0x11: # UDP
|
||||
elif protocol == 0x11: # UDP
|
||||
srcport = readnet_u16(p, subheader)
|
||||
destport = readnet_u16(p, subheader + 2)
|
||||
logging.debug(f"udp srcport={srcport}, destport={destport}")
|
||||
if destport in udpBlacklist:
|
||||
ignore = True
|
||||
logging.debug(f"ignoring blacklisted UDP port {destport}")
|
||||
elif protocol == 0x06: # TCP
|
||||
elif protocol == 0x06: # TCP
|
||||
srcport = readnet_u16(p, subheader)
|
||||
destport = readnet_u16(p, subheader + 2)
|
||||
logging.debug(f"tcp srcport={srcport}, destport={destport}")
|
||||
@@ -72,22 +77,23 @@ def readtest(tap):
|
||||
ignore = True
|
||||
logging.debug(f"ignoring blacklisted TCP port {destport}")
|
||||
else:
|
||||
logging.warning(f"unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destaddr)}")
|
||||
logging.warning(
|
||||
f"unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destaddr)}"
|
||||
)
|
||||
|
||||
if not ignore:
|
||||
logging.debug(f"Forwarding packet bytelen={len(p)} src={ipstr(srcaddr)}, dest={ipstr(destaddr)}")
|
||||
logging.debug(
|
||||
f"Forwarding packet bytelen={len(p)} src={ipstr(srcaddr)}, dest={ipstr(destaddr)}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
tun = TapDevice(mtu=200)
|
||||
# tun.create()
|
||||
tun.up()
|
||||
tun.ifconfig(address="10.115.1.2",netmask="255.255.0.0")
|
||||
tun.ifconfig(address="10.115.1.2", netmask="255.255.0.0")
|
||||
|
||||
start_new_thread(readtest,(tun,))
|
||||
start_new_thread(readtest, (tun,))
|
||||
input("press return key to quit!")
|
||||
tun.close()
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"github": {
|
||||
"silent": true
|
||||
}
|
||||
"github": {
|
||||
"silent": true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user