mirror of
https://github.com/meshtastic/python.git
synced 2025-12-28 10:27:54 -05:00
Compare commits
449 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a908bdfc1c | ||
|
|
81b64ac908 | ||
|
|
d5ccdc826f | ||
|
|
fac4faaae8 | ||
|
|
cfb8769746 | ||
|
|
c1b0e4e8d0 | ||
|
|
5683e31f6b | ||
|
|
7909ad477b | ||
|
|
f94dbf05ef | ||
|
|
a44b769390 | ||
|
|
2a4816a9cd | ||
|
|
674fd92690 | ||
|
|
cc29cab99a | ||
|
|
bf803bb6e9 | ||
|
|
3c80fd0f02 | ||
|
|
616a3ab706 | ||
|
|
8350cc611d | ||
|
|
621feb749d | ||
|
|
f6731a435d | ||
|
|
dcfe5fb558 | ||
|
|
4fa80e9652 | ||
|
|
02851b6237 | ||
|
|
a74ec12445 | ||
|
|
262e921a81 | ||
|
|
0c7b9e10f4 | ||
|
|
ab8b930365 | ||
|
|
4ae49c68aa | ||
|
|
733f22d927 | ||
|
|
791131ea27 | ||
|
|
f17292221c | ||
|
|
b60a438c9d | ||
|
|
3b3a610375 | ||
|
|
38f928bdb7 | ||
|
|
6272e992a4 | ||
|
|
3263fbca28 | ||
|
|
f0e7af389c | ||
|
|
471dfc7a29 | ||
|
|
2a7c21c062 | ||
|
|
03797e3336 | ||
|
|
dc1be12c86 | ||
|
|
4c83a43d64 | ||
|
|
e083cda3d9 | ||
|
|
c0006f888b | ||
|
|
e2d2d3a347 | ||
|
|
3fd50b0e44 | ||
|
|
4b0e3ae923 | ||
|
|
37f10cc0d4 | ||
|
|
998df265e6 | ||
|
|
d852981371 | ||
|
|
afed5bd943 | ||
|
|
97b9041b76 | ||
|
|
2dc14ef466 | ||
|
|
8e69c32a36 | ||
|
|
42b33bea5b | ||
|
|
7fd101cbf8 | ||
|
|
92ee9889b1 | ||
|
|
b6e1610abe | ||
|
|
bde5db9c51 | ||
|
|
148ae49ded | ||
|
|
d1f8365da1 | ||
|
|
59fc294d66 | ||
|
|
7473b4e18c | ||
|
|
58aafcf3f1 | ||
|
|
776fc57c35 | ||
|
|
b3f752a3c4 | ||
|
|
4965ec7f1d | ||
|
|
0746acd34f | ||
|
|
49b1c4816e | ||
|
|
7c6e87e161 | ||
|
|
b548700c0b | ||
|
|
f278a30003 | ||
|
|
22bbe67d24 | ||
|
|
a2861a133e | ||
|
|
03aab10786 | ||
|
|
95e768efd5 | ||
|
|
6644e86be9 | ||
|
|
c8363cd476 | ||
|
|
62efe1ab7f | ||
|
|
01e643ad2f | ||
|
|
e4078e84d7 | ||
|
|
abfcbe2a90 | ||
|
|
e06d8bbc06 | ||
|
|
a78cdde86f | ||
|
|
10517ac94d | ||
|
|
a572699588 | ||
|
|
d11fb47734 | ||
|
|
92c7b2db69 | ||
|
|
ff94ad968c | ||
|
|
c6071c57ec | ||
|
|
e3e3562c2c | ||
|
|
06b5b8fa83 | ||
|
|
4b95b0ff30 | ||
|
|
42f2ed571d | ||
|
|
f3791c5c6d | ||
|
|
4cff344971 | ||
|
|
594b307e94 | ||
|
|
8a5fd16469 | ||
|
|
4fa93989fa | ||
|
|
032072d2f3 | ||
|
|
ce7b1d9916 | ||
|
|
d015da3ca1 | ||
|
|
a956c8068c | ||
|
|
2124e292f1 | ||
|
|
115739a9bb | ||
|
|
cc2c16b957 | ||
|
|
b9245c6c1f | ||
|
|
d21f7811fa | ||
|
|
8dbd6431f7 | ||
|
|
c7b2bbf700 | ||
|
|
682fdb7ef4 | ||
|
|
9c79f9d80e | ||
|
|
c55b1188e8 | ||
|
|
d5e4eaf2d8 | ||
|
|
d49cc74828 | ||
|
|
47781fa1e0 | ||
|
|
cc98ed1084 | ||
|
|
bec8cf2b13 | ||
|
|
5bfebbe436 | ||
|
|
c02a4d8138 | ||
|
|
15aae34d65 | ||
|
|
89b0426a2b | ||
|
|
e1f1cab5a5 | ||
|
|
132fb4fe5f | ||
|
|
f1843649ba | ||
|
|
43b7bbb5b3 | ||
|
|
b79b7ceb40 | ||
|
|
2a546f8899 | ||
|
|
a4a0740903 | ||
|
|
075ad01e46 | ||
|
|
1296a1ce28 | ||
|
|
d510ba15c5 | ||
|
|
decc887cb5 | ||
|
|
163f7eeaaa | ||
|
|
d15667d5ce | ||
|
|
39a7869524 | ||
|
|
c9464d2595 | ||
|
|
717de611b9 | ||
|
|
85869cf595 | ||
|
|
03ca28e0d2 | ||
|
|
a3bdf976bb | ||
|
|
bf6eec626c | ||
|
|
cd0bdbbd9c | ||
|
|
e7faa85476 | ||
|
|
e419f95910 | ||
|
|
ee613104f1 | ||
|
|
93a8722b22 | ||
|
|
73b06248aa | ||
|
|
82169f9d58 | ||
|
|
1fa71f2e2d | ||
|
|
af599ab320 | ||
|
|
99eed4bb5c | ||
|
|
d8deb90527 | ||
|
|
37a0010714 | ||
|
|
3c298df5ce | ||
|
|
c6a8618d33 | ||
|
|
f19ddf66b8 | ||
|
|
203d5246eb | ||
|
|
b78276e49a | ||
|
|
bd4d309d89 | ||
|
|
804c09b6c5 | ||
|
|
92202807f7 | ||
|
|
0c92460163 | ||
|
|
8bb570d222 | ||
|
|
f1abce9eff | ||
|
|
1fc46a3c02 | ||
|
|
1d45adfb27 | ||
|
|
8365ad5d1b | ||
|
|
00346ea441 | ||
|
|
ae70d34dd6 | ||
|
|
e0ef62d1b3 | ||
|
|
02e8467fdd | ||
|
|
48e7f8c755 | ||
|
|
19b607b3f2 | ||
|
|
1d827ab2bf | ||
|
|
7af886cf07 | ||
|
|
a76ad6c686 | ||
|
|
3d9a55add3 | ||
|
|
4ceac5e847 | ||
|
|
0939022cb4 | ||
|
|
f7afb9ff15 | ||
|
|
13fd4ba614 | ||
|
|
2f80c9866a | ||
|
|
e2bca647ae | ||
|
|
ec2467486c | ||
|
|
3332271a97 | ||
|
|
7fdfd782d8 | ||
|
|
af7bf7ff7f | ||
|
|
b6dc4d0bd2 | ||
|
|
8ec5dbbf38 | ||
|
|
0c760f3721 | ||
|
|
c45568731f | ||
|
|
ef4c9dc338 | ||
|
|
89de553aba | ||
|
|
247e7b2605 | ||
|
|
f81ba64b91 | ||
|
|
d6fbca1bf1 | ||
|
|
ef9441e7d2 | ||
|
|
6fbf78fa19 | ||
|
|
e38a614c37 | ||
|
|
d8b9665946 | ||
|
|
e655beb01c | ||
|
|
aa1dcb7f99 | ||
|
|
1a2519d647 | ||
|
|
a3572efaa6 | ||
|
|
e28f0d5509 | ||
|
|
789a14054f | ||
|
|
4655b5c732 | ||
|
|
81ebd8c8e7 | ||
|
|
7998520e4a | ||
|
|
460196a62b | ||
|
|
22f43851bd | ||
|
|
91bb63eb97 | ||
|
|
ede1b5f08b | ||
|
|
a26c157c65 | ||
|
|
bcf00a1f8d | ||
|
|
749a94e6e4 | ||
|
|
fc9d1e077c | ||
|
|
a40f0b4038 | ||
|
|
dbf54396f3 | ||
|
|
3fe881e45a | ||
|
|
af03c5163c | ||
|
|
58de84945f | ||
|
|
a2d4252002 | ||
|
|
0689fc19a9 | ||
|
|
68530f6e11 | ||
|
|
fd752bedc5 | ||
|
|
cac880eb1a | ||
|
|
2c66cd4a95 | ||
|
|
64e428f182 | ||
|
|
6f2efdcefe | ||
|
|
9214b2ffcc | ||
|
|
cfb14d4b77 | ||
|
|
5965615e17 | ||
|
|
40eb7d8515 | ||
|
|
e2f36a9bea | ||
|
|
a0ba644488 | ||
|
|
2f67f344b7 | ||
|
|
8bb208aed1 | ||
|
|
48b145a592 | ||
|
|
65de9200fb | ||
|
|
e51d7a7a18 | ||
|
|
c4af50d63a | ||
|
|
933fe8953a | ||
|
|
66aa492d50 | ||
|
|
730934f520 | ||
|
|
d2ec09eaf8 | ||
|
|
7fd3b313b2 | ||
|
|
89f1549741 | ||
|
|
e91015f5c8 | ||
|
|
d9c3edfb12 | ||
|
|
cbf9696f47 | ||
|
|
24e556b9a7 | ||
|
|
eaf29512b6 | ||
|
|
0c1e0ec375 | ||
|
|
b56a054f50 | ||
|
|
33ff4e36de | ||
|
|
bf879934e6 | ||
|
|
b878fa3a80 | ||
|
|
901849f176 | ||
|
|
371c0d22c2 | ||
|
|
4f7f38e0a7 | ||
|
|
85dca2e14e | ||
|
|
e53a5023f1 | ||
|
|
ce8b75d96d | ||
|
|
26f65c4fee | ||
|
|
1ba1e51ca4 | ||
|
|
f674afc412 | ||
|
|
db90b898e1 | ||
|
|
71621c2225 | ||
|
|
ed36fca4a2 | ||
|
|
fdd3699ba5 | ||
|
|
7d4b39643b | ||
|
|
7698cd2c7d | ||
|
|
03c744df54 | ||
|
|
90978d1f35 | ||
|
|
68a2bf271a | ||
|
|
8301384c53 | ||
|
|
045592212a | ||
|
|
45d879e607 | ||
|
|
3e44ee1eba | ||
|
|
36c5fc8d3c | ||
|
|
38ceb85ad9 | ||
|
|
6d18d9226d | ||
|
|
6d5ed2129a | ||
|
|
5cfb6ffa11 | ||
|
|
363b268ccf | ||
|
|
e6cddb0084 | ||
|
|
5e77bf62b9 | ||
|
|
4905c0b179 | ||
|
|
43ab3be804 | ||
|
|
14a6e581bc |
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
|
||||||
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Linting and Tests
|
name: CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -13,11 +13,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
- "3.6"
|
|
||||||
- "3.7"
|
|
||||||
- "3.8"
|
- "3.8"
|
||||||
- "3.9"
|
- "3.9"
|
||||||
- "3.10"
|
- "3.10"
|
||||||
|
- "3.11"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Python 3
|
- name: Install Python 3
|
||||||
@@ -35,31 +34,30 @@ jobs:
|
|||||||
which meshtastic
|
which meshtastic
|
||||||
meshtastic --version
|
meshtastic --version
|
||||||
- name: Run pylint
|
- name: Run pylint
|
||||||
run: pylint meshtastic
|
run: pylint meshtastic examples/ --ignore-patterns ".*_pb2.py$"
|
||||||
- name: Run tests with pytest
|
- name: Run tests with pytest
|
||||||
run: pytest --cov=meshtastic
|
run: pytest --cov=meshtastic
|
||||||
- name: Generate coverage report
|
- name: Generate coverage report
|
||||||
run: |
|
run: |
|
||||||
pytest --cov=meshtastic --cov-report=xml
|
pytest --cov=meshtastic --cov-report=xml
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
file: ./coverage.xml
|
files: ./coverage.xml
|
||||||
flags: unittests
|
flags: unittests
|
||||||
name: codecov-umbrella
|
name: codecov-umbrella
|
||||||
yml: ./codecov.yml
|
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
verbose: true
|
||||||
validate:
|
validate:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
- "3.6"
|
|
||||||
- "3.7"
|
|
||||||
- "3.8"
|
- "3.8"
|
||||||
- "3.9"
|
- "3.9"
|
||||||
- "3.10"
|
- "3.10"
|
||||||
|
- "3.11"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Python 3
|
- 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
|
||||||
76
.github/workflows/release.yml
vendored
76
.github/workflows/release.yml
vendored
@@ -1,40 +1,52 @@
|
|||||||
name: Make Release
|
name: Make Release
|
||||||
on:
|
on: workflow_dispatch
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: "Release version (Example: 1.0.0, must match 'version' in setup.py)"
|
|
||||||
required: true
|
|
||||||
default: '1.0.0'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release_create:
|
release_create:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Create GitHub release
|
|
||||||
uses: actions/create-release@v1
|
|
||||||
id: create_release
|
|
||||||
with:
|
|
||||||
draft: true
|
|
||||||
prerelease: true
|
|
||||||
release_name: ${{ github.event.inputs.version}}
|
|
||||||
tag_name: ${{ github.event.inputs.version}}
|
|
||||||
body: |
|
|
||||||
Autogenerated by github action, developer should edit as required before publishing...
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
|
version: ${{ steps.get_version.outputs.version }}
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
new_sha: ${{ steps.commit_updated.outputs.sha }}
|
||||||
|
|
||||||
publish_to_pypi:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- 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: Get version
|
||||||
|
id: get_version
|
||||||
|
run: >-
|
||||||
|
bin/show_version.py
|
||||||
|
|
||||||
|
- 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 }}
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
- name: Set up Python 3.9
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
@@ -62,14 +74,14 @@ jobs:
|
|||||||
user: __token__
|
user: __token__
|
||||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
build-and-publish-mac:
|
build-and-publish-mac:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
needs: release_create
|
needs: release_create
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.release_create.outputs.new_sha }}
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
- name: Set up Python 3.9
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
@@ -112,9 +124,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: release_create
|
needs: release_create
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.release_create.outputs.new_sha }}
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
- name: Set up Python 3.9
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
@@ -152,9 +165,10 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: release_create
|
needs: release_create
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.release_create.outputs.new_sha }}
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
- name: Set up Python 3.9
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
|||||||
11
.github/workflows/update_protobufs.yml
vendored
11
.github/workflows/update_protobufs.yml
vendored
@@ -18,18 +18,19 @@ jobs:
|
|||||||
|
|
||||||
- name: Download nanopb
|
- name: Download nanopb
|
||||||
run: |
|
run: |
|
||||||
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.4-linux-x86.tar.gz
|
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.6-linux-x86.tar.gz
|
||||||
tar xvzf nanopb-0.4.4-linux-x86.tar.gz
|
tar xvzf nanopb-0.4.6-linux-x86.tar.gz
|
||||||
mv nanopb-0.4.4-linux-x86 nanopb-0.4.4
|
mv nanopb-0.4.6-linux-x86 nanopb-0.4.6
|
||||||
|
|
||||||
- name: Re-generate protocol buffers
|
- name: Re-generate protocol buffers
|
||||||
run: |
|
run: |
|
||||||
./bin/regen-protos.sh
|
./bin/regen-protobufs.sh
|
||||||
|
|
||||||
- name: Commit update
|
- name: Commit update
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name 'github-actions'
|
git config --global user.name 'github-actions'
|
||||||
git config --global user.email 'bot@noreply.github.com'
|
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 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"
|
git commit -m "Update protobuf submodule" && git push || echo "No changes to commit"
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,7 +5,7 @@ dist
|
|||||||
*.egg-info
|
*.egg-info
|
||||||
log_*
|
log_*
|
||||||
.eggs
|
.eggs
|
||||||
nanopb-0.4.4
|
nanopb-*
|
||||||
.*swp
|
.*swp
|
||||||
.coverage
|
.coverage
|
||||||
*.py-E
|
*.py-E
|
||||||
|
|||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
|||||||
[submodule "proto"]
|
[submodule "protobufs"]
|
||||||
path = proto
|
path = protobufs
|
||||||
url = https://github.com/meshtastic/Meshtastic-protobufs.git
|
url = http://github.com/meshtastic/protobufs
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
# Add files or directories matching the regex patterns to the blacklist. The
|
# Add files or directories matching the regex patterns to the blacklist. The
|
||||||
# regex matches against base names, not paths.
|
# regex matches against base names, not paths.
|
||||||
ignore-patterns=mqtt_pb2.py,channel_pb2.py,environmental_measurement_pb2.py,admin_pb2.py,radioconfig_pb2.py,deviceonly_pb2.py,apponly_pb2.py,remote_hardware_pb2.py,portnums_pb2.py,mesh_pb2.py,storeforward_pb2.py,cannedmessages_pb2.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,8 +23,7 @@ ignore-patterns=mqtt_pb2.py,channel_pb2.py,environmental_measurement_pb2.py,admi
|
|||||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||||
# --disable=W"
|
# --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
|
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]
|
[BASIC]
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ bad-names=foo,bar,baz,toto,tutu,tata
|
|||||||
max-line-length=150
|
max-line-length=150
|
||||||
|
|
||||||
# Maximum number of lines in a module
|
# Maximum number of lines in a module
|
||||||
max-module-lines=1200
|
max-module-lines=1600
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
7
.trunk/.gitignore
vendored
Normal file
7
.trunk/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
*out
|
||||||
|
*logs
|
||||||
|
*actions
|
||||||
|
*notifications
|
||||||
|
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"]
|
||||||
39
.trunk/trunk.yaml
Normal file
39
.trunk/trunk.yaml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
version: 0.1
|
||||||
|
cli:
|
||||||
|
version: 1.7.0
|
||||||
|
plugins:
|
||||||
|
sources:
|
||||||
|
- id: trunk
|
||||||
|
ref: v0.0.14
|
||||||
|
uri: https://github.com/trunk-io/plugins
|
||||||
|
lint:
|
||||||
|
ignore:
|
||||||
|
- linters: [ALL]
|
||||||
|
paths:
|
||||||
|
# Ignore generated files
|
||||||
|
- meshtastic/*_pb2.py
|
||||||
|
enabled:
|
||||||
|
- actionlint@1.6.23
|
||||||
|
- black@23.3.0
|
||||||
|
- git-diff-check
|
||||||
|
- gitleaks@8.16.2
|
||||||
|
- isort@5.12.0
|
||||||
|
- markdownlint@0.33.0
|
||||||
|
- prettier@2.8.7
|
||||||
|
- pylint@2.17.1
|
||||||
|
- ruff@0.0.260
|
||||||
|
- shellcheck@0.9.0
|
||||||
|
- shfmt@3.5.0
|
||||||
|
- yamllint@1.30.0
|
||||||
|
runtimes:
|
||||||
|
enabled:
|
||||||
|
- go@1.19.5
|
||||||
|
- node@18.12.1
|
||||||
|
- python@3.10.8
|
||||||
|
actions:
|
||||||
|
disabled:
|
||||||
|
- trunk-announce
|
||||||
|
- trunk-check-pre-push
|
||||||
|
- trunk-fmt-pre-commit
|
||||||
|
enabled:
|
||||||
|
- trunk-upgrade-available
|
||||||
93
.vscode/launch.json
vendored
93
.vscode/launch.json
vendored
@@ -42,7 +42,87 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"module": "meshtastic",
|
"module": "meshtastic",
|
||||||
"justMyCode": true,
|
"justMyCode": true,
|
||||||
"args": ["--debug" ]
|
"args": ["--debug"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug getPref",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--get", "power.is_power_saving"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug getPref telemetry",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--get", "telemetry.environment_update_interval"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug info",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--info"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug set region",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--set", "lora.region", "TW"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug set bluetooth fixed pin",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--set", "bluetooth.fixed_pin", "555555"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug get bluetooth fixed pin",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--get", "bluetooth.fixed_pin"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug setPref",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--set", "power.is_power_saving", "1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug setPref telemetry.environment_measurement_enabled",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--set", "telemetry.environment_measurement_enabled", "1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug setPref telemetry.environment_screen_enabled",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--set", "telemetry.environment_screen_enabled", "1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic debug setPref telemetry",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--set", "telemetry.environment_measurement_enabled", "1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "meshtastic setpref",
|
"name": "meshtastic setpref",
|
||||||
@@ -52,6 +132,15 @@
|
|||||||
"justMyCode": true,
|
"justMyCode": true,
|
||||||
"args": ["--debug", "--setchan", "psk", ""]
|
"args": ["--debug", "--setchan", "psk", ""]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "meshtastic --ch-set",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "meshtastic",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": ["--debug", "--ch-set", "channel_num", "0", "--ch-index", "0"]
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "meshtastic seturl",
|
"name": "meshtastic seturl",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
@@ -92,7 +181,7 @@
|
|||||||
"module": "meshtastic",
|
"module": "meshtastic",
|
||||||
"justMyCode": true,
|
"justMyCode": true,
|
||||||
"args": ["--debug", "--sendtext", "pytest"]
|
"args": ["--debug", "--sendtext", "pytest"]
|
||||||
}
|
},
|
||||||
{
|
{
|
||||||
"name": "meshtastic showNodes",
|
"name": "meshtastic showNodes",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -6,6 +6,10 @@ test:
|
|||||||
virt:
|
virt:
|
||||||
pytest -m smokevirt
|
pytest -m smokevirt
|
||||||
|
|
||||||
|
# run the smoke1 test (after doing a factory reset and unplugging/replugging in device)
|
||||||
|
smoke1:
|
||||||
|
pytest -m smoke1 -s -vv
|
||||||
|
|
||||||
# local install
|
# local install
|
||||||
install:
|
install:
|
||||||
pip install .
|
pip install .
|
||||||
@@ -16,12 +20,18 @@ docs:
|
|||||||
|
|
||||||
# lint the codebase
|
# lint the codebase
|
||||||
lint:
|
lint:
|
||||||
pylint meshtastic
|
pylint meshtastic examples
|
||||||
|
|
||||||
# show the slowest unit tests
|
# show the slowest unit tests
|
||||||
slow:
|
slow:
|
||||||
pytest -m unit --durations=5
|
pytest -m unit --durations=5
|
||||||
|
|
||||||
|
protobufs: FORCE
|
||||||
|
git submodule update --init --recursive
|
||||||
|
git pull --rebase
|
||||||
|
git submodule update --remote --merge
|
||||||
|
./bin/regen-protobufs.sh
|
||||||
|
|
||||||
# run the coverage report and open results in a browser
|
# run the coverage report and open results in a browser
|
||||||
cov:
|
cov:
|
||||||
pytest --cov-report html --cov=meshtastic
|
pytest --cov-report html --cov=meshtastic
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -1,15 +1,23 @@
|
|||||||
# Meshtastic-python
|
# Meshtastic Python
|
||||||
|
|
||||||
[](https://open.vscode.dev/meshtastic/Meshtastic-python)
|
|
||||||

|
|
||||||
[](https://codecov.io/gh/meshtastic/Meshtastic-python)
|
[](https://codecov.io/gh/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)
|
||||||
|
|
||||||
A python client for using [Meshtastic](https://www.meshtastic.org) devices. This small library (and example application) provides an easy API for sending and receiving messages over mesh radios. It also provides access to any of the operations/data available in the device user interface or the Android application. Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
|
## Overview
|
||||||
|
|
||||||
Full documentation including examples [here](https://meshtastic.org/docs/software/python/python-installation).
|
A Python client for use with Meshtastic devices.
|
||||||
|
This small library (and example application) provides an easy API for sending and receiving messages over mesh radios.
|
||||||
|
It also provides access to any of the operations/data available in the device user interface or the Android application.
|
||||||
|
Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
|
||||||
|
|
||||||
The library api is documented [here](https://meshtastic-python.vercel.app/meshtastic/index.html)
|
**[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)**
|
||||||
|
|
||||||
|
**[Documentation/API Reference](https://python.meshtastic.org/)**
|
||||||
|
|
||||||
[](https://vercel.com?utm_source=meshtastic&utm_campaign=oss)
|
## Stats
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
2
TODO.md
2
TODO.md
@@ -34,7 +34,7 @@ Basic functionality is complete now.
|
|||||||
- DONE add fromId and toId to received messages dictionaries
|
- DONE add fromId and toId to received messages dictionaries
|
||||||
- make command line options for displaying/changing config
|
- make command line options for displaying/changing config
|
||||||
- update nodedb as nodes change
|
- update nodedb as nodes change
|
||||||
- radioConfig - getter/setter syntax: https://www.python-course.eu/python3_properties.php
|
- localConfig - getter/setter syntax: https://www.python-course.eu/python3_properties.php
|
||||||
- let user change radio params via commandline options
|
- let user change radio params via commandline options
|
||||||
- keep nodedb up-to-date based on received MeshPackets
|
- keep nodedb up-to-date based on received MeshPackets
|
||||||
- handle radio reboots and redownload db when that happens. Look for a special FromRadio.rebooted packet
|
- handle radio reboots and redownload db when that happens. Look for a special FromRadio.rebooted packet
|
||||||
|
|||||||
30
bin/bump_version.py
Executable file
30
bin/bump_version.py
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Bump the version number"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
version_filename = "setup.py"
|
||||||
|
|
||||||
|
lines = None
|
||||||
|
|
||||||
|
with open(version_filename, "r", encoding="utf-8") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
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('"', "")
|
||||||
|
# get rid of trailing comma
|
||||||
|
line = line.replace(",", "")
|
||||||
|
# split on '='
|
||||||
|
words = line.split("=")
|
||||||
|
# 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", ""
|
||||||
|
)
|
||||||
|
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.
|
# 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
|
# see sachaw if you have questions
|
||||||
pdoc3 --html -f --output-dir docs meshtastic
|
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.6/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.4/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
|
|
||||||
20
bin/show_version.py
Executable file
20
bin/show_version.py
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Show the version number"""
|
||||||
|
|
||||||
|
version_filename = "setup.py"
|
||||||
|
|
||||||
|
lines = None
|
||||||
|
|
||||||
|
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('"', "")
|
||||||
|
# get rid of the trailing comma
|
||||||
|
line2 = line2.replace(",", "")
|
||||||
|
# split on =
|
||||||
|
words = line2.split("=")
|
||||||
|
# Note: This format is for github actions
|
||||||
|
print(f"::set-output name=version::{words[1].strip()}")
|
||||||
@@ -11,7 +11,6 @@ location:
|
|||||||
|
|
||||||
userPrefs:
|
userPrefs:
|
||||||
region: 1
|
region: 1
|
||||||
isAlwaysPowered: 'true'
|
isAlwaysPowered: "true"
|
||||||
sendOwnerInterval: 2
|
|
||||||
screenOnSecs: 31536000
|
screenOnSecs: 31536000
|
||||||
waitBluetoothSecs: 31536000
|
waitBluetoothSecs: 31536000
|
||||||
|
|||||||
@@ -2,17 +2,42 @@
|
|||||||
owner: Bob TBeam
|
owner: Bob TBeam
|
||||||
owner_short: BOB
|
owner_short: BOB
|
||||||
|
|
||||||
channel_url: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
channel_url: https://www.meshtastic.org/e/#CgMSAQESCDgBQANIAVAe
|
||||||
|
|
||||||
location:
|
location:
|
||||||
lat: 35.88888
|
lat: 35.88888
|
||||||
lon: -93.88888
|
lon: -93.88888
|
||||||
alt: 304
|
alt: 304
|
||||||
|
|
||||||
user_prefs:
|
config:
|
||||||
region: 1
|
bluetooth:
|
||||||
is_always_powered: 'true'
|
enabled: true
|
||||||
send_owner_interval: 2
|
fixedPin: 123456
|
||||||
screen_on_secs: 31536000
|
device:
|
||||||
wait_bluetooth_secs: 31536000
|
serialEnabled: true
|
||||||
location_share: 'LocEnabled'
|
display:
|
||||||
|
screenOnSecs: 600
|
||||||
|
lora:
|
||||||
|
region: US
|
||||||
|
hopLimit: 3
|
||||||
|
txEnabled: true
|
||||||
|
txPower: 30
|
||||||
|
network:
|
||||||
|
ntpServer: 0.pool.ntp.org
|
||||||
|
position:
|
||||||
|
gpsAttemptTime: 900
|
||||||
|
gpsEnabled: true
|
||||||
|
gpsUpdateInterval: 120
|
||||||
|
positionBroadcastSecs: 900
|
||||||
|
positionBroadcastSmartEnabled: true
|
||||||
|
positionFlags: 3
|
||||||
|
power:
|
||||||
|
lsSecs: 300
|
||||||
|
meshSdsTimeoutSecs: 7200
|
||||||
|
minWakeSecs: 10
|
||||||
|
sdsSecs: 4294967295
|
||||||
|
|
||||||
|
module_config:
|
||||||
|
telemetry:
|
||||||
|
deviceUpdateInterval: 900
|
||||||
|
environmentUpdateInterval: 900
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import meshtastic
|
import meshtastic
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
@@ -15,6 +16,6 @@ if len(sys.argv) != 1:
|
|||||||
iface = meshtastic.serial_interface.SerialInterface()
|
iface = meshtastic.serial_interface.SerialInterface()
|
||||||
if iface.nodes:
|
if iface.nodes:
|
||||||
for n in iface.nodes.values():
|
for n in iface.nodes.values():
|
||||||
if n['num'] == iface.myInfo.my_node_num:
|
if n["num"] == iface.myInfo.my_node_num:
|
||||||
print(n['user']['hwModel'])
|
print(n["user"]["hwModel"])
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import meshtastic
|
import meshtastic
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
|
|||||||
20
examples/info_example.py
Normal file
20
examples/info_example.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"""Simple program to demo how to use meshtastic library.
|
||||||
|
To run: python examples/info.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
|
iface = meshtastic.serial_interface.SerialInterface()
|
||||||
|
|
||||||
|
# call showInfo() just to ensure values are populated
|
||||||
|
# info = iface.showInfo()
|
||||||
|
|
||||||
|
|
||||||
|
if iface.nodes:
|
||||||
|
for n in iface.nodes.values():
|
||||||
|
if n["num"] == iface.myInfo.my_node_num:
|
||||||
|
print(n["user"]["hwModel"])
|
||||||
|
break
|
||||||
|
|
||||||
|
iface.close()
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
|
||||||
import meshtastic
|
import meshtastic
|
||||||
@@ -13,10 +14,13 @@ if len(sys.argv) < 2:
|
|||||||
print(f"usage: {sys.argv[0]} host")
|
print(f"usage: {sys.argv[0]} host")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def onConnection(interface, topic=pub.AUTO_TOPIC):
|
|
||||||
|
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||||
"""This is called when we (re)connect to the radio."""
|
"""This is called when we (re)connect to the radio."""
|
||||||
print(interface.myInfo)
|
print(interface.myInfo)
|
||||||
interface.close()
|
interface.close()
|
||||||
|
|
||||||
|
|
||||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
|
||||||
import meshtastic
|
import meshtastic
|
||||||
@@ -14,15 +15,18 @@ if len(sys.argv) < 2:
|
|||||||
print(f"usage: {sys.argv[0]} host")
|
print(f"usage: {sys.argv[0]} host")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def onReceive(packet, interface):
|
|
||||||
|
def onReceive(packet, interface): # pylint: disable=unused-argument
|
||||||
"""called when a packet arrives"""
|
"""called when a packet arrives"""
|
||||||
print(f"Received: {packet}")
|
print(f"Received: {packet}")
|
||||||
|
|
||||||
def onConnection(interface, topic=pub.AUTO_TOPIC):
|
|
||||||
|
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
|
||||||
"""called when we (re)connect to the radio"""
|
"""called when we (re)connect to the radio"""
|
||||||
# defaults to broadcast, specify a destination ID if you wish
|
# defaults to broadcast, specify a destination ID if you wish
|
||||||
interface.sendText("hello mesh")
|
interface.sendText("hello mesh")
|
||||||
|
|
||||||
|
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
pub.subscribe(onReceive, "meshtastic.receive")
|
||||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||||
try:
|
try:
|
||||||
@@ -30,6 +34,6 @@ try:
|
|||||||
while True:
|
while True:
|
||||||
time.sleep(1000)
|
time.sleep(1000)
|
||||||
iface.close()
|
iface.close()
|
||||||
except(Exception) as ex:
|
except Exception as ex:
|
||||||
print(f"Error: Could not connect to {sys.argv[1]} {ex}")
|
print(f"Error: Could not connect to {sys.argv[1]} {ex}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import meshtastic
|
|
||||||
from meshtastic.supported_device import get_unique_vendor_ids, active_ports_on_supported_devices
|
from meshtastic.util import (
|
||||||
from meshtastic.util import detect_supported_devices
|
active_ports_on_supported_devices,
|
||||||
|
detect_supported_devices,
|
||||||
|
get_unique_vendor_ids,
|
||||||
|
)
|
||||||
|
|
||||||
# simple arg check
|
# simple arg check
|
||||||
if len(sys.argv) != 1:
|
if len(sys.argv) != 1:
|
||||||
@@ -14,13 +17,13 @@ if len(sys.argv) != 1:
|
|||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
||||||
vids = get_unique_vendor_ids()
|
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()
|
sds = detect_supported_devices()
|
||||||
if len(sds) > 0:
|
if len(sds) > 0:
|
||||||
print('Detected possible devices:')
|
print("Detected possible devices:")
|
||||||
for d in sds:
|
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)
|
ports = active_ports_on_supported_devices(sds)
|
||||||
print(f'ports:{ports}')
|
print(f"ports:{ports}")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import meshtastic
|
import meshtastic
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
|
|
||||||
|
|||||||
6
examples/show_ports.py
Normal file
6
examples/show_ports.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
"""Simple program to show serial ports.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from meshtastic.util import findPorts
|
||||||
|
|
||||||
|
print(findPorts())
|
||||||
14
examples/tcp_gps_example.py
Normal file
14
examples/tcp_gps_example.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"""Demonstration of how to look up a radio's location via its LAN connection.
|
||||||
|
Before running, connect your machine to the same WiFi network as the radio.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import meshtastic
|
||||||
|
import meshtastic.tcp_interface
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
iface.close()
|
||||||
30
info/mac/nano_g1.txt
Normal file
30
info/mac/nano_g1.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
meshtastic detected port: /dev/cu.wchusbserial53820208781
|
||||||
|
|
||||||
|
ioreg -p IOUSB
|
||||||
|
|
||||||
|
shows this:
|
||||||
|
|
||||||
|
| +-o USB Single Serial@14300000 <class AppleUSBDevice, id 0x1000407a5, registered, matched, active, busy 0 (18 ms), retain 14>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
system_profiler SPUSBDataType > /tmp/a
|
||||||
|
with device plugged in
|
||||||
|
|
||||||
|
system_profiler SPUSBDataType > /tmp/b
|
||||||
|
with device not plugged in
|
||||||
|
|
||||||
|
diff /tmp/a /tmp/b
|
||||||
|
|
||||||
|
< USB Single Serial:
|
||||||
|
<
|
||||||
|
< Product ID: 0x55d4
|
||||||
|
< Vendor ID: 0x1a86
|
||||||
|
< Version: 4.43
|
||||||
|
< Serial Number: 5382020878
|
||||||
|
< Speed: Up to 12 Mb/s
|
||||||
|
< Location ID: 0x14300000 / 63
|
||||||
|
< Current Available (mA): 500
|
||||||
|
< Current Required (mA): 134
|
||||||
|
< Extra Operating Current (mA): 0
|
||||||
52
info/mac/rak11200.txt
Normal file
52
info/mac/rak11200.txt
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
|
||||||
|
% ioreg -p IOUSB > /tmp/a
|
||||||
|
# only a solid red light (pins GRND and BOOT0 jumpered)
|
||||||
|
|
||||||
|
% ioreg -p IOUSB > /tmp/b
|
||||||
|
# solid red light and solid green light
|
||||||
|
|
||||||
|
% ioreg -p IOUSB > /tmp/c
|
||||||
|
# nothing plugged in
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% diff /tmp/a /tmp/c
|
||||||
|
13c13
|
||||||
|
< +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 11>
|
||||||
|
---
|
||||||
|
> +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 10>
|
||||||
|
18d17
|
||||||
|
< +-o USB Serial@14300000 <class AppleUSBDevice, id 0x100004ca4, registered, matched, active, busy 0 (10 ms), retain 12>
|
||||||
|
|
||||||
|
|
||||||
|
% diff /tmp/b /tmp/c
|
||||||
|
13c13
|
||||||
|
< +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 11>
|
||||||
|
---
|
||||||
|
> +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 10>
|
||||||
|
18d17
|
||||||
|
< +-o USB Serial@14300000 <class AppleUSBDevice, id 0x100004ce5, registered, matched, active, busy 0 (11 ms), retain 12>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
system_profiler SPUSBDataType > /tmp/d
|
||||||
|
# red solid
|
||||||
|
|
||||||
|
|
||||||
|
system_profiler SPUSBDataType > /tmp/e
|
||||||
|
# nothing
|
||||||
|
|
||||||
|
|
||||||
|
% diff /tmp/d /tmp/e
|
||||||
|
38,48d37
|
||||||
|
< USB Serial:
|
||||||
|
<
|
||||||
|
< Product ID: 0x7523
|
||||||
|
< Vendor ID: 0x1a86
|
||||||
|
< Version: 2.64
|
||||||
|
< Speed: Up to 12 Mb/s
|
||||||
|
< Location ID: 0x14300000 / 33
|
||||||
|
< Current Available (mA): 500
|
||||||
|
< Current Required (mA): 98
|
||||||
|
< Extra Operating Current (mA): 0
|
||||||
@@ -30,3 +30,11 @@ drwxrwxrwx 1 bob staff 512 Feb 1 16:47 .fseventsd
|
|||||||
% diff /tmp/a /tmp/c
|
% diff /tmp/a /tmp/c
|
||||||
<snip>
|
<snip>
|
||||||
> | +-o T-Echo v1@14300000 <class AppleUSBDevice, id 0x100061000, registered, matched, active, busy 0 (25 ms), retain 16>
|
> | +-o T-Echo v1@14300000 <class AppleUSBDevice, id 0x100061000, registered, matched, active, busy 0 (25 ms), retain 16>
|
||||||
|
|
||||||
|
contents of: INFO_UF2.TXT
|
||||||
|
|
||||||
|
UF2 Bootloader 0.6.1-2-g1224915 lib/nrfx (v2.0.0) lib/tinyusb (0.10.1-293-gaf8e5a90) lib/uf2 (remotes/origin/configupdate-9-gadbb8c7)
|
||||||
|
Model: LilyGo T-Echo
|
||||||
|
Board-ID: nRF52840-TEcho-v1
|
||||||
|
SoftDevice: S140 version 6.1.1
|
||||||
|
Date: Oct 13 2021
|
||||||
|
|||||||
91
info/ubuntu/nano_g1.txt
Normal file
91
info/ubuntu/nano_g1.txt
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
lsusb -d 1a86: -v
|
||||||
|
|
||||||
|
Bus 001 Device 013: ID 1a86:55d4 QinHeng Electronics
|
||||||
|
Couldn't open device, some information will be missing
|
||||||
|
Device Descriptor:
|
||||||
|
bLength 18
|
||||||
|
bDescriptorType 1
|
||||||
|
bcdUSB 1.10
|
||||||
|
bDeviceClass 2 Communications
|
||||||
|
bDeviceSubClass 0
|
||||||
|
bDeviceProtocol 0
|
||||||
|
bMaxPacketSize0 8
|
||||||
|
idVendor 0x1a86 QinHeng Electronics
|
||||||
|
idProduct 0x55d4
|
||||||
|
bcdDevice 4.43
|
||||||
|
iManufacturer 0
|
||||||
|
iProduct 2
|
||||||
|
iSerial 3
|
||||||
|
bNumConfigurations 1
|
||||||
|
Configuration Descriptor:
|
||||||
|
bLength 9
|
||||||
|
bDescriptorType 2
|
||||||
|
wTotalLength 0x0043
|
||||||
|
bNumInterfaces 2
|
||||||
|
bConfigurationValue 1
|
||||||
|
iConfiguration 0
|
||||||
|
bmAttributes 0xa0
|
||||||
|
(Bus Powered)
|
||||||
|
Remote Wakeup
|
||||||
|
MaxPower 134mA
|
||||||
|
Interface Descriptor:
|
||||||
|
bLength 9
|
||||||
|
bDescriptorType 4
|
||||||
|
bInterfaceNumber 0
|
||||||
|
bAlternateSetting 0
|
||||||
|
bNumEndpoints 1
|
||||||
|
bInterfaceClass 2 Communications
|
||||||
|
bInterfaceSubClass 2 Abstract (modem)
|
||||||
|
bInterfaceProtocol 1 AT-commands (v.25ter)
|
||||||
|
iInterface 0
|
||||||
|
CDC Header:
|
||||||
|
bcdCDC 1.10
|
||||||
|
CDC Call Management:
|
||||||
|
bmCapabilities 0x00
|
||||||
|
bDataInterface 1
|
||||||
|
CDC ACM:
|
||||||
|
bmCapabilities 0x02
|
||||||
|
line coding and serial state
|
||||||
|
CDC Union:
|
||||||
|
bMasterInterface 0
|
||||||
|
bSlaveInterface 1
|
||||||
|
Endpoint Descriptor:
|
||||||
|
bLength 7
|
||||||
|
bDescriptorType 5
|
||||||
|
bEndpointAddress 0x83 EP 3 IN
|
||||||
|
bmAttributes 3
|
||||||
|
Transfer Type Interrupt
|
||||||
|
Synch Type None
|
||||||
|
Usage Type Data
|
||||||
|
wMaxPacketSize 0x0010 1x 16 bytes
|
||||||
|
bInterval 1
|
||||||
|
Interface Descriptor:
|
||||||
|
bLength 9
|
||||||
|
bDescriptorType 4
|
||||||
|
bInterfaceNumber 1
|
||||||
|
bAlternateSetting 0
|
||||||
|
bNumEndpoints 2
|
||||||
|
bInterfaceClass 10 CDC Data
|
||||||
|
bInterfaceSubClass 0
|
||||||
|
bInterfaceProtocol 0
|
||||||
|
iInterface 0
|
||||||
|
Endpoint Descriptor:
|
||||||
|
bLength 7
|
||||||
|
bDescriptorType 5
|
||||||
|
bEndpointAddress 0x02 EP 2 OUT
|
||||||
|
bmAttributes 2
|
||||||
|
Transfer Type Bulk
|
||||||
|
Synch Type None
|
||||||
|
Usage Type Data
|
||||||
|
wMaxPacketSize 0x0020 1x 32 bytes
|
||||||
|
bInterval 0
|
||||||
|
Endpoint Descriptor:
|
||||||
|
bLength 7
|
||||||
|
bDescriptorType 5
|
||||||
|
bEndpointAddress 0x82 EP 2 IN
|
||||||
|
bmAttributes 2
|
||||||
|
Transfer Type Bulk
|
||||||
|
Synch Type None
|
||||||
|
Usage Type Data
|
||||||
|
wMaxPacketSize 0x0040 1x 64 bytes
|
||||||
|
bInterval 0
|
||||||
39
info/windows/nano_g1.txt
Normal file
39
info/windows/nano_g1.txt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
Get-PnpDevice -PresentOnly | Format-List >a
|
||||||
|
Get-PnpDevice -PresentOnly | Format-List >b
|
||||||
|
Compare-Object (get-content a) (Get-Content b)
|
||||||
|
|
||||||
|
InputObject SideIndicator
|
||||||
|
----------- -------------
|
||||||
|
Caption : USB-Enhanced-SERIAL CH9102 (COM9) =>
|
||||||
|
Description : USB-Enhanced-SERIAL CH9102 =>
|
||||||
|
Name : USB-Enhanced-SERIAL CH9102 (COM9) =>
|
||||||
|
DeviceID : USB\VID_1A86&PID_55D4\5382020745 =>
|
||||||
|
PNPDeviceID : USB\VID_1A86&PID_55D4\5382020745 =>
|
||||||
|
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} =>
|
||||||
|
CompatibleID : {USB\Class_02&SubClass_02&Prot_01, USB\Class_02&SubClass_02, USB\Class_02} =>
|
||||||
|
HardwareID : {USB\VID_1A86&PID_55D4&REV_0443, USB\VID_1A86&PID_55D4} =>
|
||||||
|
Manufacturer : wch.cn =>
|
||||||
|
PNPClass : Ports =>
|
||||||
|
Service : CH343SER_A64 =>
|
||||||
|
Class : Ports =>
|
||||||
|
FriendlyName : USB-Enhanced-SERIAL CH9102 (COM9) =>
|
||||||
|
InstanceId : USB\VID_1A86&PID_55D4\5382020745 =>
|
||||||
|
InstallDate : =>
|
||||||
|
Status : OK =>
|
||||||
|
Availability : =>
|
||||||
|
ConfigManagerErrorCode : CM_PROB_NONE =>
|
||||||
|
ConfigManagerUserConfig : False =>
|
||||||
|
CreationClassName : Win32_PnPEntity =>
|
||||||
|
ErrorCleared : =>
|
||||||
|
ErrorDescription : =>
|
||||||
|
LastErrorCode : =>
|
||||||
|
PowerManagementCapabilities : =>
|
||||||
|
PowerManagementSupported : =>
|
||||||
|
StatusInfo : =>
|
||||||
|
SystemCreationClassName : Win32_ComputerSystem =>
|
||||||
|
SystemName : DESKTOP-FRFQN8H =>
|
||||||
|
Present : True =>
|
||||||
|
PSComputerName : =>
|
||||||
|
Problem : CM_PROB_NONE =>
|
||||||
|
ProblemDescription : =>
|
||||||
|
=>
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
Primary class: SerialInterface
|
Primary class: SerialInterface
|
||||||
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
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:
|
properties of SerialInterface:
|
||||||
|
|
||||||
- radioConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
|
- localConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
|
||||||
the device.
|
the device.
|
||||||
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
|
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
|
||||||
node in the mesh. This is a read-only datastructure.
|
node in the mesh. This is a read-only datastructure.
|
||||||
@@ -62,39 +62,48 @@ import os
|
|||||||
import platform
|
import platform
|
||||||
import random
|
import random
|
||||||
import socket
|
import socket
|
||||||
import sys
|
|
||||||
import stat
|
import stat
|
||||||
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import *
|
from typing import *
|
||||||
|
|
||||||
|
import google.protobuf.json_format
|
||||||
import serial
|
import serial
|
||||||
import timeago
|
import timeago
|
||||||
import google.protobuf.json_format
|
|
||||||
from pubsub import pub
|
|
||||||
from dotmap import DotMap
|
from dotmap import DotMap
|
||||||
from tabulate import tabulate
|
|
||||||
from google.protobuf.json_format import MessageToJson
|
from google.protobuf.json_format import MessageToJson
|
||||||
from meshtastic.util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
from pubsub import pub
|
||||||
from meshtastic.node import Node
|
from tabulate import tabulate
|
||||||
from meshtastic import (mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2,
|
|
||||||
environmental_measurement_pb2, remote_hardware_pb2,
|
|
||||||
channel_pb2, radioconfig_pb2, util)
|
|
||||||
|
|
||||||
|
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.
|
# Note: To follow PEP224, comments should be after the module variable.
|
||||||
|
|
||||||
LOCAL_ADDR = "^local"
|
LOCAL_ADDR = "^local"
|
||||||
"""A special ID that means the local node"""
|
"""A special ID that means the local node"""
|
||||||
|
|
||||||
BROADCAST_NUM = 0xffffffff
|
BROADCAST_NUM = 0xFFFFFFFF
|
||||||
"""if using 8 bit nodenums this will be shortend on the target"""
|
"""if using 8 bit nodenums this will be shortened on the target"""
|
||||||
|
|
||||||
BROADCAST_ADDR = "^all"
|
BROADCAST_ADDR = "^all"
|
||||||
"""A special ID that means broadcast"""
|
"""A special ID that means broadcast"""
|
||||||
|
|
||||||
OUR_APP_VERSION = 20200
|
OUR_APP_VERSION = 20300
|
||||||
"""The numeric buildnumber (shared with android apps) specifying the
|
"""The numeric buildnumber (shared with android apps) specifying the
|
||||||
level of device code we are guaranteed to understand
|
level of device code we are guaranteed to understand
|
||||||
|
|
||||||
@@ -106,6 +115,7 @@ publishingThread = DeferredExecution("publishing")
|
|||||||
|
|
||||||
class ResponseHandler(NamedTuple):
|
class ResponseHandler(NamedTuple):
|
||||||
"""A pending response callback, waiting for a response to one of our messages"""
|
"""A pending response callback, waiting for a response to one of our messages"""
|
||||||
|
|
||||||
# requestId: int - used only as a key
|
# requestId: int - used only as a key
|
||||||
callback: Callable
|
callback: Callable
|
||||||
# FIXME, add timestamp and age out old requests
|
# FIXME, add timestamp and age out old requests
|
||||||
@@ -113,6 +123,7 @@ class ResponseHandler(NamedTuple):
|
|||||||
|
|
||||||
class KnownProtocol(NamedTuple):
|
class KnownProtocol(NamedTuple):
|
||||||
"""Used to automatically decode known protocol payloads"""
|
"""Used to automatically decode known protocol payloads"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
# portnum: int, now a key
|
# portnum: int, now a key
|
||||||
# If set, will be called to prase as a protocol buffer
|
# 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
|
# Usually btw this problem is caused by apps sending binary data but setting the payload type to
|
||||||
# text.
|
# text.
|
||||||
logging.debug(f'in _onTextReceive() asDict:{asDict}')
|
logging.debug(f"in _onTextReceive() asDict:{asDict}")
|
||||||
try:
|
try:
|
||||||
asBytes = asDict["decoded"]["payload"]
|
asBytes = asDict["decoded"]["payload"]
|
||||||
asDict["decoded"]["text"] = asBytes.decode("utf-8")
|
asDict["decoded"]["text"] = asBytes.decode("utf-8")
|
||||||
@@ -140,28 +151,28 @@ def _onTextReceive(iface, asDict):
|
|||||||
|
|
||||||
def _onPositionReceive(iface, asDict):
|
def _onPositionReceive(iface, asDict):
|
||||||
"""Special auto parsing for received messages"""
|
"""Special auto parsing for received messages"""
|
||||||
logging.debug(f'in _onPositionReceive() asDict:{asDict}')
|
logging.debug(f"in _onPositionReceive() asDict:{asDict}")
|
||||||
if 'decoded' in asDict:
|
if "decoded" in asDict:
|
||||||
if 'position' in asDict['decoded'] and 'from' in asDict:
|
if "position" in asDict["decoded"] and "from" in asDict:
|
||||||
p = asDict["decoded"]["position"]
|
p = asDict["decoded"]["position"]
|
||||||
logging.debug(f'p:{p}')
|
logging.debug(f"p:{p}")
|
||||||
p = iface._fixupPosition(p)
|
p = iface._fixupPosition(p)
|
||||||
logging.debug(f'after fixup p:{p}')
|
logging.debug(f"after fixup p:{p}")
|
||||||
# update node DB as needed
|
# update node DB as needed
|
||||||
iface._getOrCreateByNum(asDict["from"])["position"] = p
|
iface._getOrCreateByNum(asDict["from"])["position"] = p
|
||||||
|
|
||||||
|
|
||||||
def _onNodeInfoReceive(iface, asDict):
|
def _onNodeInfoReceive(iface, asDict):
|
||||||
"""Special auto parsing for received messages"""
|
"""Special auto parsing for received messages"""
|
||||||
logging.debug(f'in _onNodeInfoReceive() asDict:{asDict}')
|
logging.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
|
||||||
if 'decoded' in asDict:
|
if "decoded" in asDict:
|
||||||
if 'user' in asDict['decoded'] and 'from' in asDict:
|
if "user" in asDict["decoded"] and "from" in asDict:
|
||||||
p = asDict["decoded"]["user"]
|
p = asDict["decoded"]["user"]
|
||||||
# decode user protobufs and update nodedb, provide decoded version as "position" in the published msg
|
# decode user protobufs and update nodedb, provide decoded version as "position" in the published msg
|
||||||
# update node DB as needed
|
# update node DB as needed
|
||||||
n = iface._getOrCreateByNum(asDict["from"])
|
n = iface._getOrCreateByNum(asDict["from"])
|
||||||
n["user"] = p
|
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
|
iface.nodes[p["id"]] = n
|
||||||
_receiveInfoUpdate(iface, asDict)
|
_receiveInfoUpdate(iface, asDict)
|
||||||
|
|
||||||
@@ -176,12 +187,25 @@ def _receiveInfoUpdate(iface, asDict):
|
|||||||
|
|
||||||
"""Well known message payloads can register decoders for automatic protobuf parsing"""
|
"""Well known message payloads can register decoders for automatic protobuf parsing"""
|
||||||
protocols = {
|
protocols = {
|
||||||
portnums_pb2.PortNum.TEXT_MESSAGE_APP: KnownProtocol("text", onReceive=_onTextReceive),
|
portnums_pb2.PortNum.TEXT_MESSAGE_APP: KnownProtocol(
|
||||||
portnums_pb2.PortNum.POSITION_APP: KnownProtocol("position", mesh_pb2.Position, _onPositionReceive),
|
"text", onReceive=_onTextReceive
|
||||||
portnums_pb2.PortNum.NODEINFO_APP: KnownProtocol("user", mesh_pb2.User, _onNodeInfoReceive),
|
),
|
||||||
|
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.ADMIN_APP: KnownProtocol("admin", admin_pb2.AdminMessage),
|
||||||
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
|
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
|
||||||
portnums_pb2.PortNum.ENVIRONMENTAL_MEASUREMENT_APP: KnownProtocol("environmental", environmental_measurement_pb2.EnvironmentalMeasurement),
|
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol(
|
||||||
|
"telemetry", telemetry_pb2.Telemetry
|
||||||
|
),
|
||||||
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
|
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
|
||||||
"remotehw", remote_hardware_pb2.HardwareMessage)
|
"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,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: admin.proto
|
# source: meshtastic/admin.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -12,28 +12,56 @@ from google.protobuf import symbol_database as _symbol_database
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from . import cannedmessages_pb2 as cannedmessages__pb2
|
from meshtastic import channel_pb2 as meshtastic_dot_channel__pb2
|
||||||
from . import channel_pb2 as channel__pb2
|
from meshtastic import config_pb2 as meshtastic_dot_config__pb2
|
||||||
from . import mesh_pb2 as mesh__pb2
|
from meshtastic import connection_status_pb2 as meshtastic_dot_connection__status__pb2
|
||||||
from . import radioconfig_pb2 as radioconfig__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\x14\x63\x61nnedmessages.proto\x1a\rchannel.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\"\x87\x0c\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x08 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\t \x01(\x0b\x32\x05.UserH\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18! \x01(\x08H\x00\x12\x18\n\x0e\x65xit_simulator\x18\" \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18# \x01(\x05H\x00\x12\x31\n\'get_canned_message_plugin_part1_request\x18$ \x01(\x08H\x00\x12T\n(get_canned_message_plugin_part1_response\x18% \x01(\x0b\x32 .CannedMessagePluginMessagePart1H\x00\x12\x31\n\'get_canned_message_plugin_part2_request\x18& \x01(\x08H\x00\x12T\n(get_canned_message_plugin_part2_response\x18\' \x01(\x0b\x32 .CannedMessagePluginMessagePart2H\x00\x12\x31\n\'get_canned_message_plugin_part3_request\x18( \x01(\x08H\x00\x12T\n(get_canned_message_plugin_part3_response\x18) \x01(\x0b\x32 .CannedMessagePluginMessagePart3H\x00\x12\x31\n\'get_canned_message_plugin_part4_request\x18* \x01(\x08H\x00\x12T\n(get_canned_message_plugin_part4_response\x18+ \x01(\x0b\x32 .CannedMessagePluginMessagePart4H\x00\x12\x31\n\'get_canned_message_plugin_part5_request\x18, \x01(\x08H\x00\x12T\n(get_canned_message_plugin_part5_response\x18- \x01(\x0b\x32 .CannedMessagePluginMessagePart5H\x00\x12K\n\x1fset_canned_message_plugin_part1\x18. \x01(\x0b\x32 .CannedMessagePluginMessagePart1H\x00\x12K\n\x1fset_canned_message_plugin_part2\x18/ \x01(\x0b\x32 .CannedMessagePluginMessagePart2H\x00\x12K\n\x1fset_canned_message_plugin_part3\x18\x30 \x01(\x0b\x32 .CannedMessagePluginMessagePart3H\x00\x12K\n\x1fset_canned_message_plugin_part4\x18\x31 \x01(\x0b\x32 .CannedMessagePluginMessagePart4H\x00\x12K\n\x1fset_canned_message_plugin_part5\x18\x32 \x01(\x0b\x32 .CannedMessagePluginMessagePart5H\x00\x12\x1a\n\x10shutdown_seconds\x18\x33 \x01(\x05H\x00\x42\t\n\x07variantBG\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
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\"\xad\x0e\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\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\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\"\xa4\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\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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ADMINMESSAGE = DESCRIPTOR.message_types_by_name['AdminMessage']
|
_ADMINMESSAGE = DESCRIPTOR.message_types_by_name['AdminMessage']
|
||||||
|
_HAMPARAMETERS = DESCRIPTOR.message_types_by_name['HamParameters']
|
||||||
|
_NODEREMOTEHARDWAREPINSRESPONSE = DESCRIPTOR.message_types_by_name['NodeRemoteHardwarePinsResponse']
|
||||||
|
_ADMINMESSAGE_CONFIGTYPE = _ADMINMESSAGE.enum_types_by_name['ConfigType']
|
||||||
|
_ADMINMESSAGE_MODULECONFIGTYPE = _ADMINMESSAGE.enum_types_by_name['ModuleConfigType']
|
||||||
AdminMessage = _reflection.GeneratedProtocolMessageType('AdminMessage', (_message.Message,), {
|
AdminMessage = _reflection.GeneratedProtocolMessageType('AdminMessage', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _ADMINMESSAGE,
|
'DESCRIPTOR' : _ADMINMESSAGE,
|
||||||
'__module__' : 'admin_pb2'
|
'__module__' : 'meshtastic.admin_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:AdminMessage)
|
# @@protoc_insertion_point(class_scope:AdminMessage)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(AdminMessage)
|
_sym_db.RegisterMessage(AdminMessage)
|
||||||
|
|
||||||
|
HamParameters = _reflection.GeneratedProtocolMessageType('HamParameters', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _HAMPARAMETERS,
|
||||||
|
'__module__' : 'meshtastic.admin_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:HamParameters)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(HamParameters)
|
||||||
|
|
||||||
|
NodeRemoteHardwarePinsResponse = _reflection.GeneratedProtocolMessageType('NodeRemoteHardwarePinsResponse', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _NODEREMOTEHARDWAREPINSRESPONSE,
|
||||||
|
'__module__' : 'meshtastic.admin_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:NodeRemoteHardwarePinsResponse)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(NodeRemoteHardwarePinsResponse)
|
||||||
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_ADMINMESSAGE._serialized_start=84
|
_ADMINMESSAGE._serialized_start=198
|
||||||
_ADMINMESSAGE._serialized_end=1627
|
_ADMINMESSAGE._serialized_end=2035
|
||||||
|
_ADMINMESSAGE_CONFIGTYPE._serialized_start=1572
|
||||||
|
_ADMINMESSAGE_CONFIGTYPE._serialized_end=1721
|
||||||
|
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_start=1724
|
||||||
|
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_end=2016
|
||||||
|
_HAMPARAMETERS._serialized_start=2037
|
||||||
|
_HAMPARAMETERS._serialized_end=2128
|
||||||
|
_NODEREMOTEHARDWAREPINSRESPONSE._serialized_start=2130
|
||||||
|
_NODEREMOTEHARDWAREPINSRESPONSE._serialized_end=2221
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: apponly.proto
|
# source: meshtastic/apponly.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -12,17 +12,18 @@ from google.protobuf import symbol_database as _symbol_database
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from . import channel_pb2 as channel__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\"0\n\nChannelSet\x12\"\n\x08settings\x18\x01 \x03(\x0b\x32\x10.ChannelSettingsBI\n\x13\x63om.geeksville.meshB\rAppOnlyProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_CHANNELSET = DESCRIPTOR.message_types_by_name['ChannelSet']
|
_CHANNELSET = DESCRIPTOR.message_types_by_name['ChannelSet']
|
||||||
ChannelSet = _reflection.GeneratedProtocolMessageType('ChannelSet', (_message.Message,), {
|
ChannelSet = _reflection.GeneratedProtocolMessageType('ChannelSet', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _CHANNELSET,
|
'DESCRIPTOR' : _CHANNELSET,
|
||||||
'__module__' : 'apponly_pb2'
|
'__module__' : 'meshtastic.apponly_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:ChannelSet)
|
# @@protoc_insertion_point(class_scope:ChannelSet)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(ChannelSet)
|
_sym_db.RegisterMessage(ChannelSet)
|
||||||
@@ -30,7 +31,7 @@ _sym_db.RegisterMessage(ChannelSet)
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_CHANNELSET._serialized_start=32
|
_CHANNELSET._serialized_start=79
|
||||||
_CHANNELSET._serialized_end=80
|
_CHANNELSET._serialized_end=168
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import platform
|
|||||||
from meshtastic.mesh_interface import MeshInterface
|
from meshtastic.mesh_interface import MeshInterface
|
||||||
from meshtastic.util import our_exit
|
from meshtastic.util import our_exit
|
||||||
|
|
||||||
if platform.system() == 'Linux':
|
if platform.system() == "Linux":
|
||||||
# pylint: disable=E0401
|
# pylint: disable=E0401
|
||||||
import pygatt
|
import pygatt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Our standard BLE characteristics
|
# Our standard BLE characteristics
|
||||||
TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
||||||
FROMRADIO_UUID = "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
|
FROMRADIO_UUID = "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
|
||||||
@@ -22,7 +21,7 @@ class BLEInterface(MeshInterface):
|
|||||||
"""A not quite ready - FIXME - BLE interface to devices"""
|
"""A not quite ready - FIXME - BLE interface to devices"""
|
||||||
|
|
||||||
def __init__(self, address, noProto=False, debugOut=None):
|
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)
|
our_exit("Linux is the only platform with experimental BLE support.", 1)
|
||||||
self.address = address
|
self.address = address
|
||||||
if not noProto:
|
if not noProto:
|
||||||
@@ -39,7 +38,7 @@ class BLEInterface(MeshInterface):
|
|||||||
|
|
||||||
self._readFromRadio() # read the initial responses
|
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)
|
self._handleFromRadio(data)
|
||||||
|
|
||||||
if self.device:
|
if self.device:
|
||||||
@@ -47,7 +46,7 @@ class BLEInterface(MeshInterface):
|
|||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio):
|
def _sendToRadioImpl(self, toRadio):
|
||||||
"""Send a ToRadio protobuf to the device"""
|
"""Send a ToRadio protobuf to the device"""
|
||||||
#logging.debug(f"Sending: {stripnl(toRadio)}")
|
# logging.debug(f"Sending: {stripnl(toRadio)}")
|
||||||
b = toRadio.SerializeToString()
|
b = toRadio.SerializeToString()
|
||||||
self.device.char_write(TORADIO_UUID, b)
|
self.device.char_write(TORADIO_UUID, b)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: cannedmessages.proto
|
# source: meshtastic/cannedmessages.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -14,62 +14,22 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x63\x61nnedmessages.proto\"/\n\x1f\x43\x61nnedMessagePluginMessagePart1\x12\x0c\n\x04text\x18\x01 \x01(\t\"/\n\x1f\x43\x61nnedMessagePluginMessagePart2\x12\x0c\n\x04text\x18\x01 \x01(\t\"/\n\x1f\x43\x61nnedMessagePluginMessagePart3\x12\x0c\n\x04text\x18\x01 \x01(\t\"/\n\x1f\x43\x61nnedMessagePluginMessagePart4\x12\x0c\n\x04text\x18\x01 \x01(\t\"/\n\x1f\x43\x61nnedMessagePluginMessagePart5\x12\x0c\n\x04text\x18\x01 \x01(\tBU\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART1 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart1']
|
_CANNEDMESSAGEMODULECONFIG = DESCRIPTOR.message_types_by_name['CannedMessageModuleConfig']
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART2 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart2']
|
CannedMessageModuleConfig = _reflection.GeneratedProtocolMessageType('CannedMessageModuleConfig', (_message.Message,), {
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART3 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart3']
|
'DESCRIPTOR' : _CANNEDMESSAGEMODULECONFIG,
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART4 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart4']
|
'__module__' : 'meshtastic.cannedmessages_pb2'
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART5 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart5']
|
# @@protoc_insertion_point(class_scope:CannedMessageModuleConfig)
|
||||||
CannedMessagePluginMessagePart1 = _reflection.GeneratedProtocolMessageType('CannedMessagePluginMessagePart1', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _CANNEDMESSAGEPLUGINMESSAGEPART1,
|
|
||||||
'__module__' : 'cannedmessages_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:CannedMessagePluginMessagePart1)
|
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(CannedMessagePluginMessagePart1)
|
_sym_db.RegisterMessage(CannedMessageModuleConfig)
|
||||||
|
|
||||||
CannedMessagePluginMessagePart2 = _reflection.GeneratedProtocolMessageType('CannedMessagePluginMessagePart2', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _CANNEDMESSAGEPLUGINMESSAGEPART2,
|
|
||||||
'__module__' : 'cannedmessages_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:CannedMessagePluginMessagePart2)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(CannedMessagePluginMessagePart2)
|
|
||||||
|
|
||||||
CannedMessagePluginMessagePart3 = _reflection.GeneratedProtocolMessageType('CannedMessagePluginMessagePart3', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _CANNEDMESSAGEPLUGINMESSAGEPART3,
|
|
||||||
'__module__' : 'cannedmessages_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:CannedMessagePluginMessagePart3)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(CannedMessagePluginMessagePart3)
|
|
||||||
|
|
||||||
CannedMessagePluginMessagePart4 = _reflection.GeneratedProtocolMessageType('CannedMessagePluginMessagePart4', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _CANNEDMESSAGEPLUGINMESSAGEPART4,
|
|
||||||
'__module__' : 'cannedmessages_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:CannedMessagePluginMessagePart4)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(CannedMessagePluginMessagePart4)
|
|
||||||
|
|
||||||
CannedMessagePluginMessagePart5 = _reflection.GeneratedProtocolMessageType('CannedMessagePluginMessagePart5', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _CANNEDMESSAGEPLUGINMESSAGEPART5,
|
|
||||||
'__module__' : 'cannedmessages_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:CannedMessagePluginMessagePart5)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(CannedMessagePluginMessagePart5)
|
|
||||||
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART1._serialized_start=24
|
_CANNEDMESSAGEMODULECONFIG._serialized_start=35
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART1._serialized_end=71
|
_CANNEDMESSAGEMODULECONFIG._serialized_end=80
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART2._serialized_start=73
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART2._serialized_end=120
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART3._serialized_start=122
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART3._serialized_end=169
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART4._serialized_start=171
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART4._serialized_end=218
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART5._serialized_start=220
|
|
||||||
_CANNEDMESSAGEPLUGINMESSAGEPART5._serialized_end=267
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: channel.proto
|
# source: meshtastic/channel.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -14,24 +14,23 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rchannel.proto\"\x91\x03\n\x0f\x43hannelSettings\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x32\n\x0cmodem_config\x18\x03 \x01(\x0e\x32\x1c.ChannelSettings.ModemConfig\x12\x11\n\tbandwidth\x18\x06 \x01(\r\x12\x15\n\rspread_factor\x18\x07 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x08 \x01(\r\x12\x13\n\x0b\x63hannel_num\x18\t \x01(\r\x12\x0b\n\x03psk\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\n\n\x02id\x18\n \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x10 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x11 \x01(\x08\"\x8a\x01\n\x0bModemConfig\x12\x12\n\x0e\x42w125Cr45Sf128\x10\x00\x12\x12\n\x0e\x42w500Cr45Sf128\x10\x01\x12\x14\n\x10\x42w31_25Cr48Sf512\x10\x02\x12\x13\n\x0f\x42w125Cr48Sf4096\x10\x03\x12\x13\n\x0f\x42w250Cr46Sf2048\x10\x04\x12\x13\n\x0f\x42w250Cr47Sf1024\x10\x05\"\x8b\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\"\n\x08settings\x18\x02 \x01(\x0b\x32\x10.ChannelSettings\x12\x1b\n\x04role\x18\x03 \x01(\x0e\x32\r.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42I\n\x13\x63om.geeksville.meshB\rChannelProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_CHANNELSETTINGS = DESCRIPTOR.message_types_by_name['ChannelSettings']
|
_CHANNELSETTINGS = DESCRIPTOR.message_types_by_name['ChannelSettings']
|
||||||
_CHANNEL = DESCRIPTOR.message_types_by_name['Channel']
|
_CHANNEL = DESCRIPTOR.message_types_by_name['Channel']
|
||||||
_CHANNELSETTINGS_MODEMCONFIG = _CHANNELSETTINGS.enum_types_by_name['ModemConfig']
|
|
||||||
_CHANNEL_ROLE = _CHANNEL.enum_types_by_name['Role']
|
_CHANNEL_ROLE = _CHANNEL.enum_types_by_name['Role']
|
||||||
ChannelSettings = _reflection.GeneratedProtocolMessageType('ChannelSettings', (_message.Message,), {
|
ChannelSettings = _reflection.GeneratedProtocolMessageType('ChannelSettings', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _CHANNELSETTINGS,
|
'DESCRIPTOR' : _CHANNELSETTINGS,
|
||||||
'__module__' : 'channel_pb2'
|
'__module__' : 'meshtastic.channel_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:ChannelSettings)
|
# @@protoc_insertion_point(class_scope:ChannelSettings)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(ChannelSettings)
|
_sym_db.RegisterMessage(ChannelSettings)
|
||||||
|
|
||||||
Channel = _reflection.GeneratedProtocolMessageType('Channel', (_message.Message,), {
|
Channel = _reflection.GeneratedProtocolMessageType('Channel', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _CHANNEL,
|
'DESCRIPTOR' : _CHANNEL,
|
||||||
'__module__' : 'channel_pb2'
|
'__module__' : 'meshtastic.channel_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:Channel)
|
# @@protoc_insertion_point(class_scope:Channel)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(Channel)
|
_sym_db.RegisterMessage(Channel)
|
||||||
@@ -39,13 +38,13 @@ _sym_db.RegisterMessage(Channel)
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
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._serialized_start=18
|
_CHANNELSETTINGS.fields_by_name['channel_num']._options = None
|
||||||
_CHANNELSETTINGS._serialized_end=419
|
_CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001'
|
||||||
_CHANNELSETTINGS_MODEMCONFIG._serialized_start=281
|
_CHANNELSETTINGS._serialized_start=29
|
||||||
_CHANNELSETTINGS_MODEMCONFIG._serialized_end=419
|
_CHANNELSETTINGS._serialized_end=160
|
||||||
_CHANNEL._serialized_start=422
|
_CHANNEL._serialized_start=163
|
||||||
_CHANNEL._serialized_end=561
|
_CHANNEL._serialized_end=302
|
||||||
_CHANNEL_ROLE._serialized_start=513
|
_CHANNEL_ROLE._serialized_start=254
|
||||||
_CHANNEL_ROLE._serialized_end=561
|
_CHANNEL_ROLE._serialized_end=302
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
36
meshtastic/clientonly_pb2.py
Normal file
36
meshtastic/clientonly_pb2.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: meshtastic/clientonly.proto
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
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 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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_DEVICEPROFILE = DESCRIPTOR.message_types_by_name['DeviceProfile']
|
||||||
|
DeviceProfile = _reflection.GeneratedProtocolMessageType('DeviceProfile', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _DEVICEPROFILE,
|
||||||
|
'__module__' : 'meshtastic.clientonly_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:DeviceProfile)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(DeviceProfile)
|
||||||
|
|
||||||
|
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)
|
||||||
156
meshtastic/config_pb2.py
Normal file
156
meshtastic/config_pb2.py
Normal file
File diff suppressed because one or more lines are too long
85
meshtastic/connection_status_pb2.py
Normal file
85
meshtastic/connection_status_pb2.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: meshtastic/connection_status.proto
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_DEVICECONNECTIONSTATUS = DESCRIPTOR.message_types_by_name['DeviceConnectionStatus']
|
||||||
|
_WIFICONNECTIONSTATUS = DESCRIPTOR.message_types_by_name['WifiConnectionStatus']
|
||||||
|
_ETHERNETCONNECTIONSTATUS = DESCRIPTOR.message_types_by_name['EthernetConnectionStatus']
|
||||||
|
_NETWORKCONNECTIONSTATUS = DESCRIPTOR.message_types_by_name['NetworkConnectionStatus']
|
||||||
|
_BLUETOOTHCONNECTIONSTATUS = DESCRIPTOR.message_types_by_name['BluetoothConnectionStatus']
|
||||||
|
_SERIALCONNECTIONSTATUS = DESCRIPTOR.message_types_by_name['SerialConnectionStatus']
|
||||||
|
DeviceConnectionStatus = _reflection.GeneratedProtocolMessageType('DeviceConnectionStatus', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _DEVICECONNECTIONSTATUS,
|
||||||
|
'__module__' : 'meshtastic.connection_status_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:DeviceConnectionStatus)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(DeviceConnectionStatus)
|
||||||
|
|
||||||
|
WifiConnectionStatus = _reflection.GeneratedProtocolMessageType('WifiConnectionStatus', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _WIFICONNECTIONSTATUS,
|
||||||
|
'__module__' : 'meshtastic.connection_status_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:WifiConnectionStatus)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(WifiConnectionStatus)
|
||||||
|
|
||||||
|
EthernetConnectionStatus = _reflection.GeneratedProtocolMessageType('EthernetConnectionStatus', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _ETHERNETCONNECTIONSTATUS,
|
||||||
|
'__module__' : 'meshtastic.connection_status_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:EthernetConnectionStatus)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(EthernetConnectionStatus)
|
||||||
|
|
||||||
|
NetworkConnectionStatus = _reflection.GeneratedProtocolMessageType('NetworkConnectionStatus', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _NETWORKCONNECTIONSTATUS,
|
||||||
|
'__module__' : 'meshtastic.connection_status_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:NetworkConnectionStatus)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(NetworkConnectionStatus)
|
||||||
|
|
||||||
|
BluetoothConnectionStatus = _reflection.GeneratedProtocolMessageType('BluetoothConnectionStatus', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _BLUETOOTHCONNECTIONSTATUS,
|
||||||
|
'__module__' : 'meshtastic.connection_status_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:BluetoothConnectionStatus)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(BluetoothConnectionStatus)
|
||||||
|
|
||||||
|
SerialConnectionStatus = _reflection.GeneratedProtocolMessageType('SerialConnectionStatus', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _SERIALCONNECTIONSTATUS,
|
||||||
|
'__module__' : 'meshtastic.connection_status_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:SerialConnectionStatus)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(SerialConnectionStatus)
|
||||||
|
|
||||||
|
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)
|
||||||
37
meshtastic/device_metadata_pb2.py
Normal file
37
meshtastic/device_metadata_pb2.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# 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
|
||||||
|
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 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 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__' : 'meshtastic.device_metadata_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:DeviceMetadata)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(DeviceMetadata)
|
||||||
|
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
|
DESCRIPTOR._options = None
|
||||||
|
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,7 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: deviceonly.proto
|
# source: meshtastic/deviceonly.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
|
from google.protobuf.internal import enum_type_wrapper
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
from google.protobuf import message as _message
|
from google.protobuf import message as _message
|
||||||
@@ -12,58 +13,86 @@ from google.protobuf import symbol_database as _symbol_database
|
|||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
from . import channel_pb2 as channel__pb2
|
from meshtastic import channel_pb2 as meshtastic_dot_channel__pb2
|
||||||
from . import mesh_pb2 as mesh__pb2
|
from meshtastic import localonly_pb2 as meshtastic_dot_localonly__pb2
|
||||||
from . import radioconfig_pb2 as radioconfig__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\x1a\x11radioconfig.proto\"\x80\x01\n\x11LegacyRadioConfig\x12\x39\n\x0bpreferences\x18\x01 \x01(\x0b\x32$.LegacyRadioConfig.LegacyPreferences\x1a\x30\n\x11LegacyPreferences\x12\x1b\n\x06region\x18\x0f \x01(\x0e\x32\x0b.RegionCode\"\xf0\x03\n\x0b\x44\x65viceState\x12\'\n\x0blegacyRadio\x18\x01 \x01(\x0b\x32\x12.LegacyRadioConfig\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\x12+\n#canned_message_plugin_message_part1\x18\r \x01(\t\x12+\n#canned_message_plugin_message_part2\x18\x0e \x01(\t\x12+\n#canned_message_plugin_message_part3\x18\x0f \x01(\t\x12+\n#canned_message_plugin_message_part4\x18\x10 \x01(\t\x12+\n#canned_message_plugin_message_part5\x18\x11 \x01(\tJ\x04\x08\x0c\x10\r\")\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.ChannelBF\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
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')
|
||||||
|
|
||||||
|
_SCREENFONTS = DESCRIPTOR.enum_types_by_name['ScreenFonts']
|
||||||
|
ScreenFonts = enum_type_wrapper.EnumTypeWrapper(_SCREENFONTS)
|
||||||
|
FONT_SMALL = 0
|
||||||
|
FONT_MEDIUM = 1
|
||||||
|
FONT_LARGE = 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_LEGACYRADIOCONFIG = DESCRIPTOR.message_types_by_name['LegacyRadioConfig']
|
|
||||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES = _LEGACYRADIOCONFIG.nested_types_by_name['LegacyPreferences']
|
|
||||||
_DEVICESTATE = DESCRIPTOR.message_types_by_name['DeviceState']
|
_DEVICESTATE = DESCRIPTOR.message_types_by_name['DeviceState']
|
||||||
|
_NODEINFOLITE = DESCRIPTOR.message_types_by_name['NodeInfoLite']
|
||||||
|
_POSITIONLITE = DESCRIPTOR.message_types_by_name['PositionLite']
|
||||||
_CHANNELFILE = DESCRIPTOR.message_types_by_name['ChannelFile']
|
_CHANNELFILE = DESCRIPTOR.message_types_by_name['ChannelFile']
|
||||||
LegacyRadioConfig = _reflection.GeneratedProtocolMessageType('LegacyRadioConfig', (_message.Message,), {
|
_OEMSTORE = DESCRIPTOR.message_types_by_name['OEMStore']
|
||||||
|
_NODEREMOTEHARDWAREPIN = DESCRIPTOR.message_types_by_name['NodeRemoteHardwarePin']
|
||||||
'LegacyPreferences' : _reflection.GeneratedProtocolMessageType('LegacyPreferences', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _LEGACYRADIOCONFIG_LEGACYPREFERENCES,
|
|
||||||
'__module__' : 'deviceonly_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:LegacyRadioConfig.LegacyPreferences)
|
|
||||||
})
|
|
||||||
,
|
|
||||||
'DESCRIPTOR' : _LEGACYRADIOCONFIG,
|
|
||||||
'__module__' : 'deviceonly_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:LegacyRadioConfig)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(LegacyRadioConfig)
|
|
||||||
_sym_db.RegisterMessage(LegacyRadioConfig.LegacyPreferences)
|
|
||||||
|
|
||||||
DeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message.Message,), {
|
DeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _DEVICESTATE,
|
'DESCRIPTOR' : _DEVICESTATE,
|
||||||
'__module__' : 'deviceonly_pb2'
|
'__module__' : 'meshtastic.deviceonly_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:DeviceState)
|
# @@protoc_insertion_point(class_scope:DeviceState)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(DeviceState)
|
_sym_db.RegisterMessage(DeviceState)
|
||||||
|
|
||||||
|
NodeInfoLite = _reflection.GeneratedProtocolMessageType('NodeInfoLite', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _NODEINFOLITE,
|
||||||
|
'__module__' : 'meshtastic.deviceonly_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:NodeInfoLite)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(NodeInfoLite)
|
||||||
|
|
||||||
|
PositionLite = _reflection.GeneratedProtocolMessageType('PositionLite', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _POSITIONLITE,
|
||||||
|
'__module__' : 'meshtastic.deviceonly_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:PositionLite)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(PositionLite)
|
||||||
|
|
||||||
ChannelFile = _reflection.GeneratedProtocolMessageType('ChannelFile', (_message.Message,), {
|
ChannelFile = _reflection.GeneratedProtocolMessageType('ChannelFile', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _CHANNELFILE,
|
'DESCRIPTOR' : _CHANNELFILE,
|
||||||
'__module__' : 'deviceonly_pb2'
|
'__module__' : 'meshtastic.deviceonly_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:ChannelFile)
|
# @@protoc_insertion_point(class_scope:ChannelFile)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(ChannelFile)
|
_sym_db.RegisterMessage(ChannelFile)
|
||||||
|
|
||||||
|
OEMStore = _reflection.GeneratedProtocolMessageType('OEMStore', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _OEMSTORE,
|
||||||
|
'__module__' : 'meshtastic.deviceonly_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:OEMStore)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(OEMStore)
|
||||||
|
|
||||||
|
NodeRemoteHardwarePin = _reflection.GeneratedProtocolMessageType('NodeRemoteHardwarePin', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _NODEREMOTEHARDWAREPIN,
|
||||||
|
'__module__' : 'meshtastic.deviceonly_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:NodeRemoteHardwarePin)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(NodeRemoteHardwarePin)
|
||||||
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_LEGACYRADIOCONFIG._serialized_start=67
|
_SCREENFONTS._serialized_start=1192
|
||||||
_LEGACYRADIOCONFIG._serialized_end=195
|
_SCREENFONTS._serialized_end=1254
|
||||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES._serialized_start=147
|
_DEVICESTATE._serialized_start=169
|
||||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES._serialized_end=195
|
_DEVICESTATE._serialized_end=495
|
||||||
_DEVICESTATE._serialized_start=198
|
_NODEINFOLITE._serialized_start=498
|
||||||
_DEVICESTATE._serialized_end=694
|
_NODEINFOLITE._serialized_end=669
|
||||||
_CHANNELFILE._serialized_start=696
|
_POSITIONLITE._serialized_start=672
|
||||||
_CHANNELFILE._serialized_end=737
|
_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)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: environmental_measurement.proto
|
|
||||||
"""Generated protocol buffer code."""
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x65nvironmental_measurement.proto\"\xa1\x01\n\x18\x45nvironmentalMeasurement\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\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\x42#Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ENVIRONMENTALMEASUREMENT = DESCRIPTOR.message_types_by_name['EnvironmentalMeasurement']
|
|
||||||
EnvironmentalMeasurement = _reflection.GeneratedProtocolMessageType('EnvironmentalMeasurement', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _ENVIRONMENTALMEASUREMENT,
|
|
||||||
'__module__' : 'environmental_measurement_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:EnvironmentalMeasurement)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(EnvironmentalMeasurement)
|
|
||||||
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
DESCRIPTOR._serialized_options = b'Z!github.com/meshtastic/gomeshproto'
|
|
||||||
_ENVIRONMENTALMEASUREMENT._serialized_start=36
|
|
||||||
_ENVIRONMENTALMEASUREMENT._serialized_end=197
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
|
||||||
@@ -8,8 +8,10 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Globals:
|
class Globals:
|
||||||
"""Globals class is a Singleton."""
|
"""Globals class is a Singleton."""
|
||||||
|
|
||||||
__instance = None
|
__instance = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
47
meshtastic/localonly_pb2.py
Normal file
47
meshtastic/localonly_pb2.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: meshtastic/localonly.proto
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
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 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\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\"\xb8\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\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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_LOCALCONFIG = DESCRIPTOR.message_types_by_name['LocalConfig']
|
||||||
|
_LOCALMODULECONFIG = DESCRIPTOR.message_types_by_name['LocalModuleConfig']
|
||||||
|
LocalConfig = _reflection.GeneratedProtocolMessageType('LocalConfig', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _LOCALCONFIG,
|
||||||
|
'__module__' : 'meshtastic.localonly_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:LocalConfig)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(LocalConfig)
|
||||||
|
|
||||||
|
LocalModuleConfig = _reflection.GeneratedProtocolMessageType('LocalModuleConfig', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _LOCALMODULECONFIG,
|
||||||
|
'__module__' : 'meshtastic.localonly_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:LocalModuleConfig)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(LocalModuleConfig)
|
||||||
|
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
|
DESCRIPTOR._options = None
|
||||||
|
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=1091
|
||||||
|
# @@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
197
meshtastic/module_config_pb2.py
Normal file
197
meshtastic/module_config_pb2.py
Normal file
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: mqtt.proto
|
# source: meshtastic/mqtt.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -12,17 +12,17 @@ from google.protobuf import symbol_database as _symbol_database
|
|||||||
_sym_db = _symbol_database.Default()
|
_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')
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_SERVICEENVELOPE = DESCRIPTOR.message_types_by_name['ServiceEnvelope']
|
_SERVICEENVELOPE = DESCRIPTOR.message_types_by_name['ServiceEnvelope']
|
||||||
ServiceEnvelope = _reflection.GeneratedProtocolMessageType('ServiceEnvelope', (_message.Message,), {
|
ServiceEnvelope = _reflection.GeneratedProtocolMessageType('ServiceEnvelope', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _SERVICEENVELOPE,
|
'DESCRIPTOR' : _SERVICEENVELOPE,
|
||||||
'__module__' : 'mqtt_pb2'
|
'__module__' : 'meshtastic.mqtt_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:ServiceEnvelope)
|
# @@protoc_insertion_point(class_scope:ServiceEnvelope)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(ServiceEnvelope)
|
_sym_db.RegisterMessage(ServiceEnvelope)
|
||||||
@@ -30,7 +30,7 @@ _sym_db.RegisterMessage(ServiceEnvelope)
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_SERVICEENVELOPE._serialized_start=26
|
_SERVICEENVELOPE._serialized_start=48
|
||||||
_SERVICEENVELOPE._serialized_end=112
|
_SERVICEENVELOPE._serialized_end=134
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -1,40 +1,59 @@
|
|||||||
"""Node class
|
"""Node class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
import base64
|
import base64
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
from google.protobuf.json_format import MessageToJson
|
from google.protobuf.json_format import MessageToJson
|
||||||
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_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:
|
class Node:
|
||||||
"""A model of a (local or remote) node in the mesh
|
"""A model of a (local or remote) node in the mesh
|
||||||
|
|
||||||
Includes methods for radioConfig and channels
|
Includes methods for localConfig, moduleConfig and channels
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, iface, nodeNum, noProto=False):
|
def __init__(self, iface, nodeNum, noProto=False):
|
||||||
"""Constructor"""
|
"""Constructor"""
|
||||||
self.iface = iface
|
self.iface = iface
|
||||||
self.nodeNum = nodeNum
|
self.nodeNum = nodeNum
|
||||||
self.radioConfig = None
|
self.localConfig = localonly_pb2.LocalConfig()
|
||||||
|
self.moduleConfig = localonly_pb2.LocalModuleConfig()
|
||||||
self.channels = None
|
self.channels = None
|
||||||
self._timeout = Timeout(maxSecs=300)
|
self._timeout = Timeout(maxSecs=300)
|
||||||
self.partialChannels = None
|
self.partialChannels = None
|
||||||
self.noProto = noProto
|
self.noProto = noProto
|
||||||
|
self.cannedPluginMessage = None
|
||||||
|
self.cannedPluginMessageMessages = None
|
||||||
|
self.ringtone = None
|
||||||
|
self.ringtonePart = None
|
||||||
|
|
||||||
|
self.gotResponse = None
|
||||||
|
|
||||||
def showChannels(self):
|
def showChannels(self):
|
||||||
"""Show human readable description of our channels."""
|
"""Show human readable description of our channels."""
|
||||||
print("Channels:")
|
print("Channels:")
|
||||||
if self.channels:
|
if self.channels:
|
||||||
logging.debug(f'self.channels:{self.channels}')
|
logging.debug(f"self.channels:{self.channels}")
|
||||||
for c in 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))
|
cStr = stripnl(MessageToJson(c.settings))
|
||||||
# only show if there is no psk (meaning disabled channel)
|
# don't show disabled channels
|
||||||
if c.settings.psk:
|
if channel_pb2.Channel.Role.Name(c.role) != "DISABLED":
|
||||||
print(f" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}")
|
print(
|
||||||
|
f" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}"
|
||||||
|
)
|
||||||
publicURL = self.getURL(includeAll=False)
|
publicURL = self.getURL(includeAll=False)
|
||||||
adminURL = self.getURL(includeAll=True)
|
adminURL = self.getURL(includeAll=True)
|
||||||
print(f"\nPrimary channel URL: {publicURL}")
|
print(f"\nPrimary channel URL: {publicURL}")
|
||||||
@@ -44,19 +63,75 @@ class Node:
|
|||||||
def showInfo(self):
|
def showInfo(self):
|
||||||
"""Show human readable description of our node"""
|
"""Show human readable description of our node"""
|
||||||
prefs = ""
|
prefs = ""
|
||||||
if self.radioConfig and self.radioConfig.preferences:
|
if self.localConfig:
|
||||||
prefs = stripnl(MessageToJson(self.radioConfig.preferences))
|
prefs = stripnl(MessageToJson(self.localConfig))
|
||||||
print(f"Preferences: {prefs}\n")
|
print(f"Preferences: {prefs}\n")
|
||||||
|
prefs = ""
|
||||||
|
if self.moduleConfig:
|
||||||
|
prefs = stripnl(MessageToJson(self.moduleConfig))
|
||||||
|
print(f"Module preferences: {prefs}\n")
|
||||||
self.showChannels()
|
self.showChannels()
|
||||||
|
|
||||||
def requestConfig(self):
|
def requestChannels(self):
|
||||||
"""Send regular MeshPackets to ask for settings and channels."""
|
"""Send regular MeshPackets to ask channels."""
|
||||||
logging.debug(f"requestConfig for nodeNum:{self.nodeNum}")
|
logging.debug(f"requestChannels for nodeNum:{self.nodeNum}")
|
||||||
self.radioConfig = None
|
|
||||||
self.channels = None
|
self.channels = None
|
||||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||||
|
|
||||||
self._requestSettings()
|
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):
|
def turnOffEncryptionOnPrimaryChannel(self):
|
||||||
"""Turn off encryption on primary channel."""
|
"""Turn off encryption on primary channel."""
|
||||||
@@ -64,34 +139,81 @@ class Node:
|
|||||||
print("Writing modified channels to device")
|
print("Writing modified channels to device")
|
||||||
self.writeChannel(0)
|
self.writeChannel(0)
|
||||||
|
|
||||||
def waitForConfig(self):
|
def waitForConfig(self, attribute="channels"):
|
||||||
"""Block until radio config is received. Returns True if config has been received."""
|
"""Block until radio config is received. Returns True if config has been received."""
|
||||||
return self._timeout.waitForSet(self, attrs=('radioConfig', 'channels'))
|
return self._timeout.waitForSet(self, attrs=("localConfig", attribute))
|
||||||
|
|
||||||
def writeConfig(self):
|
def writeConfig(self, config_name):
|
||||||
"""Write the current (edited) radioConfig to the device"""
|
"""Write the current (edited) localConfig to the device"""
|
||||||
if self.radioConfig is None:
|
if self.localConfig is None:
|
||||||
our_exit("Error: No RadioConfig has been read")
|
our_exit("Error: No localConfig has been read")
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.set_radio.CopyFrom(self.radioConfig)
|
|
||||||
|
|
||||||
self._sendAdmin(p)
|
if config_name == "device":
|
||||||
logging.debug("Wrote config")
|
p.set_config.device.CopyFrom(self.localConfig.device)
|
||||||
|
elif config_name == "position":
|
||||||
|
p.set_config.position.CopyFrom(self.localConfig.position)
|
||||||
|
elif config_name == "power":
|
||||||
|
p.set_config.power.CopyFrom(self.localConfig.power)
|
||||||
|
elif config_name == "network":
|
||||||
|
p.set_config.network.CopyFrom(self.localConfig.network)
|
||||||
|
elif config_name == "display":
|
||||||
|
p.set_config.display.CopyFrom(self.localConfig.display)
|
||||||
|
elif config_name == "lora":
|
||||||
|
p.set_config.lora.CopyFrom(self.localConfig.lora)
|
||||||
|
elif config_name == "bluetooth":
|
||||||
|
p.set_config.bluetooth.CopyFrom(self.localConfig.bluetooth)
|
||||||
|
elif config_name == "mqtt":
|
||||||
|
p.set_module_config.mqtt.CopyFrom(self.moduleConfig.mqtt)
|
||||||
|
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":
|
||||||
|
p.set_module_config.store_forward.CopyFrom(self.moduleConfig.store_forward)
|
||||||
|
elif config_name == "range_test":
|
||||||
|
p.set_module_config.range_test.CopyFrom(self.moduleConfig.range_test)
|
||||||
|
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 == "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)
|
||||||
|
else:
|
||||||
|
our_exit(f"Error: No valid config with name {config_name}")
|
||||||
|
|
||||||
|
logging.debug(f"Wrote: {config_name}")
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
def writeChannel(self, channelIndex, adminIndex=0):
|
def writeChannel(self, channelIndex, adminIndex=0):
|
||||||
"""Write the current (edited) channel to the device"""
|
"""Write the current (edited) channel to the device"""
|
||||||
|
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.set_channel.CopyFrom(self.channels[channelIndex])
|
p.set_channel.CopyFrom(self.channels[channelIndex])
|
||||||
|
|
||||||
self._sendAdmin(p, adminIndex=adminIndex)
|
self._sendAdmin(p, adminIndex=adminIndex)
|
||||||
logging.debug(f"Wrote channel {channelIndex}")
|
logging.debug(f"Wrote channel {channelIndex}")
|
||||||
|
|
||||||
def getChannelByChannelIndex(self, channelIndex):
|
def getChannelByChannelIndex(self, channelIndex):
|
||||||
"""Get channel by channelIndex
|
"""Get channel by channelIndex
|
||||||
channelIndex: number, typically 0-7; based on max number channels
|
channelIndex: number, typically 0-7; based on max number channels
|
||||||
returns: None if there is no channel found
|
returns: None if there is no channel found
|
||||||
"""
|
"""
|
||||||
ch = None
|
ch = None
|
||||||
if self.channels and 0 <= channelIndex < len(self.channels):
|
if self.channels and 0 <= channelIndex < len(self.channels):
|
||||||
@@ -99,9 +221,12 @@ class Node:
|
|||||||
return ch
|
return ch
|
||||||
|
|
||||||
def deleteChannel(self, channelIndex):
|
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]
|
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")
|
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
|
# we are careful here because if we move the "admin" channel the channelIndex we need to use
|
||||||
@@ -112,7 +237,7 @@ class Node:
|
|||||||
self._fixupChannels() # expand back to 8 channels
|
self._fixupChannels() # expand back to 8 channels
|
||||||
|
|
||||||
index = channelIndex
|
index = channelIndex
|
||||||
while index < self.iface.myInfo.max_channels:
|
while index < 8:
|
||||||
self.writeChannel(index, adminIndex=adminIndex)
|
self.writeChannel(index, adminIndex=adminIndex)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
@@ -125,7 +250,7 @@ class Node:
|
|||||||
|
|
||||||
def getChannelByName(self, name):
|
def getChannelByName(self, name):
|
||||||
"""Try to find the named channel or return None"""
|
"""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:
|
if c.settings and c.settings.name == name:
|
||||||
return c
|
return c
|
||||||
return None
|
return None
|
||||||
@@ -145,44 +270,33 @@ class Node:
|
|||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def setOwner(self, long_name=None, short_name=None, is_licensed=False, team=None):
|
def setOwner(self, long_name=None, short_name=None, is_licensed=False):
|
||||||
"""Set device owner name"""
|
"""Set device owner name"""
|
||||||
logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
|
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()
|
p = admin_pb2.AdminMessage()
|
||||||
|
|
||||||
|
nChars = 4
|
||||||
if long_name is not None:
|
if long_name is not None:
|
||||||
|
long_name = long_name.strip()
|
||||||
p.set_owner.long_name = long_name
|
p.set_owner.long_name = long_name
|
||||||
|
p.set_owner.is_licensed = is_licensed
|
||||||
if short_name is not None:
|
if short_name is not None:
|
||||||
short_name = short_name.strip()
|
short_name = short_name.strip()
|
||||||
if len(short_name) > nChars:
|
if len(short_name) > nChars:
|
||||||
short_name = 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.short_name = short_name
|
||||||
p.set_owner.is_licensed = is_licensed
|
|
||||||
if team is not None:
|
|
||||||
p.set_owner.team = team
|
|
||||||
|
|
||||||
# Note: These debug lines are used in unit tests
|
# 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.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.short_name:{p.set_owner.short_name}:")
|
||||||
logging.debug(f'p.set_owner.is_licensed:{p.set_owner.is_licensed}')
|
logging.debug(f"p.set_owner.is_licensed:{p.set_owner.is_licensed}")
|
||||||
logging.debug(f'p.set_owner.team:{p.set_owner.team}')
|
# If sending to a remote node, wait for ACK/NAK
|
||||||
return self._sendAdmin(p)
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
def getURL(self, includeAll: bool = True):
|
def getURL(self, includeAll: bool = True):
|
||||||
"""The sharable URL that describes the current channel"""
|
"""The sharable URL that describes the current channel"""
|
||||||
@@ -190,18 +304,23 @@ class Node:
|
|||||||
channelSet = apponly_pb2.ChannelSet()
|
channelSet = apponly_pb2.ChannelSet()
|
||||||
if self.channels:
|
if self.channels:
|
||||||
for c in 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.settings.append(c.settings)
|
||||||
|
|
||||||
|
channelSet.lora_config.CopyFrom(self.localConfig.lora)
|
||||||
some_bytes = channelSet.SerializeToString()
|
some_bytes = channelSet.SerializeToString()
|
||||||
s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
|
s = base64.urlsafe_b64encode(some_bytes).decode("ascii")
|
||||||
return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
|
s = s.replace("=", "").replace("+", "-").replace("/", "_")
|
||||||
|
return f"https://meshtastic.org/e/#{s}"
|
||||||
|
|
||||||
def setURL(self, url):
|
def setURL(self, url):
|
||||||
"""Set mesh network URL"""
|
"""Set mesh network URL"""
|
||||||
if self.radioConfig is None:
|
if self.localConfig is None:
|
||||||
our_exit("Warning: No RadioConfig has been read")
|
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
|
# Split on '/#' to find the base64 encoded channel settings
|
||||||
splitURL = url.split("/#")
|
splitURL = url.split("/#")
|
||||||
b64 = splitURL[-1]
|
b64 = splitURL[-1]
|
||||||
@@ -211,69 +330,186 @@ class Node:
|
|||||||
# per https://stackoverflow.com/a/9807138
|
# per https://stackoverflow.com/a/9807138
|
||||||
missing_padding = len(b64) % 4
|
missing_padding = len(b64) % 4
|
||||||
if missing_padding:
|
if missing_padding:
|
||||||
b64 += '=' * (4 - missing_padding)
|
b64 += "=" * (4 - missing_padding)
|
||||||
|
|
||||||
decodedURL = base64.urlsafe_b64decode(b64)
|
decodedURL = base64.urlsafe_b64decode(b64)
|
||||||
channelSet = apponly_pb2.ChannelSet()
|
channelSet = apponly_pb2.ChannelSet()
|
||||||
channelSet.ParseFromString(decodedURL)
|
channelSet.ParseFromString(decodedURL)
|
||||||
|
|
||||||
|
|
||||||
if len(channelSet.settings) == 0:
|
if len(channelSet.settings) == 0:
|
||||||
our_exit("Warning: There were no settings.")
|
our_exit("Warning: There were no settings.")
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for chs in channelSet.settings:
|
for chs in channelSet.settings:
|
||||||
ch = channel_pb2.Channel()
|
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.index = i
|
||||||
ch.settings.CopyFrom(chs)
|
ch.settings.CopyFrom(chs)
|
||||||
self.channels[ch.index] = ch
|
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)
|
self.writeChannel(ch.index)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.set_config.lora.CopyFrom(channelSet.lora_config)
|
||||||
|
self._sendAdmin(p)
|
||||||
|
|
||||||
def onResponseRequestSettings(self, p):
|
def onResponseRequestRingtone(self, p):
|
||||||
"""Handle the response packet for requesting settings _requestSettings()"""
|
"""Handle the response packet for requesting ringtone part 1"""
|
||||||
logging.debug(f'onResponseRequestSetting() p:{p}')
|
logging.debug(f"onResponseRequestRingtone() p:{p}")
|
||||||
errorFound = False
|
errorFound = False
|
||||||
if 'routing' in p["decoded"]:
|
if "routing" in p["decoded"]:
|
||||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||||
errorFound = True
|
errorFound = True
|
||||||
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
|
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
|
||||||
if errorFound is False:
|
if errorFound is False:
|
||||||
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
|
if "decoded" in p:
|
||||||
logging.debug(f'self.radioConfig:{self.radioConfig}')
|
if "admin" in p["decoded"]:
|
||||||
logging.debug("Received radio config, now fetching channels...")
|
if "raw" in p["decoded"]["admin"]:
|
||||||
self._timeout.reset() # We made foreward progress
|
self.ringtonePart = p["decoded"]["admin"][
|
||||||
self._requestChannel(0) # now start fetching channels
|
"raw"
|
||||||
|
].get_ringtone_response
|
||||||
|
logging.debug(f"self.ringtonePart:{self.ringtonePart}")
|
||||||
|
self.gotResponse = True
|
||||||
|
|
||||||
|
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_ringtone_request = True
|
||||||
|
self.gotResponse = False
|
||||||
|
self._sendAdmin(
|
||||||
|
p1, wantResponse=True, onResponse=self.onResponseRequestRingtone
|
||||||
|
)
|
||||||
|
while self.gotResponse is False:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
def _requestSettings(self):
|
logging.debug(f"self.ringtone:{self.ringtone}")
|
||||||
"""Done with initial config messages, now send regular
|
|
||||||
MeshPackets to ask for settings."""
|
|
||||||
p = admin_pb2.AdminMessage()
|
|
||||||
p.get_radio_request = True
|
|
||||||
|
|
||||||
# TODO: should we check that localNode has an 'admin' channel?
|
self.ringtone = ""
|
||||||
# Show progress message for super slow operations
|
if self.ringtonePart:
|
||||||
if self != self.iface.localNode:
|
self.ringtone += self.ringtonePart
|
||||||
print("Requesting preferences from remote node.")
|
|
||||||
print("Be sure:")
|
|
||||||
print(" 1. There is a SECONDARY channel named 'admin'.")
|
|
||||||
print(" 2. The '--seturl' was used to configure.")
|
|
||||||
print(" 3. All devices have the same modem config. (i.e., '--ch-longfast')")
|
|
||||||
print(" 4. All devices have been rebooted after all of the above. (optional, but recommended)")
|
|
||||||
print("Note: This could take a while (it requests remote channel configs, then writes config)")
|
|
||||||
|
|
||||||
return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings)
|
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}")
|
||||||
|
return self.cannedPluginMessage
|
||||||
|
|
||||||
|
def set_canned_message(self, message):
|
||||||
|
"""Set the canned message. The canned messages length must be less than 200 character."""
|
||||||
|
|
||||||
|
if len(message) > 200:
|
||||||
|
our_exit("Warning: The canned message must be less than 200 characters.")
|
||||||
|
|
||||||
|
# split into chunks
|
||||||
|
chunks = []
|
||||||
|
chunks_size = 200
|
||||||
|
for i in range(0, len(message), 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, chunk in enumerate(chunks):
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
|
||||||
|
# TODO: should be a way to improve this
|
||||||
|
if i == 0:
|
||||||
|
p.set_canned_message_module_messages = chunk
|
||||||
|
|
||||||
|
logging.debug(f"Setting canned message '{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 exitSimulator(self):
|
def exitSimulator(self):
|
||||||
"""Tell a simulator node to exit (this message
|
"""Tell a simulator node to exit (this message
|
||||||
is ignored for other nodes)"""
|
is ignored for other nodes)"""
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.exit_simulator = True
|
p.exit_simulator = True
|
||||||
logging.debug('in exitSimulator()')
|
logging.debug("in exitSimulator()")
|
||||||
|
|
||||||
return self._sendAdmin(p)
|
return self._sendAdmin(p)
|
||||||
|
|
||||||
@@ -283,7 +519,51 @@ class Node:
|
|||||||
p.reboot_seconds = secs
|
p.reboot_seconds = secs
|
||||||
logging.info(f"Telling node to reboot in {secs} seconds")
|
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."""
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.reboot_ota_seconds = secs
|
||||||
|
logging.info(f"Telling node to reboot to OTA in {secs} seconds")
|
||||||
|
|
||||||
|
# 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):
|
def shutdown(self, secs: int = 10):
|
||||||
"""Tell the node to shutdown."""
|
"""Tell the node to shutdown."""
|
||||||
@@ -291,7 +571,48 @@ class Node:
|
|||||||
p.shutdown_seconds = secs
|
p.shutdown_seconds = secs
|
||||||
logging.info(f"Telling node to shutdown in {secs} seconds")
|
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):
|
||||||
|
"""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
|
||||||
|
)
|
||||||
|
|
||||||
|
def factoryReset(self):
|
||||||
|
"""Tell the node to factory reset."""
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.factory_reset = True
|
||||||
|
logging.info(f"Telling node to factory reset")
|
||||||
|
|
||||||
|
# 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."""
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.nodedb_reset = True
|
||||||
|
logging.info(f"Telling node to reset the NodeDB")
|
||||||
|
|
||||||
|
# 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):
|
def _fixupChannels(self):
|
||||||
"""Fixup indexes and add disabled channels as needed"""
|
"""Fixup indexes and add disabled channels as needed"""
|
||||||
@@ -308,20 +629,59 @@ class Node:
|
|||||||
|
|
||||||
# Add extra disabled channels as needed
|
# Add extra disabled channels as needed
|
||||||
index = len(self.channels)
|
index = len(self.channels)
|
||||||
while index < self.iface.myInfo.max_channels:
|
while index < 8:
|
||||||
ch = channel_pb2.Channel()
|
ch = channel_pb2.Channel()
|
||||||
ch.role = channel_pb2.Channel.Role.DISABLED
|
ch.role = channel_pb2.Channel.Role.DISABLED
|
||||||
ch.index = index
|
ch.index = index
|
||||||
self.channels.append(ch)
|
self.channels.append(ch)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
|
def onRequestGetMetadata(self, p):
|
||||||
|
"""Handle the response packet for requesting device metadata getMetadata()"""
|
||||||
|
logging.debug(f"onRequestGetMetadata() p:{p}")
|
||||||
|
|
||||||
|
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.debug(f"Retrying metadata request.")
|
||||||
|
self.getMetadata()
|
||||||
|
return
|
||||||
|
|
||||||
|
c = p["decoded"]["admin"]["raw"].get_device_metadata_response
|
||||||
|
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):
|
def onResponseRequestChannel(self, p):
|
||||||
"""Handle the response packet for requesting a channel _requestChannel()"""
|
"""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"]["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
|
||||||
|
lastTried = 0
|
||||||
|
if len(self.partialChannels) > 0:
|
||||||
|
lastTried = self.partialChannels[-1].index
|
||||||
|
logging.debug(f"Retrying previous channel request.")
|
||||||
|
self._requestChannel(lastTried)
|
||||||
|
return
|
||||||
|
|
||||||
c = p["decoded"]["admin"]["raw"].get_channel_response
|
c = p["decoded"]["admin"]["raw"].get_channel_response
|
||||||
self.partialChannels.append(c)
|
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)}")
|
logging.debug(f"Received channel {stripnl(c)}")
|
||||||
index = c.index
|
index = c.index
|
||||||
|
|
||||||
@@ -330,9 +690,11 @@ class Node:
|
|||||||
|
|
||||||
# Once we see a response that has NO settings, assume
|
# Once we see a response that has NO settings, assume
|
||||||
# we are at the end of channels and stop fetching
|
# 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")
|
logging.debug("Finished downloading channels")
|
||||||
|
|
||||||
self.channels = self.partialChannels
|
self.channels = self.partialChannels
|
||||||
@@ -343,37 +705,70 @@ class Node:
|
|||||||
else:
|
else:
|
||||||
self._requestChannel(index + 1)
|
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):
|
def _requestChannel(self, channelNum: int):
|
||||||
"""Done with initial config messages, now send regular
|
"""Done with initial config messages, now send regular
|
||||||
MeshPackets to ask for settings"""
|
MeshPackets to ask for settings"""
|
||||||
p = admin_pb2.AdminMessage()
|
p = admin_pb2.AdminMessage()
|
||||||
p.get_channel_request = channelNum + 1
|
p.get_channel_request = channelNum + 1
|
||||||
|
|
||||||
# Show progress message for super slow operations
|
# Show progress message for super slow operations
|
||||||
if self != self.iface.localNode:
|
if self != self.iface.localNode:
|
||||||
print(f"Requesting channel {channelNum} info from remote node (this could take a while)")
|
print(
|
||||||
logging.debug(f"Requesting channel {channelNum} info from remote node (this could take a while)")
|
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:
|
else:
|
||||||
logging.debug(f"Requesting channel {channelNum}")
|
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
|
# pylint: disable=R1710
|
||||||
def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=False,
|
def _sendAdmin(
|
||||||
onResponse=None, adminIndex=0):
|
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)"""
|
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
|
||||||
|
|
||||||
if self.noProto:
|
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:
|
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()
|
adminIndex = self.iface.localNode._getAdminChannelIndex()
|
||||||
logging.debug(f'adminIndex:{adminIndex}')
|
logging.debug(f"adminIndex:{adminIndex}")
|
||||||
|
|
||||||
return self.iface.sendData(p, self.nodeNum,
|
return self.iface.sendData(
|
||||||
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
p,
|
||||||
wantAck=True,
|
self.nodeNum,
|
||||||
wantResponse=wantResponse,
|
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
||||||
onResponse=onResponse,
|
wantAck=False,
|
||||||
channelIndex=adminIndex)
|
wantResponse=wantResponse,
|
||||||
|
onResponse=onResponse,
|
||||||
|
channelIndex=adminIndex,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: portnums.proto
|
# source: meshtastic/portnums.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf.internal import enum_type_wrapper
|
from google.protobuf.internal import enum_type_wrapper
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eportnums.proto*\xcb\x02\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12!\n\x1d\x45NVIRONMENTAL_MEASUREMENT_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42\x44\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19meshtastic/portnums.proto*\xd4\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\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')
|
||||||
|
|
||||||
_PORTNUM = DESCRIPTOR.enum_types_by_name['PortNum']
|
_PORTNUM = DESCRIPTOR.enum_types_by_name['PortNum']
|
||||||
PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM)
|
PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM)
|
||||||
@@ -26,13 +26,20 @@ POSITION_APP = 3
|
|||||||
NODEINFO_APP = 4
|
NODEINFO_APP = 4
|
||||||
ROUTING_APP = 5
|
ROUTING_APP = 5
|
||||||
ADMIN_APP = 6
|
ADMIN_APP = 6
|
||||||
|
TEXT_MESSAGE_COMPRESSED_APP = 7
|
||||||
|
WAYPOINT_APP = 8
|
||||||
|
AUDIO_APP = 9
|
||||||
|
DETECTION_SENSOR_APP = 10
|
||||||
REPLY_APP = 32
|
REPLY_APP = 32
|
||||||
IP_TUNNEL_APP = 33
|
IP_TUNNEL_APP = 33
|
||||||
SERIAL_APP = 64
|
SERIAL_APP = 64
|
||||||
STORE_FORWARD_APP = 65
|
STORE_FORWARD_APP = 65
|
||||||
RANGE_TEST_APP = 66
|
RANGE_TEST_APP = 66
|
||||||
ENVIRONMENTAL_MEASUREMENT_APP = 67
|
TELEMETRY_APP = 67
|
||||||
ZPS_APP = 68
|
ZPS_APP = 68
|
||||||
|
SIMULATOR_APP = 69
|
||||||
|
TRACEROUTE_APP = 70
|
||||||
|
NEIGHBORINFO_APP = 71
|
||||||
PRIVATE_APP = 256
|
PRIVATE_APP = 256
|
||||||
ATAK_FORWARDER = 257
|
ATAK_FORWARDER = 257
|
||||||
MAX = 511
|
MAX = 511
|
||||||
@@ -41,7 +48,7 @@ MAX = 511
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_PORTNUM._serialized_start=19
|
_PORTNUM._serialized_start=30
|
||||||
_PORTNUM._serialized_end=350
|
_PORTNUM._serialized_end=498
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,14 +1,15 @@
|
|||||||
"""Remote hardware
|
"""Remote hardware
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
|
||||||
from meshtastic import portnums_pb2, remote_hardware_pb2
|
from meshtastic import portnums_pb2, remote_hardware_pb2
|
||||||
from meshtastic.util import our_exit
|
from meshtastic.util import our_exit
|
||||||
|
|
||||||
|
|
||||||
def onGPIOreceive(packet, interface):
|
def onGPIOreceive(packet, interface):
|
||||||
"""Callback for received GPIO responses
|
"""Callback for received GPIO responses"""
|
||||||
"""
|
|
||||||
logging.debug(f"packet:{packet} interface:{interface}")
|
logging.debug(f"packet:{packet} interface:{interface}")
|
||||||
gpioValue = 0
|
gpioValue = 0
|
||||||
hw = packet["decoded"]["remotehw"]
|
hw = packet["decoded"]["remotehw"]
|
||||||
@@ -21,9 +22,11 @@ def onGPIOreceive(packet, interface):
|
|||||||
# so, we set it here
|
# so, we set it here
|
||||||
gpioValue = 0
|
gpioValue = 0
|
||||||
|
|
||||||
#print(f'mask:{interface.mask}')
|
# print(f'mask:{interface.mask}')
|
||||||
value = int(gpioValue) & int(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
|
interface.gotResponse = True
|
||||||
|
|
||||||
|
|
||||||
@@ -44,46 +47,55 @@ class RemoteHardwareClient:
|
|||||||
ch = iface.localNode.getChannelByName("gpio")
|
ch = iface.localNode.getChannelByName("gpio")
|
||||||
if not ch:
|
if not ch:
|
||||||
our_exit(
|
our_exit(
|
||||||
"Warning: No channel named 'gpio' was found.\n"\
|
"Warning: No channel named 'gpio' was found.\n"
|
||||||
"On the sending and receive nodes create a channel named 'gpio'.\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"\
|
"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.")
|
"the other devices using the url from the device where the channel was added."
|
||||||
|
)
|
||||||
self.channelIndex = ch.index
|
self.channelIndex = ch.index
|
||||||
|
|
||||||
pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw")
|
pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw")
|
||||||
|
|
||||||
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
|
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
|
||||||
if not nodeid:
|
if not nodeid:
|
||||||
our_exit(r"Warning: Must use a destination node ID for this operation (use --dest \!xxxxxxxxx)")
|
our_exit(
|
||||||
return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP,
|
r"Warning: Must use a destination node ID for this operation (use --dest \!xxxxxxxxx)"
|
||||||
wantAck=True, channelIndex=self.channelIndex,
|
)
|
||||||
wantResponse=wantResponse, onResponse=onResponse)
|
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):
|
def writeGPIOs(self, nodeid, mask, vals):
|
||||||
"""
|
"""
|
||||||
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
||||||
are 1 will be changed
|
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 = 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_mask = mask
|
||||||
r.gpio_value = vals
|
r.gpio_value = vals
|
||||||
return self._sendHardware(nodeid, r)
|
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"""
|
"""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 = 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
|
r.gpio_mask = mask
|
||||||
return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse)
|
return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse)
|
||||||
|
|
||||||
def watchGPIOs(self, nodeid, mask):
|
def watchGPIOs(self, nodeid, mask):
|
||||||
"""Watch the specified bits from GPIO inputs on the device for changes"""
|
"""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 = 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
|
r.gpio_mask = mask
|
||||||
self.iface.mask = mask
|
self.iface.mask = mask
|
||||||
return self._sendHardware(nodeid, r)
|
return self._sendHardware(nodeid, r)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: remote_hardware.proto
|
# source: meshtastic/remote_hardware.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15remote_hardware.proto\"\xca\x01\n\x0fHardwareMessage\x12\"\n\x03typ\x18\x01 \x01(\x0e\x32\x15.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42J\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ _HARDWAREMESSAGE = DESCRIPTOR.message_types_by_name['HardwareMessage']
|
|||||||
_HARDWAREMESSAGE_TYPE = _HARDWAREMESSAGE.enum_types_by_name['Type']
|
_HARDWAREMESSAGE_TYPE = _HARDWAREMESSAGE.enum_types_by_name['Type']
|
||||||
HardwareMessage = _reflection.GeneratedProtocolMessageType('HardwareMessage', (_message.Message,), {
|
HardwareMessage = _reflection.GeneratedProtocolMessageType('HardwareMessage', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _HARDWAREMESSAGE,
|
'DESCRIPTOR' : _HARDWAREMESSAGE,
|
||||||
'__module__' : 'remote_hardware_pb2'
|
'__module__' : 'meshtastic.remote_hardware_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:HardwareMessage)
|
# @@protoc_insertion_point(class_scope:HardwareMessage)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(HardwareMessage)
|
_sym_db.RegisterMessage(HardwareMessage)
|
||||||
@@ -30,9 +30,9 @@ _sym_db.RegisterMessage(HardwareMessage)
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_HARDWAREMESSAGE._serialized_start=26
|
_HARDWAREMESSAGE._serialized_start=37
|
||||||
_HARDWAREMESSAGE._serialized_end=228
|
_HARDWAREMESSAGE._serialized_end=240
|
||||||
_HARDWAREMESSAGE_TYPE._serialized_start=120
|
_HARDWAREMESSAGE_TYPE._serialized_start=132
|
||||||
_HARDWAREMESSAGE_TYPE._serialized_end=228
|
_HARDWAREMESSAGE_TYPE._serialized_end=240
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
35
meshtastic/rtttl_pb2.py
Normal file
35
meshtastic/rtttl_pb2.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: meshtastic/rtttl.proto
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_RTTTLCONFIG = DESCRIPTOR.message_types_by_name['RTTTLConfig']
|
||||||
|
RTTTLConfig = _reflection.GeneratedProtocolMessageType('RTTTLConfig', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _RTTTLCONFIG,
|
||||||
|
'__module__' : 'meshtastic.rtttl_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:RTTTLConfig)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(RTTTLConfig)
|
||||||
|
|
||||||
|
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
|
""" Serial interface class
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
import platform
|
import platform
|
||||||
|
import time
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
import meshtastic.util
|
import meshtastic.util
|
||||||
from meshtastic.stream_interface import StreamInterface
|
from meshtastic.stream_interface import StreamInterface
|
||||||
|
|
||||||
if platform.system() != 'Windows':
|
if platform.system() != "Windows":
|
||||||
import termios
|
import termios
|
||||||
|
|
||||||
|
|
||||||
class SerialInterface(StreamInterface):
|
class SerialInterface(StreamInterface):
|
||||||
"""Interface class for meshtastic devices over a serial link"""
|
"""Interface class for meshtastic devices over a serial link"""
|
||||||
|
|
||||||
@@ -24,8 +26,10 @@ class SerialInterface(StreamInterface):
|
|||||||
"""
|
"""
|
||||||
self.noProto = noProto
|
self.noProto = noProto
|
||||||
|
|
||||||
if devPath is None:
|
self.devPath = devPath
|
||||||
ports = meshtastic.util.findPorts()
|
|
||||||
|
if self.devPath is None:
|
||||||
|
ports = meshtastic.util.findPorts(True)
|
||||||
logging.debug(f"ports:{ports}")
|
logging.debug(f"ports:{ports}")
|
||||||
if len(ports) == 0:
|
if len(ports) == 0:
|
||||||
meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
|
meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
|
||||||
@@ -34,25 +38,29 @@ class SerialInterface(StreamInterface):
|
|||||||
message += f" Ports detected:{ports}"
|
message += f" Ports detected:{ports}"
|
||||||
meshtastic.util.our_exit(message)
|
meshtastic.util.our_exit(message)
|
||||||
else:
|
else:
|
||||||
devPath = ports[0]
|
self.devPath = ports[0]
|
||||||
|
|
||||||
logging.debug(f"Connecting to {devPath}")
|
logging.debug(f"Connecting to {self.devPath}")
|
||||||
|
|
||||||
# first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR
|
# 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
|
# see https://github.com/pyserial/pyserial/issues/124
|
||||||
if platform.system() != 'Windows':
|
if platform.system() != "Windows":
|
||||||
with open(devPath, encoding='utf8') as f:
|
with open(self.devPath, encoding="utf8") as f:
|
||||||
attrs = termios.tcgetattr(f)
|
attrs = termios.tcgetattr(f)
|
||||||
attrs[2] = attrs[2] & ~termios.HUPCL
|
attrs[2] = attrs[2] & ~termios.HUPCL
|
||||||
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
|
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
|
||||||
f.close()
|
f.close()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
self.stream = serial.Serial(devPath, 921600, 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()
|
self.stream.flush()
|
||||||
time.sleep(0.1)
|
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):
|
def close(self):
|
||||||
"""Close a connection to the device"""
|
"""Close a connection to the device"""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: storeforward.proto
|
# source: meshtastic/storeforward.proto
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -14,7 +14,7 @@ _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')
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -27,26 +27,26 @@ StoreAndForward = _reflection.GeneratedProtocolMessageType('StoreAndForward', (_
|
|||||||
|
|
||||||
'Statistics' : _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), {
|
'Statistics' : _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _STOREANDFORWARD_STATISTICS,
|
'DESCRIPTOR' : _STOREANDFORWARD_STATISTICS,
|
||||||
'__module__' : 'storeforward_pb2'
|
'__module__' : 'meshtastic.storeforward_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:StoreAndForward.Statistics)
|
# @@protoc_insertion_point(class_scope:StoreAndForward.Statistics)
|
||||||
})
|
})
|
||||||
,
|
,
|
||||||
|
|
||||||
'History' : _reflection.GeneratedProtocolMessageType('History', (_message.Message,), {
|
'History' : _reflection.GeneratedProtocolMessageType('History', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _STOREANDFORWARD_HISTORY,
|
'DESCRIPTOR' : _STOREANDFORWARD_HISTORY,
|
||||||
'__module__' : 'storeforward_pb2'
|
'__module__' : 'meshtastic.storeforward_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:StoreAndForward.History)
|
# @@protoc_insertion_point(class_scope:StoreAndForward.History)
|
||||||
})
|
})
|
||||||
,
|
,
|
||||||
|
|
||||||
'Heartbeat' : _reflection.GeneratedProtocolMessageType('Heartbeat', (_message.Message,), {
|
'Heartbeat' : _reflection.GeneratedProtocolMessageType('Heartbeat', (_message.Message,), {
|
||||||
'DESCRIPTOR' : _STOREANDFORWARD_HEARTBEAT,
|
'DESCRIPTOR' : _STOREANDFORWARD_HEARTBEAT,
|
||||||
'__module__' : 'storeforward_pb2'
|
'__module__' : 'meshtastic.storeforward_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:StoreAndForward.Heartbeat)
|
# @@protoc_insertion_point(class_scope:StoreAndForward.Heartbeat)
|
||||||
})
|
})
|
||||||
,
|
,
|
||||||
'DESCRIPTOR' : _STOREANDFORWARD,
|
'DESCRIPTOR' : _STOREANDFORWARD,
|
||||||
'__module__' : 'storeforward_pb2'
|
'__module__' : 'meshtastic.storeforward_pb2'
|
||||||
# @@protoc_insertion_point(class_scope:StoreAndForward)
|
# @@protoc_insertion_point(class_scope:StoreAndForward)
|
||||||
})
|
})
|
||||||
_sym_db.RegisterMessage(StoreAndForward)
|
_sym_db.RegisterMessage(StoreAndForward)
|
||||||
@@ -57,15 +57,15 @@ _sym_db.RegisterMessage(StoreAndForward.Heartbeat)
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosH\003Z!github.com/meshtastic/gomeshproto'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_STOREANDFORWARD._serialized_start=23
|
_STOREANDFORWARD._serialized_start=34
|
||||||
_STOREANDFORWARD._serialized_end=801
|
_STOREANDFORWARD._serialized_end=864
|
||||||
_STOREANDFORWARD_STATISTICS._serialized_start=223
|
_STOREANDFORWARD_STATISTICS._serialized_start=257
|
||||||
_STOREANDFORWARD_STATISTICS._serialized_end=428
|
_STOREANDFORWARD_STATISTICS._serialized_end=462
|
||||||
_STOREANDFORWARD_HISTORY._serialized_start=430
|
_STOREANDFORWARD_HISTORY._serialized_start=464
|
||||||
_STOREANDFORWARD_HISTORY._serialized_end=503
|
_STOREANDFORWARD_HISTORY._serialized_end=537
|
||||||
_STOREANDFORWARD_HEARTBEAT._serialized_start=505
|
_STOREANDFORWARD_HEARTBEAT._serialized_start=539
|
||||||
_STOREANDFORWARD_HEARTBEAT._serialized_end=551
|
_STOREANDFORWARD_HEARTBEAT._serialized_end=585
|
||||||
_STOREANDFORWARD_REQUESTRESPONSE._serialized_start=554
|
_STOREANDFORWARD_REQUESTRESPONSE._serialized_start=588
|
||||||
_STOREANDFORWARD_REQUESTRESPONSE._serialized_end=801
|
_STOREANDFORWARD_REQUESTRESPONSE._serialized_end=853
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
|
|
||||||
from meshtastic.mesh_interface import MeshInterface
|
from meshtastic.mesh_interface import MeshInterface
|
||||||
from meshtastic.util import stripnl
|
from meshtastic.util import is_windows11, stripnl
|
||||||
|
|
||||||
|
|
||||||
START1 = 0x94
|
START1 = 0x94
|
||||||
START2 = 0xc3
|
START2 = 0xC3
|
||||||
HEADER_LEN = 4
|
HEADER_LEN = 4
|
||||||
MAX_TO_FROM_RADIO_SIZE = 512
|
MAX_TO_FROM_RADIO_SIZE = 512
|
||||||
|
|
||||||
@@ -32,12 +31,15 @@ class StreamInterface(MeshInterface):
|
|||||||
Exception: [description]
|
Exception: [description]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not hasattr(self, 'stream') and not noProto:
|
if not hasattr(self, "stream") and not noProto:
|
||||||
raise Exception(
|
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._rxBuf = bytes() # empty
|
||||||
self._wantExit = False
|
self._wantExit = False
|
||||||
|
|
||||||
|
self.is_windows11 = is_windows11()
|
||||||
|
|
||||||
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
||||||
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
|
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ class StreamInterface(MeshInterface):
|
|||||||
|
|
||||||
# Send some bogus UART characters to force a sleeping device to wake, and
|
# Send some bogus UART characters to force a sleeping device to wake, and
|
||||||
# if the reading statemachine was parsing a bad packet make sure
|
# 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)
|
# because we want to ensure it is looking for START1)
|
||||||
p = bytearray([START2] * 32)
|
p = bytearray([START2] * 32)
|
||||||
self._writeBytes(p)
|
self._writeBytes(p)
|
||||||
@@ -88,8 +90,12 @@ class StreamInterface(MeshInterface):
|
|||||||
if self.stream: # ignore writes when stream is closed
|
if self.stream: # ignore writes when stream is closed
|
||||||
self.stream.write(b)
|
self.stream.write(b)
|
||||||
self.stream.flush()
|
self.stream.flush()
|
||||||
# we sleep here to give the TBeam a chance to work
|
# win11 might need a bit more time, too
|
||||||
time.sleep(0.1)
|
if self.is_windows11:
|
||||||
|
time.sleep(1.0)
|
||||||
|
else:
|
||||||
|
# we sleep here to give the TBeam a chance to work
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
def _readBytes(self, length):
|
def _readBytes(self, length):
|
||||||
"""Read an array of bytes from our stream"""
|
"""Read an array of bytes from our stream"""
|
||||||
@@ -104,8 +110,8 @@ class StreamInterface(MeshInterface):
|
|||||||
b = toRadio.SerializeToString()
|
b = toRadio.SerializeToString()
|
||||||
bufLen = len(b)
|
bufLen = len(b)
|
||||||
# We convert into a string, because the TCP code doesn't work with byte arrays
|
# We convert into a string, because the TCP code doesn't work with byte arrays
|
||||||
header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
header = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
|
||||||
logging.debug(f'sending header:{header} b:{b}')
|
logging.debug(f"sending header:{header} b:{b}")
|
||||||
self._writeBytes(header + b)
|
self._writeBytes(header + b)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@@ -120,18 +126,18 @@ class StreamInterface(MeshInterface):
|
|||||||
|
|
||||||
def __reader(self):
|
def __reader(self):
|
||||||
"""The reader thread that reads bytes from our stream"""
|
"""The reader thread that reads bytes from our stream"""
|
||||||
logging.debug('in __reader()')
|
logging.debug("in __reader()")
|
||||||
empty = bytes()
|
empty = bytes()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while not self._wantExit:
|
while not self._wantExit:
|
||||||
logging.debug("reading character")
|
# logging.debug("reading character")
|
||||||
b = self._readBytes(1)
|
b = self._readBytes(1)
|
||||||
logging.debug("In reader loop")
|
# logging.debug("In reader loop")
|
||||||
#logging.debug(f"read returned {b}")
|
# logging.debug(f"read returned {b}")
|
||||||
if len(b) > 0:
|
if len(b) > 0:
|
||||||
c = b[0]
|
c = b[0]
|
||||||
#logging.debug(f'c:{c}')
|
# logging.debug(f'c:{c}')
|
||||||
ptr = len(self._rxBuf)
|
ptr = len(self._rxBuf)
|
||||||
|
|
||||||
# Assume we want to append this byte, fixme use bytearray instead
|
# Assume we want to append this byte, fixme use bytearray instead
|
||||||
@@ -144,38 +150,54 @@ class StreamInterface(MeshInterface):
|
|||||||
try:
|
try:
|
||||||
self.debugOut.write(b.decode("utf-8"))
|
self.debugOut.write(b.decode("utf-8"))
|
||||||
except:
|
except:
|
||||||
self.debugOut.write('?')
|
self.debugOut.write("?")
|
||||||
|
|
||||||
elif ptr == 1: # looking for START2
|
elif ptr == 1: # looking for START2
|
||||||
if c != START2:
|
if c != START2:
|
||||||
self._rxBuf = empty # failed to find start2
|
self._rxBuf = empty # failed to find start2
|
||||||
elif ptr >= HEADER_LEN - 1: # we've at least got a header
|
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
|
# big endian length follows header
|
||||||
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
|
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:
|
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:
|
if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
|
||||||
try:
|
try:
|
||||||
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
||||||
except Exception as ex:
|
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()
|
traceback.print_exc()
|
||||||
self._rxBuf = empty
|
self._rxBuf = empty
|
||||||
else:
|
else:
|
||||||
# logging.debug(f"timeout")
|
# logging.debug(f"timeout")
|
||||||
pass
|
pass
|
||||||
except serial.SerialException as ex:
|
except serial.SerialException as ex:
|
||||||
if not self._wantExit: # We might intentionally get an exception during shutdown
|
if (
|
||||||
logging.warning(f"Meshtastic serial port disconnected, disconnecting... {ex}")
|
not self._wantExit
|
||||||
|
): # We might intentionally get an exception during shutdown
|
||||||
|
logging.warning(
|
||||||
|
f"Meshtastic serial port disconnected, disconnecting... {ex}"
|
||||||
|
)
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
if not self._wantExit: # We might intentionally get an exception during shutdown
|
if (
|
||||||
logging.error(f"Unexpected OSError, terminating meshtastic reader... {ex}")
|
not self._wantExit
|
||||||
|
): # We might intentionally get an exception during shutdown
|
||||||
|
logging.error(
|
||||||
|
f"Unexpected OSError, terminating meshtastic reader... {ex}"
|
||||||
|
)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
|
logging.error(
|
||||||
|
f"Unexpected exception, terminating meshtastic reader... {ex}"
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
logging.debug("reader is exiting")
|
logging.debug("reader is exiting")
|
||||||
self._disconnected()
|
self._disconnected()
|
||||||
|
|||||||
@@ -2,191 +2,227 @@
|
|||||||
It is used for auto detection as to which device might be connected.
|
It is used for auto detection as to which device might be connected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import platform
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Goal is to detect which device and port to use from the supported devices
|
# 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
|
# without installing any libraries that are not currently in the python meshtastic library
|
||||||
|
|
||||||
class SupportedDevice():
|
|
||||||
|
class SupportedDevice:
|
||||||
"""Devices supported on Meshtastic"""
|
"""Devices supported on Meshtastic"""
|
||||||
|
|
||||||
def __init__(self, name, version=None, for_firmware=None, device_class="esp32",
|
def __init__(
|
||||||
baseport_on_linux=None, baseport_on_mac=None, baseport_on_windows="COM",
|
self,
|
||||||
usb_vendor_id_in_hex=None, usb_product_id_in_hex=None):
|
name,
|
||||||
""" constructor """
|
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.name = name
|
||||||
self.version = version
|
self.version = version
|
||||||
self.for_firmware = for_firmware
|
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
|
# 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_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_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_mac = baseport_on_mac
|
||||||
self.baseport_on_windows = baseport_on_windows
|
self.baseport_on_windows = baseport_on_windows
|
||||||
|
|
||||||
|
|
||||||
# supported devices
|
# supported devices
|
||||||
tbeam_v0_7 = SupportedDevice(name="T-Beam", version="0.7", for_firmware="tbeam0.7",
|
tbeam_v0_7 = SupportedDevice(
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
name="T-Beam",
|
||||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
version="0.7",
|
||||||
tbeam_v1_1 = SupportedDevice(name="T-Beam", version="1.1", for_firmware="tbeam",
|
for_firmware="tbeam0.7",
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
baseport_on_linux="ttyACM",
|
||||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
baseport_on_mac="cu.usbmodem",
|
||||||
tbeam_M8N = SupportedDevice(name="T-Beam", version="M8N", for_firmware="tbeam",
|
usb_vendor_id_in_hex="1a86",
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
usb_product_id_in_hex="55d4",
|
||||||
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",
|
tbeam_v1_1 = SupportedDevice(
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
name="T-Beam",
|
||||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
version="1.1",
|
||||||
tlora_v1_1 = SupportedDevice(name="T-Lora", version="1.1", for_firmware="tlora-v1",
|
for_firmware="tbeam",
|
||||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial",
|
baseport_on_linux="ttyACM",
|
||||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
baseport_on_mac="cu.usbmodem",
|
||||||
tlora_v1_3 = SupportedDevice(name="T-Lora", version="1.3", for_firmware="tlora-v1-3",
|
usb_vendor_id_in_hex="1a86",
|
||||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial",
|
usb_product_id_in_hex="55d4",
|
||||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
)
|
||||||
tlora_v2_0 = SupportedDevice(name="T-Lora", version="2.0", for_firmware="tlora-v2-1",
|
tbeam_M8N = SupportedDevice(
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
name="T-Beam",
|
||||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
version="M8N",
|
||||||
tlora_v2_1 = SupportedDevice(name="T-Lora", version="2.1", for_firmware="tlora-v2-1",
|
for_firmware="tbeam",
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
baseport_on_linux="ttyACM",
|
||||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
baseport_on_mac="cu.usbmodem",
|
||||||
tlora_v2_1_1_6 = SupportedDevice(name="T-Lora", version="2.1-1.6", for_firmware="tlora-v2-1-1.6",
|
usb_vendor_id_in_hex="1a86",
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
usb_product_id_in_hex="55d4",
|
||||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
)
|
||||||
heltec_v1 = SupportedDevice(name="Heltec", version="1", for_firmware="heltec-v1",
|
tbeam_M8N_SX1262 = SupportedDevice(
|
||||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
name="T-Beam",
|
||||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
version="M8N_SX1262",
|
||||||
heltec_v2_0 = SupportedDevice(name="Heltec", version="2.0", for_firmware="heltec-v2.0",
|
for_firmware="tbeam",
|
||||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
baseport_on_linux="ttyACM",
|
||||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
baseport_on_mac="cu.usbmodem",
|
||||||
heltec_v2_1 = SupportedDevice(name="Heltec", version="2.1", for_firmware="heltec-v2.1",
|
usb_vendor_id_in_hex="1a86",
|
||||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
usb_product_id_in_hex="55d4",
|
||||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
)
|
||||||
meshtastic_diy_v1 = SupportedDevice(name="Meshtastic DIY", version="1", for_firmware="meshtastic-diy-v1",
|
tlora_v1 = SupportedDevice(
|
||||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
|
name="T-Lora",
|
||||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
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
|
# 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",
|
techo_1 = SupportedDevice(
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
name="T-Echo",
|
||||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
|
version="1",
|
||||||
rak4631_5005 = SupportedDevice(name="RAK 4631 5005", version="", for_firmware="rak4631_5005",
|
for_firmware="t-echo-1",
|
||||||
device_class="nrf52",
|
device_class="nrf52",
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
baseport_on_linux="ttyACM",
|
||||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
|
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
|
# Note: The 19003 reports same product id as 5005 in boot mode
|
||||||
rak4631_19003 = SupportedDevice(name="RAK 4631 19003", version="", for_firmware="rak4631_19003",
|
rak4631_19003 = SupportedDevice(
|
||||||
device_class="nrf52",
|
name="RAK 4631 19003",
|
||||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
version="",
|
||||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="8029")
|
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,
|
supported_devices = [
|
||||||
tlora_v1_1, tlora_v1_3, tlora_v2_0, tlora_v2_1, tlora_v2_1_1_6,
|
tbeam_v0_7,
|
||||||
heltec_v1, heltec_v2_0, heltec_v2_1,
|
tbeam_v1_1,
|
||||||
meshtastic_diy_v1, techo_1, rak4631_5005, rak4631_19003]
|
tbeam_M8N,
|
||||||
|
tbeam_M8N_SX1262,
|
||||||
|
tlora_v1,
|
||||||
def get_unique_vendor_ids():
|
tlora_v1_3,
|
||||||
"""Return a set of unique vendor ids"""
|
tlora_v2,
|
||||||
vids = set()
|
tlora_v2_1_1_6,
|
||||||
for d in supported_devices:
|
heltec_v1,
|
||||||
if d.usb_vendor_id_in_hex:
|
heltec_v2_0,
|
||||||
vids.add(d.usb_vendor_id_in_hex)
|
heltec_v2_1,
|
||||||
return vids
|
meshtastic_diy_v1,
|
||||||
|
techo_1,
|
||||||
def get_devices_with_vendor_id(vid):
|
rak4631_5005,
|
||||||
"""Return a set of unique devices with the vendor id"""
|
rak4631_5005_epaper,
|
||||||
sd = set()
|
rak4631_19003,
|
||||||
for d in supported_devices:
|
rak11200,
|
||||||
if d.usb_vendor_id_in_hex == vid:
|
nano_g1,
|
||||||
sd.add(d)
|
]
|
||||||
return sd
|
|
||||||
|
|
||||||
def active_ports_on_supported_devices(sds):
|
|
||||||
"""Return a set of active ports based on the supplied supported devices"""
|
|
||||||
ports = set()
|
|
||||||
baseports = set()
|
|
||||||
system = platform.system()
|
|
||||||
|
|
||||||
# figure out what possible base ports there are
|
|
||||||
for d in sds:
|
|
||||||
if system == "Linux":
|
|
||||||
baseports.add(d.baseport_on_linux)
|
|
||||||
elif system == "Darwin":
|
|
||||||
baseports.add(d.baseport_on_mac)
|
|
||||||
elif system == "Windows":
|
|
||||||
baseports.add(d.baseport_on_windows)
|
|
||||||
|
|
||||||
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}')
|
|
||||||
_, ls_output = subprocess.getstatusoutput(command)
|
|
||||||
#print(f'ls_output:{ls_output}')
|
|
||||||
# if we got output, there are ports
|
|
||||||
if len(ls_output) > 0:
|
|
||||||
#print('got output')
|
|
||||||
# for each line of output
|
|
||||||
lines = ls_output.split('\n')
|
|
||||||
#print(f'lines:{lines}')
|
|
||||||
for line in lines:
|
|
||||||
parts = line.split(' ')
|
|
||||||
#print(f'parts:{parts}')
|
|
||||||
port = parts[-1]
|
|
||||||
#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}')
|
|
||||||
_, ls_output = subprocess.getstatusoutput(command)
|
|
||||||
#print(f'ls_output:{ls_output}')
|
|
||||||
# if we got output, there are ports
|
|
||||||
if len(ls_output) > 0:
|
|
||||||
#print('got output')
|
|
||||||
# for each line of output
|
|
||||||
lines = ls_output.split('\n')
|
|
||||||
#print(f'lines:{lines}')
|
|
||||||
for line in lines:
|
|
||||||
parts = line.split(' ')
|
|
||||||
#print(f'parts:{parts}')
|
|
||||||
port = parts[-1]
|
|
||||||
#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}')
|
|
||||||
# add all ports
|
|
||||||
for com_port in com_ports:
|
|
||||||
ports.add(com_port)
|
|
||||||
return ports
|
|
||||||
|
|
||||||
|
|
||||||
def detect_windows_port(sd):
|
|
||||||
"""detect if Windows port"""
|
|
||||||
ports = set()
|
|
||||||
|
|
||||||
if sd:
|
|
||||||
system = platform.system()
|
|
||||||
|
|
||||||
if system == "Windows":
|
|
||||||
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}')
|
|
||||||
_, sp_output = subprocess.getstatusoutput(command)
|
|
||||||
#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}')
|
|
||||||
return ports
|
|
||||||
|
|||||||
@@ -6,11 +6,18 @@ from typing import AnyStr
|
|||||||
|
|
||||||
from meshtastic.stream_interface import StreamInterface
|
from meshtastic.stream_interface import StreamInterface
|
||||||
|
|
||||||
|
|
||||||
class TCPInterface(StreamInterface):
|
class TCPInterface(StreamInterface):
|
||||||
"""Interface class for meshtastic devices over a TCP link"""
|
"""Interface class for meshtastic devices over a TCP link"""
|
||||||
|
|
||||||
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False,
|
def __init__(
|
||||||
connectNow=True, portNumber=4403):
|
self,
|
||||||
|
hostname: AnyStr,
|
||||||
|
debugOut=None,
|
||||||
|
noProto=False,
|
||||||
|
connectNow=True,
|
||||||
|
portNumber=4403,
|
||||||
|
):
|
||||||
"""Constructor, opens a connection to a specified IP address/hostname
|
"""Constructor, opens a connection to a specified IP address/hostname
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
@@ -30,12 +37,13 @@ class TCPInterface(StreamInterface):
|
|||||||
else:
|
else:
|
||||||
self.socket = None
|
self.socket = None
|
||||||
|
|
||||||
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
|
StreamInterface.__init__(
|
||||||
connectNow=connectNow)
|
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow
|
||||||
|
)
|
||||||
|
|
||||||
def _socket_shutdown(self):
|
def _socket_shutdown(self):
|
||||||
"""Shutdown the socket.
|
"""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)
|
self.socket.shutdown(socket.SHUT_RDWR)
|
||||||
|
|
||||||
|
|||||||
84
meshtastic/telemetry_pb2.py
Normal file
84
meshtastic/telemetry_pb2.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: meshtastic/telemetry.proto
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
from google.protobuf.internal import enum_type_wrapper
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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\"\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\"\xb5\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\x42\t\n\x07variant*\xc7\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\rBd\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\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
|
||||||
|
QMC5883L = 11
|
||||||
|
SHT31 = 12
|
||||||
|
PMSA003I = 13
|
||||||
|
|
||||||
|
|
||||||
|
_DEVICEMETRICS = DESCRIPTOR.message_types_by_name['DeviceMetrics']
|
||||||
|
_ENVIRONMENTMETRICS = DESCRIPTOR.message_types_by_name['EnvironmentMetrics']
|
||||||
|
_AIRQUALITYMETRICS = DESCRIPTOR.message_types_by_name['AirQualityMetrics']
|
||||||
|
_TELEMETRY = DESCRIPTOR.message_types_by_name['Telemetry']
|
||||||
|
DeviceMetrics = _reflection.GeneratedProtocolMessageType('DeviceMetrics', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _DEVICEMETRICS,
|
||||||
|
'__module__' : 'meshtastic.telemetry_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:DeviceMetrics)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(DeviceMetrics)
|
||||||
|
|
||||||
|
EnvironmentMetrics = _reflection.GeneratedProtocolMessageType('EnvironmentMetrics', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _ENVIRONMENTMETRICS,
|
||||||
|
'__module__' : 'meshtastic.telemetry_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:EnvironmentMetrics)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(EnvironmentMetrics)
|
||||||
|
|
||||||
|
AirQualityMetrics = _reflection.GeneratedProtocolMessageType('AirQualityMetrics', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _AIRQUALITYMETRICS,
|
||||||
|
'__module__' : 'meshtastic.telemetry_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:AirQualityMetrics)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(AirQualityMetrics)
|
||||||
|
|
||||||
|
Telemetry = _reflection.GeneratedProtocolMessageType('Telemetry', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _TELEMETRY,
|
||||||
|
'__module__' : 'meshtastic.telemetry_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:Telemetry)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(Telemetry)
|
||||||
|
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
|
DESCRIPTOR._options = None
|
||||||
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
|
_TELEMETRYSENSORTYPE._serialized_start=802
|
||||||
|
_TELEMETRYSENSORTYPE._serialized_end=1001
|
||||||
|
_DEVICEMETRICS._serialized_start=30
|
||||||
|
_DEVICEMETRICS._serialized_end=135
|
||||||
|
_ENVIRONMENTMETRICS._serialized_start=138
|
||||||
|
_ENVIRONMENTMETRICS._serialized_end=293
|
||||||
|
_AIRQUALITYMETRICS._serialized_start=296
|
||||||
|
_AIRQUALITYMETRICS._serialized_end=615
|
||||||
|
_TELEMETRY._serialized_start=618
|
||||||
|
_TELEMETRY._serialized_end=799
|
||||||
|
# @@protoc_insertion_point(module_scope)
|
||||||
@@ -2,17 +2,18 @@
|
|||||||
messages and report back if successful.
|
messages and report back if successful.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from dotmap import DotMap
|
from dotmap import DotMap
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
|
||||||
import meshtastic.util
|
import meshtastic.util
|
||||||
from meshtastic.__init__ import BROADCAST_NUM
|
from meshtastic.__init__ import BROADCAST_NUM
|
||||||
from meshtastic.serial_interface import SerialInterface
|
from meshtastic.serial_interface import SerialInterface
|
||||||
from meshtastic.tcp_interface import TCPInterface
|
from meshtastic.tcp_interface import TCPInterface
|
||||||
|
|
||||||
|
|
||||||
"""The interfaces we are using for our tests"""
|
"""The interfaces we are using for our tests"""
|
||||||
interfaces = None
|
interfaces = None
|
||||||
|
|
||||||
@@ -52,7 +53,9 @@ def subscribe():
|
|||||||
pub.subscribe(onNode, "meshtastic.node")
|
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
|
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:
|
else:
|
||||||
toNode = toInterface.myInfo.my_node_num
|
toNode = toInterface.myInfo.my_node_num
|
||||||
|
|
||||||
logging.debug(
|
logging.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
||||||
f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
|
||||||
# pylint: disable=W0603
|
# pylint: disable=W0603
|
||||||
global sendingInterface
|
global sendingInterface
|
||||||
sendingInterface = fromInterface
|
sendingInterface = fromInterface
|
||||||
if not asBinary:
|
if not asBinary:
|
||||||
fromInterface.sendText(f"Test {testNumber}", toNode, wantAck=wantAck)
|
fromInterface.sendText(f"Test {testNumber}", toNode, wantAck=wantAck)
|
||||||
else:
|
else:
|
||||||
fromInterface.sendData((f"Binary {testNumber}").encode(
|
fromInterface.sendData(
|
||||||
"utf-8"), toNode, wantAck=wantAck)
|
(f"Binary {testNumber}").encode("utf-8"), toNode, wantAck=wantAck
|
||||||
|
)
|
||||||
for _ in range(60): # max of 60 secs before we timeout
|
for _ in range(60): # max of 60 secs before we timeout
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if len(receivedPackets) >= 1:
|
if len(receivedPackets) >= 1:
|
||||||
return True
|
return True
|
||||||
return False # Failed to send
|
return False # Failed to send
|
||||||
|
|
||||||
@@ -102,15 +105,18 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
|
|||||||
isBroadcast = True
|
isBroadcast = True
|
||||||
# asBinary=(i % 2 == 0)
|
# asBinary=(i % 2 == 0)
|
||||||
success = testSend(
|
success = testSend(
|
||||||
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck)
|
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck
|
||||||
|
)
|
||||||
if not success:
|
if not success:
|
||||||
numFail = numFail + 1
|
numFail = numFail + 1
|
||||||
logging.error(
|
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:
|
else:
|
||||||
numSuccess = numSuccess + 1
|
numSuccess = numSuccess + 1
|
||||||
logging.info(
|
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)
|
time.sleep(1)
|
||||||
|
|
||||||
@@ -140,7 +146,7 @@ def openDebugLog(portName):
|
|||||||
"""Open the debug log file"""
|
"""Open the debug log file"""
|
||||||
debugname = "log" + portName.replace("/", "_")
|
debugname = "log" + portName.replace("/", "_")
|
||||||
logging.info(f"Writing serial debugging to {debugname}")
|
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):
|
def testAll(numTests=5):
|
||||||
@@ -149,16 +155,24 @@ def testAll(numTests=5):
|
|||||||
This is called from the cli with the "--test" option.
|
This is called from the cli with the "--test" option.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ports = meshtastic.util.findPorts()
|
ports = meshtastic.util.findPorts(True)
|
||||||
if len(ports) < 2:
|
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(onConnection, "meshtastic.connection")
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
pub.subscribe(onReceive, "meshtastic.receive")
|
||||||
# pylint: disable=W0603
|
# pylint: disable=W0603
|
||||||
global interfaces
|
global interfaces
|
||||||
interfaces = list(map(lambda port: SerialInterface(
|
interfaces = list(
|
||||||
port, debugOut=openDebugLog(port), connectNow=True), ports))
|
map(
|
||||||
|
lambda port: SerialInterface(
|
||||||
|
port, debugOut=openDebugLog(port), connectNow=True
|
||||||
|
),
|
||||||
|
ports,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
logging.info("Ports opened, starting test")
|
logging.info("Ports opened, starting test")
|
||||||
result = testThread(numTests)
|
result = testThread(numTests)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"""Common pytest code (place for fixtures)."""
|
"""Common pytest code (place for fixtures)."""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from meshtastic.__main__ import Globals
|
from meshtastic.__main__ import Globals
|
||||||
|
|
||||||
from ..mesh_interface import MeshInterface
|
from ..mesh_interface import MeshInterface
|
||||||
|
|
||||||
|
|
||||||
@@ -22,36 +23,34 @@ def reset_globals():
|
|||||||
def iface_with_nodes():
|
def iface_with_nodes():
|
||||||
"""Fixture to setup some nodes."""
|
"""Fixture to setup some nodes."""
|
||||||
nodesById = {
|
nodesById = {
|
||||||
'!9388f81c': {
|
"!9388f81c": {
|
||||||
'num': 2475227164,
|
"num": 2475227164,
|
||||||
'user': {
|
"user": {
|
||||||
'id': '!9388f81c',
|
"id": "!9388f81c",
|
||||||
'longName': 'Unknown f81c',
|
"longName": "Unknown f81c",
|
||||||
'shortName': '?1C',
|
"shortName": "?1C",
|
||||||
'macaddr': 'RBeTiPgc',
|
"macaddr": "RBeTiPgc",
|
||||||
'hwModel': 'TBEAM'
|
"hwModel": "TBEAM",
|
||||||
},
|
},
|
||||||
'position': {},
|
"position": {},
|
||||||
'lastHeard': 1640204888
|
"lastHeard": 1640204888,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodesByNum = {
|
nodesByNum = {
|
||||||
2475227164: {
|
2475227164: {
|
||||||
'num': 2475227164,
|
"num": 2475227164,
|
||||||
'user': {
|
"user": {
|
||||||
'id': '!9388f81c',
|
"id": "!9388f81c",
|
||||||
'longName': 'Unknown f81c',
|
"longName": "Unknown f81c",
|
||||||
'shortName': '?1C',
|
"shortName": "?1C",
|
||||||
'macaddr': 'RBeTiPgc',
|
"macaddr": "RBeTiPgc",
|
||||||
'hwModel': 'TBEAM'
|
"hwModel": "TBEAM",
|
||||||
},
|
},
|
||||||
'position': {
|
"position": {"time": 1640206266},
|
||||||
'time': 1640206266
|
"lastHeard": 1640206266,
|
||||||
},
|
}
|
||||||
'lastHeard': 1640206266
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
iface.nodes = nodesById
|
iface.nodes = nodesById
|
||||||
iface.nodesByNum = nodesByNum
|
iface.nodesByNum = nodesByNum
|
||||||
|
|||||||
@@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..ble_interface import BLEInterface
|
from ..ble_interface import BLEInterface
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('platform.system', return_value='Linux')
|
@patch("platform.system", return_value="Linux")
|
||||||
def test_BLEInterface(mock_platform):
|
def test_BLEInterface(mock_platform):
|
||||||
"""Test that we can instantiate a BLEInterface"""
|
"""Test that we can instantiate a BLEInterface"""
|
||||||
iface = BLEInterface('foo', debugOut=True, noProto=True)
|
iface = BLEInterface("foo", debugOut=True, noProto=True)
|
||||||
iface.close()
|
iface.close()
|
||||||
mock_platform.assert_called()
|
mock_platform.assert_called()
|
||||||
|
|||||||
@@ -6,19 +6,24 @@ import subprocess
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.examples
|
@pytest.mark.examples
|
||||||
def test_examples_hello_world_serial_no_arg():
|
def test_examples_hello_world_serial_no_arg():
|
||||||
"""Test hello_world_serial without any args"""
|
"""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
|
assert return_value == 3
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.examples
|
@pytest.mark.examples
|
||||||
def test_examples_hello_world_serial_with_arg(capsys):
|
def test_examples_hello_world_serial_with_arg(capsys):
|
||||||
"""Test hello_world_serial with arg"""
|
"""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
|
assert return_value == 1
|
||||||
_, err = capsys.readouterr()
|
_, err = capsys.readouterr()
|
||||||
assert err == ''
|
assert err == ""
|
||||||
# TODO: Why does this not work?
|
# TODO: Why does this not work?
|
||||||
# assert out == 'Warning: No Meshtastic devices detected.'
|
# assert out == 'Warning: No Meshtastic devices detected.'
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
"""Meshtastic unit tests for __init__.py"""
|
"""Meshtastic unit tests for __init__.py"""
|
||||||
|
|
||||||
import re
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from meshtastic.__init__ import _onTextReceive, _onPositionReceive, _onNodeInfoReceive
|
from meshtastic.__init__ import _onNodeInfoReceive, _onPositionReceive, _onTextReceive
|
||||||
from ..serial_interface import SerialInterface
|
|
||||||
from ..globals import Globals
|
from ..globals import Globals
|
||||||
|
from ..serial_interface import SerialInterface
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -20,8 +21,8 @@ def test_init_onTextReceive_with_exception(caplog):
|
|||||||
packet = {}
|
packet = {}
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
_onTextReceive(iface, packet)
|
_onTextReceive(iface, packet)
|
||||||
assert re.search(r'in _onTextReceive', caplog.text, re.MULTILINE)
|
assert re.search(r"in _onTextReceive", caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'Malformatted', caplog.text, re.MULTILINE)
|
assert re.search(r"Malformatted", caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -30,15 +31,10 @@ def test_init_onPositionReceive(caplog):
|
|||||||
args = MagicMock()
|
args = MagicMock()
|
||||||
Globals.getInstance().set_args(args)
|
Globals.getInstance().set_args(args)
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
packet = {
|
packet = {"from": "foo", "decoded": {"position": {}}}
|
||||||
'from': 'foo',
|
|
||||||
'decoded': {
|
|
||||||
'position': {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
_onPositionReceive(iface, packet)
|
_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
|
@pytest.mark.unit
|
||||||
@@ -49,13 +45,13 @@ def test_init_onNodeInfoReceive(caplog, iface_with_nodes):
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
packet = {
|
packet = {
|
||||||
'from': 'foo',
|
"from": "foo",
|
||||||
'decoded': {
|
"decoded": {
|
||||||
'user': {
|
"user": {
|
||||||
'id': 'bar',
|
"id": "bar",
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
_onNodeInfoReceive(iface, packet)
|
_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
|
@pytest.mark.int
|
||||||
def test_int_meshtastic_no_args():
|
def test_int_meshtastic_no_args():
|
||||||
"""Test meshtastic without any args"""
|
"""Test meshtastic without any args"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic')
|
return_value, out = subprocess.getstatusoutput("meshtastic")
|
||||||
assert re.match(r'usage: meshtastic', out)
|
assert re.match(r"usage: meshtastic", out)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.int
|
@pytest.mark.int
|
||||||
def test_int_mesh_tunnel_no_args():
|
def test_int_mesh_tunnel_no_args():
|
||||||
"""Test mesh-tunnel without any args"""
|
"""Test mesh-tunnel without any args"""
|
||||||
return_value, out = subprocess.getstatusoutput('mesh-tunnel')
|
return_value, out = subprocess.getstatusoutput("mesh-tunnel")
|
||||||
assert re.match(r'usage: mesh-tunnel', out)
|
assert re.match(r"usage: mesh-tunnel", out)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.int
|
@pytest.mark.int
|
||||||
def test_int_version():
|
def test_int_version():
|
||||||
"""Test '--version'."""
|
"""Test '--version'."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --version')
|
return_value, out = subprocess.getstatusoutput("meshtastic --version")
|
||||||
assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
|
assert re.match(r"[0-9]+\.[0-9]+\.[0-9]", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.int
|
@pytest.mark.int
|
||||||
def test_int_help():
|
def test_int_help():
|
||||||
"""Test '--help'."""
|
"""Test '--help'."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --help')
|
return_value, out = subprocess.getstatusoutput("meshtastic --help")
|
||||||
assert re.match(r'usage: meshtastic ', out)
|
assert re.match(r"usage: meshtastic ", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.int
|
@pytest.mark.int
|
||||||
def test_int_support():
|
def test_int_support():
|
||||||
"""Test '--support'."""
|
"""Test '--support'."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --support')
|
return_value, out = subprocess.getstatusoutput("meshtastic --support")
|
||||||
assert re.search(r'System', out)
|
assert re.search(r"System", out)
|
||||||
assert re.search(r'Python', out)
|
assert re.search(r"Python", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,18 @@
|
|||||||
"""Meshtastic unit tests for mesh_interface.py"""
|
"""Meshtastic unit tests for mesh_interface.py"""
|
||||||
|
|
||||||
import re
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from unittest.mock import patch, MagicMock
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from .. import mesh_pb2
|
||||||
|
from ..__init__ import BROADCAST_ADDR, LOCAL_ADDR
|
||||||
from ..mesh_interface import MeshInterface
|
from ..mesh_interface import MeshInterface
|
||||||
from ..node import Node
|
from ..node import Node
|
||||||
from .. import mesh_pb2
|
|
||||||
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
|
# TODO
|
||||||
from ..radioconfig_pb2 import RadioConfig
|
# from ..config import Config
|
||||||
from ..util import Timeout
|
from ..util import Timeout
|
||||||
|
|
||||||
|
|
||||||
@@ -19,24 +21,24 @@ from ..util import Timeout
|
|||||||
def test_MeshInterface(capsys):
|
def test_MeshInterface(capsys):
|
||||||
"""Test that we can instantiate a MeshInterface"""
|
"""Test that we can instantiate a MeshInterface"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
anode = Node('foo', 'bar')
|
anode = Node("foo", "bar")
|
||||||
|
|
||||||
nodes = {
|
nodes = {
|
||||||
'!9388f81c': {
|
"!9388f81c": {
|
||||||
'num': 2475227164,
|
"num": 2475227164,
|
||||||
'user': {
|
"user": {
|
||||||
'id': '!9388f81c',
|
"id": "!9388f81c",
|
||||||
'longName': 'Unknown f81c',
|
"longName": "Unknown f81c",
|
||||||
'shortName': '?1C',
|
"shortName": "?1C",
|
||||||
'macaddr': 'RBeTiPgc',
|
"macaddr": "RBeTiPgc",
|
||||||
'hwModel': 'TBEAM'
|
"hwModel": "TBEAM",
|
||||||
},
|
},
|
||||||
'position': {},
|
"position": {},
|
||||||
'lastHeard': 1640204888
|
"lastHeard": 1640204888,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iface.nodesByNum = {1: anode }
|
iface.nodesByNum = {1: anode}
|
||||||
iface.nodes = nodes
|
iface.nodes = nodes
|
||||||
|
|
||||||
myInfo = MagicMock()
|
myInfo = MagicMock()
|
||||||
@@ -45,15 +47,15 @@ def test_MeshInterface(capsys):
|
|||||||
iface.showInfo()
|
iface.showInfo()
|
||||||
iface.localNode.showInfo()
|
iface.localNode.showInfo()
|
||||||
iface.showNodes()
|
iface.showNodes()
|
||||||
iface.sendText('hello')
|
iface.sendText("hello")
|
||||||
iface.close()
|
iface.close()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
|
assert re.search(r"Owner: None \(None\)", out, re.MULTILINE)
|
||||||
assert re.search(r'Nodes', out, re.MULTILINE)
|
assert re.search(r"Nodes", out, re.MULTILINE)
|
||||||
assert re.search(r'Preferences', out, re.MULTILINE)
|
assert re.search(r"Preferences", out, re.MULTILINE)
|
||||||
assert re.search(r'Channels', out, re.MULTILINE)
|
assert re.search(r"Channels", out, re.MULTILINE)
|
||||||
assert re.search(r'Primary channel URL', out, re.MULTILINE)
|
assert re.search(r"Primary channel URL", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -64,7 +66,7 @@ def test_getMyUser(iface_with_nodes):
|
|||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
myuser = iface.getMyUser()
|
myuser = iface.getMyUser()
|
||||||
assert myuser is not None
|
assert myuser is not None
|
||||||
assert myuser["id"] == '!9388f81c'
|
assert myuser["id"] == "!9388f81c"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -74,7 +76,7 @@ def test_getLongName(iface_with_nodes):
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
mylongname = iface.getLongName()
|
mylongname = iface.getLongName()
|
||||||
assert mylongname == 'Unknown f81c'
|
assert mylongname == "Unknown f81c"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -84,7 +86,7 @@ def test_getShortName(iface_with_nodes):
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
myshortname = iface.getShortName()
|
myshortname = iface.getShortName()
|
||||||
assert myshortname == '?1C'
|
assert myshortname == "?1C"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -95,24 +97,24 @@ def test_handlePacketFromRadio_no_from(capsys):
|
|||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
iface._handlePacketFromRadio(meshPacket)
|
iface._handlePacketFromRadio(meshPacket)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Device returned a packet we sent, ignoring', out, re.MULTILINE)
|
assert re.search(r"Device returned a packet we sent, ignoring", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_globals")
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
def test_handlePacketFromRadio_with_a_portnum(caplog):
|
def test_handlePacketFromRadio_with_a_portnum(caplog):
|
||||||
"""Test _handlePacketFromRadio with a portnum
|
"""Test _handlePacketFromRadio with a portnum
|
||||||
Since we have an attribute called 'from', we cannot simply 'set' it.
|
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.
|
Had to implement a hack just to be able to test some code.
|
||||||
"""
|
"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
meshPacket.decoded.payload = b''
|
meshPacket.decoded.payload = b""
|
||||||
meshPacket.decoded.portnum = 1
|
meshPacket.decoded.portnum = 1
|
||||||
with caplog.at_level(logging.WARNING):
|
with caplog.at_level(logging.WARNING):
|
||||||
iface._handlePacketFromRadio(meshPacket, hack=True)
|
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
|
@pytest.mark.unit
|
||||||
@@ -121,10 +123,10 @@ def test_handlePacketFromRadio_no_portnum(caplog):
|
|||||||
"""Test _handlePacketFromRadio without a portnum"""
|
"""Test _handlePacketFromRadio without a portnum"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
meshPacket.decoded.payload = b''
|
meshPacket.decoded.payload = b""
|
||||||
with caplog.at_level(logging.WARNING):
|
with caplog.at_level(logging.WARNING):
|
||||||
iface._handlePacketFromRadio(meshPacket, hack=True)
|
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
|
@pytest.mark.unit
|
||||||
@@ -143,10 +145,10 @@ def test_getNode_not_local(caplog):
|
|||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
anode = MagicMock(autospec=Node)
|
anode = MagicMock(autospec=Node)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('meshtastic.node.Node', return_value=anode):
|
with patch("meshtastic.node.Node", return_value=anode):
|
||||||
another_node = iface.getNode('bar2')
|
another_node = iface.getNode("bar2")
|
||||||
assert another_node != iface.localNode
|
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
|
@pytest.mark.unit
|
||||||
@@ -156,14 +158,14 @@ def test_getNode_not_local_timeout(capsys):
|
|||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
anode = MagicMock(autospec=Node)
|
anode = MagicMock(autospec=Node)
|
||||||
anode.waitForConfig.return_value = False
|
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:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
iface.getNode('bar2')
|
iface.getNode("bar2")
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.match(r'Error: Timed out waiting for node config', out)
|
assert re.match(r"Error: Timed out waiting for node config", out)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -174,35 +176,37 @@ def test_sendPosition(caplog):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface.sendPosition()
|
iface.sendPosition()
|
||||||
iface.close()
|
iface.close()
|
||||||
assert re.search(r'p.time:', caplog.text, re.MULTILINE)
|
assert re.search(r"p.time:", caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
# TODO
|
||||||
@pytest.mark.usefixtures("reset_globals")
|
# @pytest.mark.unit
|
||||||
def test_close_with_heartbeatTimer(caplog):
|
# @pytest.mark.usefixtures("reset_globals")
|
||||||
"""Test close() with heartbeatTimer"""
|
# def test_close_with_heartbeatTimer(caplog):
|
||||||
iface = MeshInterface(noProto=True)
|
# """Test close() with heartbeatTimer"""
|
||||||
anode = Node('foo', 'bar')
|
# iface = MeshInterface(noProto=True)
|
||||||
radioConfig = RadioConfig()
|
# anode = Node('foo', 'bar')
|
||||||
radioConfig.preferences.phone_timeout_secs = 10
|
# aconfig = Config()
|
||||||
anode.radioConfig = radioConfig
|
# aonfig.preferences.phone_timeout_secs = 10
|
||||||
iface.localNode = anode
|
# anode.config = aconfig
|
||||||
assert iface.heartbeatTimer is None
|
# iface.localNode = anode
|
||||||
with caplog.at_level(logging.DEBUG):
|
# assert iface.heartbeatTimer is None
|
||||||
iface._startHeartbeat()
|
# with caplog.at_level(logging.DEBUG):
|
||||||
assert iface.heartbeatTimer is not None
|
# iface._startHeartbeat()
|
||||||
iface.close()
|
# assert iface.heartbeatTimer is not None
|
||||||
|
# iface.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
# TODO
|
||||||
@pytest.mark.usefixtures("reset_globals")
|
# @pytest.mark.unit
|
||||||
def test_handleFromRadio_empty_payload(caplog):
|
# @pytest.mark.usefixtures("reset_globals")
|
||||||
"""Test _handleFromRadio"""
|
# def test_handleFromRadio_empty_payload(caplog):
|
||||||
iface = MeshInterface(noProto=True)
|
# """Test _handleFromRadio"""
|
||||||
with caplog.at_level(logging.DEBUG):
|
# iface = MeshInterface(noProto=True)
|
||||||
iface._handleFromRadio(b'')
|
# with caplog.at_level(logging.DEBUG):
|
||||||
iface.close()
|
# iface._handleFromRadio(b'')
|
||||||
assert re.search(r'Unexpected FromRadio payload', caplog.text, re.MULTILINE)
|
# iface.close()
|
||||||
|
# assert re.search(r'Unexpected FromRadio payload', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -213,22 +217,21 @@ def test_handleFromRadio_with_my_info(caplog):
|
|||||||
# It "translates" to this:
|
# It "translates" to this:
|
||||||
# my_info {
|
# my_info {
|
||||||
# my_node_num: 682584012
|
# my_node_num: 682584012
|
||||||
# num_bands: 13
|
|
||||||
# firmware_version: "1.2.49.5354c49"
|
# firmware_version: "1.2.49.5354c49"
|
||||||
# reboot_count: 13
|
# reboot_count: 13
|
||||||
# bitrate: 17.088470458984375
|
# bitrate: 17.088470458984375
|
||||||
# message_timeout_msec: 300000
|
# message_timeout_msec: 300000
|
||||||
# min_app_version: 20200
|
# min_app_version: 20200
|
||||||
# max_channels: 8
|
# 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)
|
iface = MeshInterface(noProto=True)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface._handleFromRadio(from_radio_bytes)
|
iface._handleFromRadio(from_radio_bytes)
|
||||||
iface.close()
|
iface.close()
|
||||||
assert re.search(r'Received myinfo', caplog.text, re.MULTILINE)
|
assert re.search(r"Received myinfo", caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'num_bands: 13', caplog.text, re.MULTILINE)
|
assert re.search(r"max_channels: 8", caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'max_channels: 8', caplog.text, re.MULTILINE)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -255,16 +258,16 @@ def test_handleFromRadio_with_node_info(caplog, capsys):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface._startConfig()
|
iface._startConfig()
|
||||||
iface._handleFromRadio(from_radio_bytes)
|
iface._handleFromRadio(from_radio_bytes)
|
||||||
assert re.search(r'Received nodeinfo', 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"682584012", caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'HELTEC_V2_1', caplog.text, re.MULTILINE)
|
assert re.search(r"HELTEC_V2_1", caplog.text, re.MULTILINE)
|
||||||
# validate some of showNodes() output
|
# validate some of showNodes() output
|
||||||
iface.showNodes()
|
iface.showNodes()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r' 1 ', out, re.MULTILINE)
|
assert re.search(r" 1 ", out, re.MULTILINE)
|
||||||
assert re.search(r'│ Unknown 67cc │ ', 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 re.search(r"│ !28af67cc │ N/A │ N/A │ N/A", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -279,16 +282,16 @@ def test_handleFromRadio_with_node_info_tbeam1(caplog, capsys):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface._startConfig()
|
iface._startConfig()
|
||||||
iface._handleFromRadio(from_radio_bytes)
|
iface._handleFromRadio(from_radio_bytes)
|
||||||
assert re.search(r'Received nodeinfo', 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"TBeam 1", caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'2127707136', caplog.text, re.MULTILINE)
|
assert re.search(r"2127707136", caplog.text, re.MULTILINE)
|
||||||
# validate some of showNodes() output
|
# validate some of showNodes() output
|
||||||
iface.showNodes()
|
iface.showNodes()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r' 1 ', out, re.MULTILINE)
|
assert re.search(r" 1 ", out, re.MULTILINE)
|
||||||
assert re.search(r'│ TBeam 1 │ ', out, re.MULTILINE)
|
assert re.search(r"│ TBeam 1 │ ", out, re.MULTILINE)
|
||||||
assert re.search(r'│ !7ed23c00 │', out, re.MULTILINE)
|
assert re.search(r"│ !7ed23c00 │", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -310,8 +313,8 @@ def test_MeshInterface_sendToRadioImpl(caplog):
|
|||||||
"""Test _sendToRadioImp()"""
|
"""Test _sendToRadioImp()"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface._sendToRadioImpl('foo')
|
iface._sendToRadioImpl("foo")
|
||||||
assert re.search(r'Subclass must provide toradio', caplog.text, re.MULTILINE)
|
assert re.search(r"Subclass must provide toradio", caplog.text, re.MULTILINE)
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -321,8 +324,8 @@ def test_MeshInterface_sendToRadio_no_proto(caplog):
|
|||||||
"""Test sendToRadio()"""
|
"""Test sendToRadio()"""
|
||||||
iface = MeshInterface()
|
iface = MeshInterface()
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface._sendToRadioImpl('foo')
|
iface._sendToRadioImpl("foo")
|
||||||
assert re.search(r'Subclass must provide toradio', caplog.text, re.MULTILINE)
|
assert re.search(r"Subclass must provide toradio", caplog.text, re.MULTILINE)
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -331,22 +334,22 @@ def test_MeshInterface_sendToRadio_no_proto(caplog):
|
|||||||
def test_sendData_too_long(caplog):
|
def test_sendData_too_long(caplog):
|
||||||
"""Test when data payload is too big"""
|
"""Test when data payload is too big"""
|
||||||
iface = MeshInterface(noProto=True)
|
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 caplog.at_level(logging.DEBUG):
|
||||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
iface.sendData(some_large_text)
|
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
|
assert pytest_wrapped_e.type == Exception
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
@@ -357,10 +360,10 @@ def test_sendData_unknown_app(capsys):
|
|||||||
"""Test sendData when unknown app"""
|
"""Test sendData when unknown app"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
iface.sendData(b'hello', portNum=0)
|
iface.sendData(b"hello", portNum=0)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: A non-zero port number', out, re.MULTILINE)
|
assert re.search(r"Warning: A non-zero port number", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
|
||||||
@@ -372,9 +375,9 @@ def test_sendPosition_with_a_position(caplog):
|
|||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface.sendPosition(latitude=40.8, longitude=-111.86, altitude=201)
|
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.latitude_i:408", caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'p.longitude_i:-11186', 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.altitude:201", caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -383,10 +386,10 @@ def test_sendPacket_with_no_destination(capsys):
|
|||||||
"""Test _sendPacket()"""
|
"""Test _sendPacket()"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
iface._sendPacket(b'', destinationId=None)
|
iface._sendPacket(b"", destinationId=None)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: destinationId must not be None', out, re.MULTILINE)
|
assert re.search(r"Warning: destinationId must not be None", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
|
||||||
@@ -399,7 +402,7 @@ def test_sendPacket_with_destination_as_int(caplog):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
iface._sendPacket(meshPacket, destinationId=123)
|
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
|
@pytest.mark.unit
|
||||||
@@ -409,8 +412,8 @@ def test_sendPacket_with_destination_starting_with_a_bang(caplog):
|
|||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
iface._sendPacket(meshPacket, destinationId='!1234')
|
iface._sendPacket(meshPacket, destinationId="!1234")
|
||||||
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
|
@pytest.mark.unit
|
||||||
@@ -421,7 +424,7 @@ def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
iface._sendPacket(meshPacket, destinationId=BROADCAST_ADDR)
|
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
|
@pytest.mark.unit
|
||||||
@@ -433,8 +436,8 @@ def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys):
|
|||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
|
iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: No myInfo', out, re.MULTILINE)
|
assert re.search(r"Warning: No myInfo", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
|
||||||
@@ -450,7 +453,7 @@ def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
|
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
|
@pytest.mark.unit
|
||||||
@@ -460,12 +463,12 @@ def test_sendPacket_with_destination_is_blank_with_nodes(capsys, iface_with_node
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
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.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.match(r'Warning: NodeId not found in DB', out, re.MULTILINE)
|
assert re.match(r"Warning: NodeId not found in DB", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -476,8 +479,8 @@ def test_sendPacket_with_destination_is_blank_without_nodes(caplog, iface_with_n
|
|||||||
iface.nodes = None
|
iface.nodes = None
|
||||||
meshPacket = mesh_pb2.MeshPacket()
|
meshPacket = mesh_pb2.MeshPacket()
|
||||||
with caplog.at_level(logging.WARNING):
|
with caplog.at_level(logging.WARNING):
|
||||||
iface._sendPacket(meshPacket, destinationId='')
|
iface._sendPacket(meshPacket, destinationId="")
|
||||||
assert re.search(r'Warning: There were no self.nodes.', caplog.text, re.MULTILINE)
|
assert re.search(r"Warning: There were no self.nodes.", caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -486,7 +489,7 @@ def test_getMyNodeInfo():
|
|||||||
"""Test getMyNodeInfo()"""
|
"""Test getMyNodeInfo()"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
anode = iface.getNode(LOCAL_ADDR)
|
anode = iface.getNode(LOCAL_ADDR)
|
||||||
iface.nodesByNum = {1: anode }
|
iface.nodesByNum = {1: anode}
|
||||||
assert iface.nodesByNum.get(1) == anode
|
assert iface.nodesByNum.get(1) == anode
|
||||||
myInfo = MagicMock()
|
myInfo = MagicMock()
|
||||||
iface.myInfo = myInfo
|
iface.myInfo = myInfo
|
||||||
@@ -506,8 +509,10 @@ def test_generatePacketId(capsys):
|
|||||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
iface._generatePacketId()
|
iface._generatePacketId()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Not connected yet, can not generate packet', out, re.MULTILINE)
|
assert re.search(
|
||||||
assert err == ''
|
r"Not connected yet, can not generate packet", out, re.MULTILINE
|
||||||
|
)
|
||||||
|
assert err == ""
|
||||||
assert pytest_wrapped_e.type == Exception
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
|
||||||
|
|
||||||
@@ -538,10 +543,12 @@ def test_fixupPosition():
|
|||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
pos = {"latitudeI": 1010000000, "longitudeI": 1020000000}
|
pos = {"latitudeI": 1010000000, "longitudeI": 1020000000}
|
||||||
newpos = iface._fixupPosition(pos)
|
newpos = iface._fixupPosition(pos)
|
||||||
assert newpos == {"latitude": 101.0,
|
assert newpos == {
|
||||||
"latitudeI": 1010000000,
|
"latitude": 101.0,
|
||||||
"longitude": 102.0,
|
"latitudeI": 1010000000,
|
||||||
"longitudeI": 1020000000}
|
"longitude": 102.0,
|
||||||
|
"longitudeI": 1020000000,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -551,7 +558,7 @@ def test_nodeNumToId(iface_with_nodes):
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
someid = iface._nodeNumToId(2475227164)
|
someid = iface._nodeNumToId(2475227164)
|
||||||
assert someid == '!9388f81c'
|
assert someid == "!9388f81c"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -570,8 +577,8 @@ def test_nodeNumToId_to_all(iface_with_nodes):
|
|||||||
"""Test _nodeNumToId()"""
|
"""Test _nodeNumToId()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
someid = iface._nodeNumToId(0xffffffff)
|
someid = iface._nodeNumToId(0xFFFFFFFF)
|
||||||
assert someid == '^all'
|
assert someid == "^all"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -581,7 +588,7 @@ def test_getOrCreateByNum_minimal(iface_with_nodes):
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
tmp = iface._getOrCreateByNum(123)
|
tmp = iface._getOrCreateByNum(123)
|
||||||
assert tmp == {'num': 123}
|
assert tmp == {"num": 123}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -591,7 +598,7 @@ def test_getOrCreateByNum_not_found(iface_with_nodes):
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
iface._getOrCreateByNum(0xffffffff)
|
iface._getOrCreateByNum(0xFFFFFFFF)
|
||||||
assert pytest_wrapped_e.type == Exception
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
|
||||||
|
|
||||||
@@ -602,14 +609,15 @@ def test_getOrCreateByNum(iface_with_nodes):
|
|||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
tmp = iface._getOrCreateByNum(2475227164)
|
tmp = iface._getOrCreateByNum(2475227164)
|
||||||
assert tmp['num'] == 2475227164
|
assert tmp["num"] == 2475227164
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
# TODO
|
||||||
def test_enter():
|
# @pytest.mark.unit
|
||||||
"""Test __enter__()"""
|
# def test_enter():
|
||||||
iface = MeshInterface(noProto=True)
|
# """Test __enter__()"""
|
||||||
assert iface == iface.__enter__()
|
# iface = MeshInterface(noProto=True)
|
||||||
|
# assert iface == iface.__enter__()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -617,9 +625,13 @@ def test_exit_with_exception(caplog):
|
|||||||
"""Test __exit__()"""
|
"""Test __exit__()"""
|
||||||
iface = MeshInterface(noProto=True)
|
iface = MeshInterface(noProto=True)
|
||||||
with caplog.at_level(logging.ERROR):
|
with caplog.at_level(logging.ERROR):
|
||||||
iface.__exit__('foo', 'bar', 'baz')
|
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(
|
||||||
assert re.search(r'Traceback: baz', caplog.text, re.MULTILINE)
|
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
|
@pytest.mark.unit
|
||||||
@@ -643,8 +655,10 @@ def test_waitForConfig(capsys):
|
|||||||
iface.waitForConfig()
|
iface.waitForConfig()
|
||||||
assert pytest_wrapped_e.type == Exception
|
assert pytest_wrapped_e.type == Exception
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Exception: Timed out waiting for interface config', err, re.MULTILINE)
|
assert re.search(
|
||||||
assert out == ''
|
r"Exception: Timed out waiting for interface config", err, re.MULTILINE
|
||||||
|
)
|
||||||
|
assert out == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -656,8 +670,8 @@ def test_waitConnected_raises_an_exception(capsys):
|
|||||||
iface._waitConnected(0.01)
|
iface._waitConnected(0.01)
|
||||||
assert pytest_wrapped_e.type == Exception
|
assert pytest_wrapped_e.type == Exception
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'warn about something', err, re.MULTILINE)
|
assert re.search(r"warn about something", err, re.MULTILINE)
|
||||||
assert out == ''
|
assert out == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -668,5 +682,5 @@ def test_waitConnected_isConnected_timeout(capsys):
|
|||||||
iface._waitConnected(0.01)
|
iface._waitConnected(0.01)
|
||||||
assert pytest_wrapped_e.type == Exception
|
assert pytest_wrapped_e.type == Exception
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'warn about something', err, re.MULTILINE)
|
assert re.search(r"warn about something", err, re.MULTILINE)
|
||||||
assert out == ''
|
assert out == ""
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from unittest.mock import patch, MagicMock
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..remote_hardware import RemoteHardwareClient, onGPIOreceive
|
from ..remote_hardware import RemoteHardwareClient, onGPIOreceive
|
||||||
@@ -23,25 +23,25 @@ def test_RemoteHardwareClient():
|
|||||||
def test_onGPIOreceive(capsys):
|
def test_onGPIOreceive(capsys):
|
||||||
"""Test onGPIOreceive"""
|
"""Test onGPIOreceive"""
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
packet = {'decoded': {'remotehw': {'typ': 'foo', 'gpioValue': '4096' }}}
|
packet = {"decoded": {"remotehw": {"type": "foo", "gpioValue": "4096"}}}
|
||||||
onGPIOreceive(packet, iface)
|
onGPIOreceive(packet, iface)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Received RemoteHardware', out)
|
assert re.search(r"Received RemoteHardware", out)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_RemoteHardwareClient_no_gpio_channel(capsys):
|
def test_RemoteHardwareClient_no_gpio_channel(capsys):
|
||||||
"""Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'"""
|
"""Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'"""
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
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.localNode.getChannelByName.return_value = None
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
RemoteHardwareClient(mo)
|
RemoteHardwareClient(mo)
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: No channel named', out)
|
assert re.search(r"Warning: No channel named", out)
|
||||||
assert err == ""
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ def test_readGPIOs(caplog):
|
|||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
rhw = RemoteHardwareClient(iface)
|
rhw = RemoteHardwareClient(iface)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
rhw.readGPIOs('0x10', 123)
|
rhw.readGPIOs("0x10", 123)
|
||||||
assert re.search(r'readGPIOs', caplog.text, re.MULTILINE)
|
assert re.search(r"readGPIOs", caplog.text, re.MULTILINE)
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ def test_writeGPIOs(caplog):
|
|||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
rhw = RemoteHardwareClient(iface)
|
rhw = RemoteHardwareClient(iface)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
rhw.writeGPIOs('0x10', 123, 1)
|
rhw.writeGPIOs("0x10", 123, 1)
|
||||||
assert re.search(r'writeGPIOs', caplog.text, re.MULTILINE)
|
assert re.search(r"writeGPIOs", caplog.text, re.MULTILINE)
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ def test_watchGPIOs(caplog):
|
|||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
rhw = RemoteHardwareClient(iface)
|
rhw = RemoteHardwareClient(iface)
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
rhw.watchGPIOs('0x10', 123)
|
rhw.watchGPIOs("0x10", 123)
|
||||||
assert re.search(r'watchGPIOs', caplog.text, re.MULTILINE)
|
assert re.search(r"watchGPIOs", caplog.text, re.MULTILINE)
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -82,11 +82,11 @@ def test_watchGPIOs(caplog):
|
|||||||
def test_sendHardware_no_nodeid(capsys):
|
def test_sendHardware_no_nodeid(capsys):
|
||||||
"""Test sending no nodeid to _sendHardware()"""
|
"""Test sending no nodeid to _sendHardware()"""
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
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:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
rhw = RemoteHardwareClient(mo)
|
rhw = RemoteHardwareClient(mo)
|
||||||
rhw._sendHardware(None, None)
|
rhw._sendHardware(None, None)
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: Must use a destination node ID', out)
|
assert re.search(r"Warning: Must use a destination node ID", out)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
"""Meshtastic unit tests for serial_interface.py"""
|
"""Meshtastic unit tests for serial_interface.py"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from unittest.mock import mock_open, patch
|
||||||
|
|
||||||
|
|
||||||
from unittest.mock import patch, mock_open
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..serial_interface import SerialInterface
|
from ..serial_interface import SerialInterface
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch("time.sleep")
|
@patch("time.sleep")
|
||||||
@patch("termios.tcsetattr")
|
@patch("termios.tcsetattr")
|
||||||
@patch("termios.tcgetattr")
|
@patch("termios.tcgetattr")
|
||||||
@patch("builtins.open", new_callable=mock_open, read_data="data")
|
@patch("builtins.open", new_callable=mock_open, read_data="data")
|
||||||
@patch('serial.Serial')
|
@patch("serial.Serial")
|
||||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
|
@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):
|
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"""
|
"""Test that we can instantiate a SerialInterface with a single port"""
|
||||||
iface = SerialInterface(noProto=True)
|
iface = SerialInterface(noProto=True)
|
||||||
iface.showInfo()
|
iface.showInfo()
|
||||||
@@ -28,15 +30,15 @@ def test_SerialInterface_single_port(mocked_findPorts, mocked_serial, mocked_ope
|
|||||||
mock_set.assert_called()
|
mock_set.assert_called()
|
||||||
mock_sleep.assert_called()
|
mock_sleep.assert_called()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Nodes in mesh', 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"Preferences", out, re.MULTILINE)
|
||||||
assert re.search(r'Channels', out, re.MULTILINE)
|
assert re.search(r"Channels", out, re.MULTILINE)
|
||||||
assert re.search(r'Primary channel', out, re.MULTILINE)
|
assert re.search(r"Primary channel", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('meshtastic.util.findPorts', return_value=[])
|
@patch("meshtastic.util.findPorts", return_value=[])
|
||||||
def test_SerialInterface_no_ports(mocked_findPorts, capsys):
|
def test_SerialInterface_no_ports(mocked_findPorts, capsys):
|
||||||
"""Test that we can instantiate a SerialInterface with no ports"""
|
"""Test that we can instantiate a SerialInterface with no ports"""
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
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.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: No Meshtastic devices detected', out, re.MULTILINE)
|
assert re.search(r"Warning: No Meshtastic devices detected", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@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):
|
def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
|
||||||
"""Test that we can instantiate a SerialInterface with two ports"""
|
"""Test that we can instantiate a SerialInterface with two ports"""
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
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.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: Multiple serial ports were detected', out, re.MULTILINE)
|
assert re.search(r"Warning: Multiple serial ports were detected", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"""Meshtastic smoke tests with a single device via USB"""
|
"""Meshtastic smoke tests with a single device via USB"""
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import platform
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Do not like using hard coded sleeps, but it probably makes
|
# 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
|
import pytest
|
||||||
|
|
||||||
from ..util import findPorts
|
from ..util import findPorts
|
||||||
@@ -19,7 +19,7 @@ PAUSE_AFTER_REBOOT = 7
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_reboot():
|
def test_smoke1_reboot():
|
||||||
"""Test reboot"""
|
"""Test reboot"""
|
||||||
return_value, _ = subprocess.getstatusoutput('meshtastic --reboot')
|
return_value, _ = subprocess.getstatusoutput("meshtastic --reboot")
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio to reset (10 seconds for the pause, and a few more seconds to be back up)
|
# pause for the radio to reset (10 seconds for the pause, and a few more seconds to be back up)
|
||||||
time.sleep(18)
|
time.sleep(18)
|
||||||
@@ -28,94 +28,100 @@ def test_smoke1_reboot():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_info():
|
def test_smoke1_info():
|
||||||
"""Test --info"""
|
"""Test --info"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'^Owner', out, re.MULTILINE)
|
assert re.search(r"^Owner", out, re.MULTILINE)
|
||||||
assert re.search(r'^My info', 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"^Nodes in mesh", out, re.MULTILINE)
|
||||||
assert re.search(r'^Preferences', out, re.MULTILINE)
|
assert re.search(r"^Preferences", out, re.MULTILINE)
|
||||||
assert re.search(r'^Channels', out, re.MULTILINE)
|
assert re.search(r"^Channels", out, re.MULTILINE)
|
||||||
assert re.search(r'^ PRIMARY', out, re.MULTILINE)
|
assert re.search(r"^ PRIMARY", out, re.MULTILINE)
|
||||||
assert re.search(r'^Primary channel URL', out, re.MULTILINE)
|
assert re.search(r"^Primary channel URL", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_sendping():
|
def test_smoke1_sendping():
|
||||||
"""Test --sendping"""
|
"""Test --sendping"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --sendping')
|
return_value, out = subprocess.getstatusoutput("meshtastic --sendping")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'^Sending ping message', out, re.MULTILINE)
|
assert re.search(r"^Sending ping message", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_get_with_invalid_setting():
|
def test_get_with_invalid_setting():
|
||||||
"""Test '--get a_bad_setting'."""
|
"""Test '--get a_bad_setting'."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --get a_bad_setting')
|
return_value, out = subprocess.getstatusoutput("meshtastic --get a_bad_setting")
|
||||||
assert re.search(r'Choices in sorted order', out)
|
assert re.search(r"Choices in sorted order", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_set_with_invalid_setting():
|
def test_set_with_invalid_setting():
|
||||||
"""Test '--set a_bad_setting'."""
|
"""Test '--set a_bad_setting'."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set a_bad_setting foo')
|
return_value, out = subprocess.getstatusoutput("meshtastic --set a_bad_setting foo")
|
||||||
assert re.search(r'Choices in sorted order', out)
|
assert re.search(r"Choices in sorted order", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_ch_set_with_invalid_settingpatch_find_ports():
|
def test_ch_set_with_invalid_settingpatch_find_ports():
|
||||||
"""Test '--ch-set with a_bad_setting'."""
|
"""Test '--ch-set with a_bad_setting'."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set invalid_setting foo --ch-index 0')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.search(r'Choices in sorted order', out)
|
"meshtastic --ch-set invalid_setting foo --ch-index 0"
|
||||||
|
)
|
||||||
|
assert re.search(r"Choices in sorted order", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_pos_fields():
|
def test_smoke1_pos_fields():
|
||||||
"""Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)"""
|
"""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')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
"meshtastic --pos-fields POS_ALTITUDE POS_ALT_MSL POS_BATTERY"
|
||||||
assert re.search(r'^Setting position fields to 35', out, re.MULTILINE)
|
)
|
||||||
|
assert re.match(r"Connected to radio", out)
|
||||||
|
assert re.search(r"^Setting position fields to 35", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --pos-fields')
|
return_value, out = subprocess.getstatusoutput("meshtastic --pos-fields")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'POS_ALTITUDE', out, re.MULTILINE)
|
assert re.search(r"POS_ALTITUDE", out, re.MULTILINE)
|
||||||
assert re.search(r'POS_ALT_MSL', out, re.MULTILINE)
|
assert re.search(r"POS_ALT_MSL", out, re.MULTILINE)
|
||||||
assert re.search(r'POS_BATTERY', out, re.MULTILINE)
|
assert re.search(r"POS_BATTERY", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_test_with_arg_but_no_hardware():
|
def test_smoke1_test_with_arg_but_no_hardware():
|
||||||
"""Test --test
|
"""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')
|
return_value, out = subprocess.getstatusoutput("meshtastic --test")
|
||||||
assert re.search(r'^Warning: Must have at least two devices', out, re.MULTILINE)
|
assert re.search(r"^Warning: Must have at least two devices", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_debug():
|
def test_smoke1_debug():
|
||||||
"""Test --debug"""
|
"""Test --debug"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info --debug')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info --debug")
|
||||||
assert re.search(r'^Owner', out, re.MULTILINE)
|
assert re.search(r"^Owner", out, re.MULTILINE)
|
||||||
assert re.search(r'^DEBUG file', out, re.MULTILINE)
|
assert re.search(r"^DEBUG file", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_seriallog_to_file():
|
def test_smoke1_seriallog_to_file():
|
||||||
"""Test --seriallog to a file creates a file"""
|
"""Test --seriallog to a file creates a file"""
|
||||||
filename = 'tmpoutput.txt'
|
filename = "tmpoutput.txt"
|
||||||
if os.path.exists(f"{filename}"):
|
if os.path.exists(f"{filename}"):
|
||||||
os.remove(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 os.path.exists(f"{filename}")
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
os.remove(f"{filename}")
|
os.remove(f"{filename}")
|
||||||
@@ -124,10 +130,10 @@ def test_smoke1_seriallog_to_file():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_qr():
|
def test_smoke1_qr():
|
||||||
"""Test --qr"""
|
"""Test --qr"""
|
||||||
filename = 'tmpqr'
|
filename = "tmpqr"
|
||||||
if os.path.exists(f"{filename}"):
|
if os.path.exists(f"{filename}"):
|
||||||
os.remove(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}")
|
assert os.path.exists(f"{filename}")
|
||||||
# not really testing that a valid qr code is created, just that the file size
|
# not really testing that a valid qr code is created, just that the file size
|
||||||
# is reasonably big enough for a qr code
|
# is reasonably big enough for a qr code
|
||||||
@@ -139,20 +145,20 @@ def test_smoke1_qr():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_nodes():
|
def test_smoke1_nodes():
|
||||||
"""Test --nodes"""
|
"""Test --nodes"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --nodes')
|
return_value, out = subprocess.getstatusoutput("meshtastic --nodes")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
if platform.system() != 'Windows':
|
if platform.system() != "Windows":
|
||||||
assert re.search(r' User ', out, re.MULTILINE)
|
assert re.search(r" User ", out, re.MULTILINE)
|
||||||
assert re.search(r' 1 ', out, re.MULTILINE)
|
assert re.search(r" 1 ", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_send_hello():
|
def test_smoke1_send_hello():
|
||||||
"""Test --sendtext hello"""
|
"""Test --sendtext hello"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --sendtext hello')
|
return_value, out = subprocess.getstatusoutput("meshtastic --sendtext hello")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'^Sending text message hello to \^all', out, re.MULTILINE)
|
assert re.search(r"^Sending text message hello to \^all", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@@ -160,59 +166,33 @@ def test_smoke1_send_hello():
|
|||||||
def test_smoke1_port():
|
def test_smoke1_port():
|
||||||
"""Test --port"""
|
"""Test --port"""
|
||||||
# first, get the ports
|
# first, get the ports
|
||||||
ports = findPorts()
|
ports = findPorts(True)
|
||||||
# hopefully there is just one
|
# hopefully there is just one
|
||||||
assert len(ports) == 1
|
assert len(ports) == 1
|
||||||
port = ports[0]
|
port = ports[0]
|
||||||
return_value, out = subprocess.getstatusoutput(f'meshtastic --port {port} --info')
|
return_value, out = subprocess.getstatusoutput(f"meshtastic --port {port} --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'^Owner', out, re.MULTILINE)
|
assert re.search(r"^Owner", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
|
||||||
def test_smoke1_set_is_router_true():
|
|
||||||
"""Test --set is_router true"""
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set is_router true')
|
|
||||||
assert re.match(r'Connected to radio', out)
|
|
||||||
assert re.search(r'^Set is_router to true', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
|
||||||
# pause for the radio
|
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
|
|
||||||
assert re.search(r'^is_router: True', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_set_location_info():
|
def test_smoke1_set_location_info():
|
||||||
"""Test --setlat, --setlon and --setalt """
|
"""Test --setlat, --setlon and --setalt"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
"meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337"
|
||||||
assert re.search(r'^Fixing altitude', out, re.MULTILINE)
|
)
|
||||||
assert re.search(r'^Fixing latitude', out, re.MULTILINE)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'^Fixing longitude', out, re.MULTILINE)
|
assert re.search(r"^Fixing altitude", out, re.MULTILINE)
|
||||||
|
assert re.search(r"^Fixing latitude", out, re.MULTILINE)
|
||||||
|
assert re.search(r"^Fixing longitude", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out2 = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out2 = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search(r'1337', out2, re.MULTILINE)
|
assert re.search(r"1337", out2, re.MULTILINE)
|
||||||
assert re.search(r'32.7767', out2, re.MULTILINE)
|
assert re.search(r"32.7767", out2, re.MULTILINE)
|
||||||
assert re.search(r'-96.797', out2, re.MULTILINE)
|
assert re.search(r"-96.797", out2, re.MULTILINE)
|
||||||
assert return_value == 0
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
|
||||||
def test_smoke1_set_is_router_false():
|
|
||||||
"""Test --set is_router false"""
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set is_router false')
|
|
||||||
assert re.match(r'Connected to radio', out)
|
|
||||||
assert re.search(r'^Set is_router to false', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
|
||||||
# pause for the radio
|
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
|
|
||||||
assert re.search(r'^is_router: False', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@@ -220,72 +200,80 @@ def test_smoke1_set_is_router_false():
|
|||||||
def test_smoke1_set_owner():
|
def test_smoke1_set_owner():
|
||||||
"""Test --set-owner name"""
|
"""Test --set-owner name"""
|
||||||
# make sure the owner is not Joe
|
# make sure the owner is not Joe
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Bob')
|
return_value, out = subprocess.getstatusoutput("meshtastic --set-owner Bob")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'^Setting device owner to Bob', out, re.MULTILINE)
|
assert re.search(r"^Setting device owner to Bob", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert not re.search(r'Owner: Joe', out, re.MULTILINE)
|
assert not re.search(r"Owner: Joe", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set-owner Joe')
|
return_value, out = subprocess.getstatusoutput("meshtastic --set-owner Joe")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'^Setting device owner to Joe', out, re.MULTILINE)
|
assert re.search(r"^Setting device owner to Joe", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search(r'Owner: Joe', out, re.MULTILINE)
|
assert re.search(r"Owner: Joe", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_set_team():
|
def test_smoke1_ch_set_modem_config():
|
||||||
"""Test --set-team """
|
"""Test --ch-set modem_config"""
|
||||||
# unset the team
|
return_value, out = subprocess.getstatusoutput(
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set-team CLEAR')
|
"meshtastic --ch-set modem_config MedFast"
|
||||||
assert re.match(r'Connected to radio', out)
|
)
|
||||||
assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
|
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)
|
||||||
|
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)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_REBOOT)
|
time.sleep(PAUSE_AFTER_REBOOT)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set-team CYAN')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
|
assert re.search(r"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'CYAN', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ch_values():
|
def test_smoke1_ch_values():
|
||||||
"""Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
|
"""Test --ch-vlongslow --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
|
||||||
--ch-shortslow, and --ch-shortfast arguments
|
--ch-shortslow, and --ch-shortfast arguments
|
||||||
"""
|
"""
|
||||||
exp = {
|
exp = {
|
||||||
'--ch-longslow': 'Bw125Cr48Sf4096',
|
"--ch-vlongslow": '{ "psk": "AQ==" }',
|
||||||
'--ch-longfast': 'Bw31_25Cr48Sf512',
|
"--ch-longslow": "LongSlow",
|
||||||
'--ch-mediumslow': 'Bw250Cr46Sf2048',
|
"--ch-longfast": "LongFast",
|
||||||
'--ch-mediumfast': 'Bw250Cr47Sf1024',
|
"--ch-medslow": "MedSlow",
|
||||||
# for some reason, this value does not show any modemConfig
|
"--ch-medfast": "MedFast",
|
||||||
'--ch-shortslow': '{ "psk',
|
"--ch-shortslow": "ShortSlow",
|
||||||
'--ch-shortfast': 'Bw500Cr45Sf128'
|
"--ch-shortfast": "ShortFast",
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, val in exp.items():
|
for key, val in exp.items():
|
||||||
print(key, val)
|
print(key, val)
|
||||||
return_value, out = subprocess.getstatusoutput(f'meshtastic {key}')
|
return_value, out = subprocess.getstatusoutput(f"meshtastic {key}")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
assert re.search(r"Writing modified channels to device", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio (might reboot)
|
# pause for the radio (might reboot)
|
||||||
time.sleep(PAUSE_AFTER_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 re.search(val, out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
@@ -295,132 +283,144 @@ def test_smoke1_ch_values():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ch_set_name():
|
def test_smoke1_ch_set_name():
|
||||||
"""Test --ch-set name"""
|
"""Test --ch-set name"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert not re.search(r'MyChannel', out, re.MULTILINE)
|
assert not re.search(r"MyChannel", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-set name MyChannel")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
|
assert re.search(r"Warning: Need to specify", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set name MyChannel --ch-index 0')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
"meshtastic --ch-set name MyChannel --ch-index 0"
|
||||||
assert re.search(r'^Set name to MyChannel', out, re.MULTILINE)
|
)
|
||||||
|
assert re.match(r"Connected to radio", out)
|
||||||
|
assert re.search(r"^Set name to MyChannel", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search(r'MyChannel', out, re.MULTILINE)
|
assert re.search(r"MyChannel", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ch_set_downlink_and_uplink():
|
def test_smoke1_ch_set_downlink_and_uplink():
|
||||||
"""Test -ch-set downlink_enabled X and --ch-set uplink_enabled X"""
|
"""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')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
"meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false"
|
||||||
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
|
)
|
||||||
|
assert re.match(r"Connected to radio", out)
|
||||||
|
assert re.search(r"Warning: Need to specify", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled false --ch-set uplink_enabled false --ch-index 0')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
"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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert not re.search(r'uplinkEnabled', out, re.MULTILINE)
|
assert not re.search(r"uplinkEnabled", out, re.MULTILINE)
|
||||||
assert not re.search(r'downlinkEnabled', out, re.MULTILINE)
|
assert not re.search(r"downlinkEnabled", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set downlink_enabled true --ch-set uplink_enabled true --ch-index 0')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
"meshtastic --ch-set downlink_enabled true --ch-set uplink_enabled true --ch-index 0"
|
||||||
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 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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search(r'uplinkEnabled', out, re.MULTILINE)
|
assert re.search(r"uplinkEnabled", out, re.MULTILINE)
|
||||||
assert re.search(r'downlinkEnabled', out, re.MULTILINE)
|
assert re.search(r"downlinkEnabled", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ch_add_and_ch_del():
|
def test_smoke1_ch_add_and_ch_del():
|
||||||
"""Test --ch-add"""
|
"""Test --ch-add"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing")
|
||||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
assert re.search(r"Writing modified channels to device", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'SECONDARY', out, re.MULTILINE)
|
assert re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert re.search(r'testing', out, re.MULTILINE)
|
assert re.search(r"testing", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-index 1 --ch-del')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-index 1 --ch-del")
|
||||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
assert re.search(r"Deleting channel 1", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_REBOOT)
|
time.sleep(PAUSE_AFTER_REBOOT)
|
||||||
# make sure the secondar channel is not there
|
# make sure the secondar channel is not there
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert not re.search(r'SECONDARY', out, re.MULTILINE)
|
assert not re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert not re.search(r'testing', out, re.MULTILINE)
|
assert not re.search(r"testing", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ch_enable_and_disable():
|
def test_smoke1_ch_enable_and_disable():
|
||||||
"""Test --ch-enable and --ch-disable"""
|
"""Test --ch-enable and --ch-disable"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing")
|
||||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
assert re.search(r"Writing modified channels to device", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'SECONDARY', out, re.MULTILINE)
|
assert re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert re.search(r'testing', out, re.MULTILINE)
|
assert re.search(r"testing", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
# ensure they need to specify a --ch-index
|
# 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
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'DISABLED', out, re.MULTILINE)
|
assert re.search(r"DISABLED", out, re.MULTILINE)
|
||||||
assert re.search(r'testing', out, re.MULTILINE)
|
assert re.search(r"testing", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'SECONDARY', out, re.MULTILINE)
|
assert re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert re.search(r'testing', out, re.MULTILINE)
|
assert re.search(r"testing", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
@@ -429,32 +429,32 @@ def test_smoke1_ch_enable_and_disable():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ch_del_a_disabled_non_primary_channel():
|
def test_smoke1_ch_del_a_disabled_non_primary_channel():
|
||||||
"""Test --ch-del will work on 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')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing")
|
||||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
assert re.search(r"Writing modified channels to device", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'SECONDARY', out, re.MULTILINE)
|
assert re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert re.search(r'testing', out, re.MULTILINE)
|
assert re.search(r"testing", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
# ensure they need to specify a --ch-index
|
# 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
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert not re.search(r'DISABLED', out, re.MULTILINE)
|
assert not re.search(r"DISABLED", out, re.MULTILINE)
|
||||||
assert not re.search(r'SECONDARY', out, re.MULTILINE)
|
assert not re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert not re.search(r'testing', out, re.MULTILINE)
|
assert not re.search(r"testing", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
@@ -463,8 +463,8 @@ def test_smoke1_ch_del_a_disabled_non_primary_channel():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_attempt_to_delete_primary_channel():
|
def test_smoke1_attempt_to_delete_primary_channel():
|
||||||
"""Test that we cannot delete the PRIMARY channel."""
|
"""Test that we cannot delete the PRIMARY channel."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 0')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 0")
|
||||||
assert re.search(r'Warning: Cannot delete primary channel', out, re.MULTILINE)
|
assert re.search(r"Warning: Cannot delete primary channel", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
@@ -473,8 +473,10 @@ def test_smoke1_attempt_to_delete_primary_channel():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_attempt_to_disable_primary_channel():
|
def test_smoke1_attempt_to_disable_primary_channel():
|
||||||
"""Test that we cannot disable the PRIMARY channel."""
|
"""Test that we cannot disable the PRIMARY channel."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-disable --ch-index 0')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
|
"meshtastic --ch-disable --ch-index 0"
|
||||||
|
)
|
||||||
|
assert re.search(r"Warning: Cannot enable", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
@@ -483,8 +485,10 @@ def test_smoke1_attempt_to_disable_primary_channel():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_attempt_to_enable_primary_channel():
|
def test_smoke1_attempt_to_enable_primary_channel():
|
||||||
"""Test that we cannot enable the PRIMARY channel."""
|
"""Test that we cannot enable the PRIMARY channel."""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-enable --ch-index 0')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
|
"meshtastic --ch-enable --ch-index 0"
|
||||||
|
)
|
||||||
|
assert re.search(r"Warning: Cannot enable", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
@@ -493,42 +497,42 @@ def test_smoke1_attempt_to_enable_primary_channel():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ensure_ch_del_second_of_three_channels():
|
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."""
|
"""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')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing1")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'SECONDARY', out, re.MULTILINE)
|
assert re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert re.search(r'testing1', out, re.MULTILINE)
|
assert re.search(r"testing1", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing2')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing2")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'testing2', out, re.MULTILINE)
|
assert re.search(r"testing2", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
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 re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'testing2', out, re.MULTILINE)
|
assert re.search(r"testing2", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
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 re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
@@ -537,91 +541,69 @@ def test_smoke1_ensure_ch_del_second_of_three_channels():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_ensure_ch_del_third_of_three_channels():
|
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."""
|
"""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')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing1")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'SECONDARY', out, re.MULTILINE)
|
assert re.search(r"SECONDARY", out, re.MULTILINE)
|
||||||
assert re.search(r'testing1', out, re.MULTILINE)
|
assert re.search(r"testing1", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-add testing2')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-add testing2")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'testing2', out, re.MULTILINE)
|
assert re.search(r"testing2", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-del --ch-index 2')
|
return_value, out = subprocess.getstatusoutput("meshtastic --ch-del --ch-index 2")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search(r'testing1', out, re.MULTILINE)
|
assert re.search(r"testing1", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
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 re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
|
||||||
def test_smoke1_ch_set_modem_config():
|
|
||||||
"""Test --ch-set modem_config"""
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512')
|
|
||||||
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
|
|
||||||
assert return_value == 1
|
|
||||||
# pause for the radio
|
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
|
||||||
assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
|
||||||
# pause for the radio
|
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512 --ch-index 0')
|
|
||||||
assert re.match(r'Connected to radio', out)
|
|
||||||
assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
|
||||||
# pause for the radio
|
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
|
||||||
assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
|
|
||||||
assert return_value == 0
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_seturl_default():
|
def test_smoke1_seturl_default():
|
||||||
"""Test --seturl with default value"""
|
"""Test --seturl with default value"""
|
||||||
# set some channel value so we no longer have a default channel
|
# 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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
# ensure we no longer have a default primary channel
|
# ensure we no longer have a default primary channel
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert not re.search('CgUYAyIBAQ', out, re.MULTILINE)
|
assert not re.search("CgUYAyIBAQ", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
|
url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
|
||||||
return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
|
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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search('CgUYAyIBAQ', out, re.MULTILINE)
|
assert re.search("CgUYAyIBAQ", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@@ -631,8 +613,8 @@ def test_smoke1_seturl_invalid_url():
|
|||||||
# Note: This url is no longer a valid url.
|
# Note: This url is no longer a valid url.
|
||||||
url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ="
|
url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ="
|
||||||
return_value, out = subprocess.getstatusoutput(f"meshtastic --seturl {url}")
|
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 re.search('Warning: There were no settings', out, re.MULTILINE)
|
assert re.search("Warning: There were no settings", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
@@ -641,19 +623,18 @@ def test_smoke1_seturl_invalid_url():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_configure():
|
def test_smoke1_configure():
|
||||||
"""Test --configure"""
|
"""Test --configure"""
|
||||||
_ , out = subprocess.getstatusoutput(f"meshtastic --configure example_config.yaml")
|
_, out = subprocess.getstatusoutput(f"meshtastic --configure example_config.yaml")
|
||||||
assert re.match(r'Connected to radio', out)
|
assert re.match(r"Connected to radio", out)
|
||||||
assert re.search('^Setting device owner to Bob TBeam', out, re.MULTILINE)
|
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 altitude at 304 meters", out, re.MULTILINE)
|
||||||
assert re.search('^Fixing latitude at 35.8', 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("^Fixing longitude at -93.8", out, re.MULTILINE)
|
||||||
assert re.search('^Setting device position', 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 region to 1", out, re.MULTILINE)
|
||||||
assert re.search('^Set is_always_powered to true', 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 screen_on_secs to 31536000', out, re.MULTILINE)
|
assert re.search("^Set wait_bluetooth_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)
|
||||||
assert re.search('^Writing modified preferences to device', out, re.MULTILINE)
|
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_REBOOT)
|
time.sleep(PAUSE_AFTER_REBOOT)
|
||||||
|
|
||||||
@@ -661,41 +642,47 @@ def test_smoke1_configure():
|
|||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_set_ham():
|
def test_smoke1_set_ham():
|
||||||
"""Test --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')
|
return_value, out = subprocess.getstatusoutput("meshtastic --set-ham KI1234")
|
||||||
assert re.search(r'Setting Ham ID', out, re.MULTILINE)
|
assert re.search(r"Setting Ham ID", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_REBOOT)
|
time.sleep(PAUSE_AFTER_REBOOT)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search(r'Owner: KI1234', out, re.MULTILINE)
|
assert re.search(r"Owner: KI1234", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_set_wifi_settings():
|
def test_smoke1_set_wifi_settings():
|
||||||
"""Test --set wifi_ssid and --set wifi_password"""
|
"""Test --set wifi_ssid and --set wifi_password"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set wifi_ssid "some_ssid" --set wifi_password "temp1234"')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
'meshtastic --set wifi_ssid "some_ssid" --set wifi_password "temp1234"'
|
||||||
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 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
|
assert return_value == 0
|
||||||
# pause for the radio
|
# pause for the radio
|
||||||
time.sleep(PAUSE_AFTER_COMMAND)
|
time.sleep(PAUSE_AFTER_COMMAND)
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --get wifi_ssid --get wifi_password')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.search(r'^wifi_ssid: some_ssid', out, re.MULTILINE)
|
"meshtastic --get wifi_ssid --get wifi_password"
|
||||||
assert re.search(r'^wifi_password: sekrit', out, re.MULTILINE)
|
)
|
||||||
|
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
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke1
|
@pytest.mark.smoke1
|
||||||
def test_smoke1_factory_reset():
|
def test_smoke1_factory_reset():
|
||||||
"""Test factory reset"""
|
"""Test factory reset"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --set factory_reset true')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.match(r'Connected to radio', out)
|
"meshtastic --set factory_reset true"
|
||||||
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 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
|
assert return_value == 0
|
||||||
# NOTE: The radio may not be responsive after this, may need to do a manual reboot
|
# NOTE: The radio may not be responsive after this, may need to do a manual reboot
|
||||||
# by pressing the button
|
# by pressing the button
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import pytest
|
|||||||
@pytest.mark.smoke2
|
@pytest.mark.smoke2
|
||||||
def test_smoke2_info():
|
def test_smoke2_info():
|
||||||
"""Test --info with 2 devices connected serially"""
|
"""Test --info with 2 devices connected serially"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
return_value, out = subprocess.getstatusoutput("meshtastic --info")
|
||||||
assert re.search(r'Warning: Multiple', out, re.MULTILINE)
|
assert re.search(r"Warning: Multiple", out, re.MULTILINE)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.smoke2
|
@pytest.mark.smoke2
|
||||||
def test_smoke2_test():
|
def test_smoke2_test():
|
||||||
"""Test --test"""
|
"""Test --test"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --test')
|
return_value, out = subprocess.getstatusoutput("meshtastic --test")
|
||||||
assert re.search(r'Writing serial debugging', out, re.MULTILINE)
|
assert re.search(r"Writing serial debugging", out, re.MULTILINE)
|
||||||
assert re.search(r'Ports opened', out, re.MULTILINE)
|
assert re.search(r"Ports opened", out, re.MULTILINE)
|
||||||
assert re.search(r'Running 5 tests', out, re.MULTILINE)
|
assert re.search(r"Running 5 tests", out, re.MULTILINE)
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ import pytest
|
|||||||
@pytest.mark.smokewifi
|
@pytest.mark.smokewifi
|
||||||
def test_smokewifi_info():
|
def test_smokewifi_info():
|
||||||
"""Test --info"""
|
"""Test --info"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic --info --host meshtastic.local')
|
return_value, out = subprocess.getstatusoutput(
|
||||||
assert re.search(r'^Owner', out, re.MULTILINE)
|
"meshtastic --info --host meshtastic.local"
|
||||||
assert re.search(r'^My info', out, re.MULTILINE)
|
)
|
||||||
assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
|
assert re.search(r"^Owner", out, re.MULTILINE)
|
||||||
assert re.search(r'^Preferences', out, re.MULTILINE)
|
assert re.search(r"^My info", out, re.MULTILINE)
|
||||||
assert re.search(r'^Channels', out, re.MULTILINE)
|
assert re.search(r"^Nodes in mesh", out, re.MULTILINE)
|
||||||
assert re.search(r'^ PRIMARY', out, re.MULTILINE)
|
assert re.search(r"^Preferences", out, re.MULTILINE)
|
||||||
assert re.search(r'^Primary channel URL', 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
|
assert return_value == 0
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,14 @@
|
|||||||
"""Meshtastic unit tests for stream_interface.py"""
|
"""Meshtastic unit tests for stream_interface.py"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
|
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..stream_interface import StreamInterface
|
from ..stream_interface import StreamInterface
|
||||||
|
|
||||||
|
# import re
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_StreamInterface():
|
def test_StreamInterface():
|
||||||
@@ -22,10 +23,10 @@ def test_StreamInterface():
|
|||||||
@pytest.mark.usefixtures("reset_globals")
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
def test_StreamInterface_with_noProto(caplog):
|
def test_StreamInterface_with_noProto(caplog):
|
||||||
"""Test that we can instantiate a StreamInterface based on nonProto
|
"""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()
|
stream = MagicMock()
|
||||||
test_data = b'hello'
|
test_data = b"hello"
|
||||||
stream.read.return_value = test_data
|
stream.read.return_value = test_data
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
iface = StreamInterface(noProto=True, connectNow=False)
|
iface = StreamInterface(noProto=True, connectNow=False)
|
||||||
@@ -35,48 +36,49 @@ def test_StreamInterface_with_noProto(caplog):
|
|||||||
assert data == test_data
|
assert data == test_data
|
||||||
|
|
||||||
|
|
||||||
## Note: This takes a bit, so moving from unit to slow
|
# TODO
|
||||||
## Tip: If you want to see the print output, run with '-s' flag:
|
### Note: This takes a bit, so moving from unit to slow
|
||||||
## pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
|
### Tip: If you want to see the print output, run with '-s' flag:
|
||||||
@pytest.mark.unitslow
|
### pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
|
||||||
@pytest.mark.usefixtures("reset_globals")
|
# @pytest.mark.unitslow
|
||||||
def test_sendToRadioImpl(caplog):
|
# @pytest.mark.usefixtures("reset_globals")
|
||||||
"""Test _sendToRadioImpl()"""
|
# def test_sendToRadioImpl(caplog):
|
||||||
|
# """Test _sendToRadioImpl()"""
|
||||||
# def add_header(b):
|
#
|
||||||
# """Add header stuffs for radio"""
|
## def add_header(b):
|
||||||
# bufLen = len(b)
|
## """Add header stuffs for radio"""
|
||||||
# header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
## bufLen = len(b)
|
||||||
# return header + b
|
## header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
||||||
|
## return header + b
|
||||||
# captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
|
#
|
||||||
raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
|
# # captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
|
||||||
raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
|
# raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
|
||||||
# pylint: disable=C0301
|
# raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
|
||||||
raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
|
# # pylint: disable=C0301
|
||||||
raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
|
# raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
|
||||||
# pylint: disable=C0301
|
# raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
|
||||||
raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
|
# # pylint: disable=C0301
|
||||||
# pylint: disable=C0301
|
# raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
|
||||||
raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
|
# # pylint: disable=C0301
|
||||||
# pylint: disable=C0301
|
# raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
|
||||||
raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
|
# # pylint: disable=C0301
|
||||||
raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
|
# raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
|
||||||
raw_blank = b''
|
# raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
|
||||||
|
# raw_blank = b''
|
||||||
test_data = b'hello'
|
#
|
||||||
stream = MagicMock()
|
# test_data = b'hello'
|
||||||
#stream.read.return_value = add_header(test_data)
|
# stream = MagicMock()
|
||||||
stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
|
# #stream.read.return_value = add_header(test_data)
|
||||||
raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
|
# stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
|
||||||
raw_blank, raw_blank]
|
# raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
|
||||||
toRadio = MagicMock()
|
# raw_blank, raw_blank]
|
||||||
toRadio.SerializeToString.return_value = test_data
|
# toRadio = MagicMock()
|
||||||
with caplog.at_level(logging.DEBUG):
|
# toRadio.SerializeToString.return_value = test_data
|
||||||
iface = StreamInterface(noProto=True, connectNow=False)
|
# with caplog.at_level(logging.DEBUG):
|
||||||
iface.stream = stream
|
# iface = StreamInterface(noProto=True, connectNow=False)
|
||||||
iface.connect()
|
# iface.stream = stream
|
||||||
iface._sendToRadioImpl(toRadio)
|
# iface.connect()
|
||||||
assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
# iface._sendToRadioImpl(toRadio)
|
||||||
assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
# assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
# assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
||||||
|
# assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"""Meshtastic unit tests for tcp_interface.py"""
|
"""Meshtastic unit tests for tcp_interface.py"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..tcp_interface import TCPInterface
|
from ..tcp_interface import TCPInterface
|
||||||
@@ -11,18 +11,18 @@ from ..tcp_interface import TCPInterface
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_TCPInterface(capsys):
|
def test_TCPInterface(capsys):
|
||||||
"""Test that we can instantiate a TCPInterface"""
|
"""Test that we can instantiate a TCPInterface"""
|
||||||
with patch('socket.socket') as mock_socket:
|
with patch("socket.socket") as mock_socket:
|
||||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
iface = TCPInterface(hostname="localhost", noProto=True)
|
||||||
iface.myConnect()
|
iface.myConnect()
|
||||||
iface.showInfo()
|
iface.showInfo()
|
||||||
iface.localNode.showInfo()
|
iface.localNode.showInfo()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
|
assert re.search(r"Owner: None \(None\)", out, re.MULTILINE)
|
||||||
assert re.search(r'Nodes', out, re.MULTILINE)
|
assert re.search(r"Nodes", out, re.MULTILINE)
|
||||||
assert re.search(r'Preferences', out, re.MULTILINE)
|
assert re.search(r"Preferences", out, re.MULTILINE)
|
||||||
assert re.search(r'Channels', out, re.MULTILINE)
|
assert re.search(r"Channels", out, re.MULTILINE)
|
||||||
assert re.search(r'Primary channel URL', out, re.MULTILINE)
|
assert re.search(r"Primary channel URL", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
assert mock_socket.called
|
assert mock_socket.called
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
@@ -34,10 +34,12 @@ def test_TCPInterface_exception():
|
|||||||
def throw_an_exception():
|
def throw_an_exception():
|
||||||
raise ValueError("Fake 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
|
mock_shutdown.side_effect = throw_an_exception
|
||||||
with patch('socket.socket') as mock_socket:
|
with patch("socket.socket") as mock_socket:
|
||||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
iface = TCPInterface(hostname="localhost", noProto=True)
|
||||||
iface.myConnect()
|
iface.myConnect()
|
||||||
iface.close()
|
iface.close()
|
||||||
assert mock_socket.called
|
assert mock_socket.called
|
||||||
@@ -47,6 +49,6 @@ def test_TCPInterface_exception():
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_TCPInterface_without_connecting():
|
def test_TCPInterface_without_connecting():
|
||||||
"""Test that we can instantiate a TCPInterface with connectNow as false"""
|
"""Test that we can instantiate a TCPInterface with connectNow as false"""
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
iface = TCPInterface(hostname='localhost', noProto=True, connectNow=False)
|
iface = TCPInterface(hostname="localhost", noProto=True, connectNow=False)
|
||||||
assert iface.socket is None
|
assert iface.socket is None
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
"""Meshtastic unit tests for tunnel.py"""
|
"""Meshtastic unit tests for tunnel.py"""
|
||||||
|
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import logging
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from unittest.mock import patch, MagicMock
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from ..globals import Globals
|
||||||
from ..tcp_interface import TCPInterface
|
from ..tcp_interface import TCPInterface
|
||||||
from ..tunnel import Tunnel, onTunnelReceive
|
from ..tunnel import Tunnel, onTunnelReceive
|
||||||
from ..globals import Globals
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_Tunnel_on_non_linux_system(mock_platform_system):
|
def test_Tunnel_on_non_linux_system(mock_platform_system):
|
||||||
"""Test that we cannot instantiate a Tunnel on a non Linux system"""
|
"""Test that we cannot instantiate a Tunnel on a non Linux system"""
|
||||||
a_mock = MagicMock()
|
a_mock = MagicMock()
|
||||||
a_mock.return_value = 'notLinux'
|
a_mock.return_value = "notLinux"
|
||||||
mock_platform_system.side_effect = a_mock
|
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:
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
iface = TCPInterface(hostname="localhost", noProto=True)
|
||||||
Tunnel(iface)
|
Tunnel(iface)
|
||||||
assert pytest_wrapped_e.type == Exception
|
assert pytest_wrapped_e.type == Exception
|
||||||
assert mock_socket.called
|
assert mock_socket.called
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_Tunnel_without_interface(mock_platform_system):
|
def test_Tunnel_without_interface(mock_platform_system):
|
||||||
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
||||||
a_mock = MagicMock()
|
a_mock = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
Tunnel(None)
|
Tunnel(None)
|
||||||
@@ -40,227 +40,235 @@ def test_Tunnel_without_interface(mock_platform_system):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_Tunnel_with_interface(mock_platform_system, caplog, iface_with_nodes):
|
def test_Tunnel_with_interface(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
a_mock = MagicMock()
|
a_mock = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.WARNING):
|
with caplog.at_level(logging.WARNING):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
assert tun == Globals.getInstance().get_tunnelInstance()
|
assert tun == Globals.getInstance().get_tunnelInstance()
|
||||||
iface.close()
|
iface.close()
|
||||||
assert re.search(r'Not creating a TapDevice()', 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 starting TUN reader", caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
|
assert re.search(r"Not sending packet", caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, iface_with_nodes):
|
def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test onTunnelReceive"""
|
"""Test onTunnelReceive"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
sys.argv = ['']
|
sys.argv = [""]
|
||||||
Globals.getInstance().set_args(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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
Globals.getInstance().set_tunnelInstance(tun)
|
Globals.getInstance().set_tunnelInstance(tun)
|
||||||
onTunnelReceive(packet, iface)
|
onTunnelReceive(packet, iface)
|
||||||
assert re.search(r'in onTunnelReceive', 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)
|
assert re.search(r"Ignoring message we sent", caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_onTunnelReceive_from_someone_else(mock_platform_system, caplog, iface_with_nodes):
|
def test_onTunnelReceive_from_someone_else(
|
||||||
|
mock_platform_system, caplog, iface_with_nodes
|
||||||
|
):
|
||||||
"""Test onTunnelReceive"""
|
"""Test onTunnelReceive"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
sys.argv = ['']
|
sys.argv = [""]
|
||||||
Globals.getInstance().set_args(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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
Globals.getInstance().set_tunnelInstance(tun)
|
Globals.getInstance().set_tunnelInstance(tun)
|
||||||
onTunnelReceive(packet, iface)
|
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
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_shouldFilterPacket_random(mock_platform_system, caplog, iface_with_nodes):
|
def test_shouldFilterPacket_random(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test _shouldFilterPacket()"""
|
"""Test _shouldFilterPacket()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
# random packet
|
# random packet
|
||||||
packet = b'1234567890123456789012345678901234567890'
|
packet = b"1234567890123456789012345678901234567890"
|
||||||
a_mock = MagicMock()
|
a_mock = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
ignore = tun._shouldFilterPacket(packet)
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
assert not ignore
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_shouldFilterPacket_in_blacklist(mock_platform_system, caplog, iface_with_nodes):
|
def test_shouldFilterPacket_in_blacklist(
|
||||||
|
mock_platform_system, caplog, iface_with_nodes
|
||||||
|
):
|
||||||
"""Test _shouldFilterPacket()"""
|
"""Test _shouldFilterPacket()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
# faked IGMP
|
# 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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
ignore = tun._shouldFilterPacket(packet)
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
assert ignore
|
assert ignore
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_shouldFilterPacket_icmp(mock_platform_system, caplog, iface_with_nodes):
|
def test_shouldFilterPacket_icmp(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test _shouldFilterPacket()"""
|
"""Test _shouldFilterPacket()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
# faked ICMP
|
# 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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
ignore = tun._shouldFilterPacket(packet)
|
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
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_shouldFilterPacket_udp(mock_platform_system, caplog, iface_with_nodes):
|
def test_shouldFilterPacket_udp(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test _shouldFilterPacket()"""
|
"""Test _shouldFilterPacket()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
# faked UDP
|
# 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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
ignore = tun._shouldFilterPacket(packet)
|
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
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_shouldFilterPacket_udp_blacklisted(mock_platform_system, caplog, iface_with_nodes):
|
def test_shouldFilterPacket_udp_blacklisted(
|
||||||
|
mock_platform_system, caplog, iface_with_nodes
|
||||||
|
):
|
||||||
"""Test _shouldFilterPacket()"""
|
"""Test _shouldFilterPacket()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
# faked UDP
|
# 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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
# Note: custom logging level
|
# Note: custom logging level
|
||||||
LOG_TRACE = 5
|
LOG_TRACE = 5
|
||||||
with caplog.at_level(LOG_TRACE):
|
with caplog.at_level(LOG_TRACE):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
ignore = tun._shouldFilterPacket(packet)
|
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
|
assert ignore
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_shouldFilterPacket_tcp(mock_platform_system, caplog, iface_with_nodes):
|
def test_shouldFilterPacket_tcp(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test _shouldFilterPacket()"""
|
"""Test _shouldFilterPacket()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
# faked TCP
|
# 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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
ignore = tun._shouldFilterPacket(packet)
|
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
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_shouldFilterPacket_tcp_blacklisted(mock_platform_system, caplog, iface_with_nodes):
|
def test_shouldFilterPacket_tcp_blacklisted(
|
||||||
|
mock_platform_system, caplog, iface_with_nodes
|
||||||
|
):
|
||||||
"""Test _shouldFilterPacket()"""
|
"""Test _shouldFilterPacket()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
# faked TCP
|
# 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 = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
# Note: custom logging level
|
# Note: custom logging level
|
||||||
LOG_TRACE = 5
|
LOG_TRACE = 5
|
||||||
with caplog.at_level(LOG_TRACE):
|
with caplog.at_level(LOG_TRACE):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
ignore = tun._shouldFilterPacket(packet)
|
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
|
assert ignore
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_ipToNodeId_none(mock_platform_system, caplog, iface_with_nodes):
|
def test_ipToNodeId_none(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test _ipToNodeId()"""
|
"""Test _ipToNodeId()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
a_mock = MagicMock()
|
a_mock = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
nodeid = tun._ipToNodeId('something not useful')
|
nodeid = tun._ipToNodeId("something not useful")
|
||||||
assert nodeid is None
|
assert nodeid is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@patch('platform.system')
|
@patch("platform.system")
|
||||||
def test_ipToNodeId_all(mock_platform_system, caplog, iface_with_nodes):
|
def test_ipToNodeId_all(mock_platform_system, caplog, iface_with_nodes):
|
||||||
"""Test _ipToNodeId()"""
|
"""Test _ipToNodeId()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
iface.noProto = True
|
iface.noProto = True
|
||||||
a_mock = MagicMock()
|
a_mock = MagicMock()
|
||||||
a_mock.return_value = 'Linux'
|
a_mock.return_value = "Linux"
|
||||||
mock_platform_system.side_effect = a_mock
|
mock_platform_system.side_effect = a_mock
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
with patch('socket.socket'):
|
with patch("socket.socket"):
|
||||||
tun = Tunnel(iface)
|
tun = Tunnel(iface)
|
||||||
nodeid = tun._ipToNodeId(b'\x00\x00\xff\xff')
|
nodeid = tun._ipToNodeId(b"\x00\x00\xff\xff")
|
||||||
assert nodeid == '^all'
|
assert nodeid == "^all"
|
||||||
|
|||||||
@@ -1,111 +1,131 @@
|
|||||||
"""Meshtastic unit tests for util.py"""
|
"""Meshtastic unit tests for util.py"""
|
||||||
|
|
||||||
import re
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
|
from meshtastic.supported_device import SupportedDevice
|
||||||
support_info, genPSK256, fromStr, fromPSK,
|
from meshtastic.util import (
|
||||||
quoteBooleans, catchAndIgnore,
|
Timeout,
|
||||||
remove_keys_from_dict, Timeout, hexstr,
|
active_ports_on_supported_devices,
|
||||||
ipstr, readnet_u16, findPorts, convert_mac_addr,
|
camel_to_snake,
|
||||||
snake_to_camel, 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
|
@pytest.mark.unit
|
||||||
def test_genPSK256():
|
def test_genPSK256():
|
||||||
"""Test genPSK256"""
|
"""Test genPSK256"""
|
||||||
assert genPSK256() != ''
|
assert genPSK256() != ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_fromStr():
|
def test_fromStr():
|
||||||
"""Test fromStr"""
|
"""Test fromStr"""
|
||||||
assert fromStr('') == b''
|
assert fromStr("") == b""
|
||||||
assert fromStr('0x12') == b'\x12'
|
assert fromStr("0x12") == b"\x12"
|
||||||
assert fromStr('t')
|
assert fromStr("t")
|
||||||
assert fromStr('T')
|
assert fromStr("T")
|
||||||
assert fromStr('true')
|
assert fromStr("true")
|
||||||
assert fromStr('True')
|
assert fromStr("True")
|
||||||
assert fromStr('yes')
|
assert fromStr("yes")
|
||||||
assert fromStr('Yes')
|
assert fromStr("Yes")
|
||||||
assert fromStr('f') is False
|
assert fromStr("f") is False
|
||||||
assert fromStr('F') is False
|
assert fromStr("F") is False
|
||||||
assert fromStr('false') is False
|
assert fromStr("false") is False
|
||||||
assert fromStr('False') is False
|
assert fromStr("False") is False
|
||||||
assert fromStr('no') is False
|
assert fromStr("no") is False
|
||||||
assert fromStr('No') is False
|
assert fromStr("No") is False
|
||||||
assert fromStr('100.01') == 100.01
|
assert fromStr("100.01") == 100.01
|
||||||
assert fromStr('123') == 123
|
assert fromStr("123") == 123
|
||||||
assert fromStr('abc') == 'abc'
|
assert fromStr("abc") == "abc"
|
||||||
assert fromStr('123456789') == 123456789
|
assert fromStr("123456789") == 123456789
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_quoteBooleans():
|
def test_quoteBooleans():
|
||||||
"""Test quoteBooleans"""
|
"""Test quoteBooleans"""
|
||||||
assert quoteBooleans('') == ''
|
assert quoteBooleans("") == ""
|
||||||
assert quoteBooleans('foo') == 'foo'
|
assert quoteBooleans("foo") == "foo"
|
||||||
assert quoteBooleans('true') == 'true'
|
assert quoteBooleans("true") == "true"
|
||||||
assert quoteBooleans('false') == 'false'
|
assert quoteBooleans("false") == "false"
|
||||||
assert quoteBooleans(': true') == ": 'true'"
|
assert quoteBooleans(": true") == ": 'true'"
|
||||||
assert quoteBooleans(': false') == ": 'false'"
|
assert quoteBooleans(": false") == ": 'false'"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_fromPSK():
|
def test_fromPSK():
|
||||||
"""Test fromPSK"""
|
"""Test fromPSK"""
|
||||||
assert fromPSK('random') != ''
|
assert fromPSK("random") != ""
|
||||||
assert fromPSK('none') == b'\x00'
|
assert fromPSK("none") == b"\x00"
|
||||||
assert fromPSK('default') == b'\x01'
|
assert fromPSK("default") == b"\x01"
|
||||||
assert fromPSK('simple22') == b'\x17'
|
assert fromPSK("simple22") == b"\x17"
|
||||||
assert fromPSK('trash') == 'trash'
|
assert fromPSK("trash") == "trash"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_stripnl():
|
def test_stripnl():
|
||||||
"""Test stripnl"""
|
"""Test stripnl"""
|
||||||
assert stripnl('') == ''
|
assert stripnl("") == ""
|
||||||
assert stripnl('a\n') == 'a'
|
assert stripnl("a\n") == "a"
|
||||||
assert stripnl(' a \n ') == 'a'
|
assert stripnl(" a \n ") == "a"
|
||||||
assert stripnl('a\nb') == 'a b'
|
assert stripnl("a\nb") == "a b"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_pskToString_empty_string():
|
def test_pskToString_empty_string():
|
||||||
"""Test pskToString empty string"""
|
"""Test pskToString empty string"""
|
||||||
assert pskToString('') == 'unencrypted'
|
assert pskToString("") == "unencrypted"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_pskToString_string():
|
def test_pskToString_string():
|
||||||
"""Test pskToString string"""
|
"""Test pskToString string"""
|
||||||
assert pskToString('hunter123') == 'secret'
|
assert pskToString("hunter123") == "secret"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_pskToString_one_byte_zero_value():
|
def test_pskToString_one_byte_zero_value():
|
||||||
"""Test pskToString one byte that is value of 0"""
|
"""Test pskToString one byte that is value of 0"""
|
||||||
assert pskToString(bytes([0x00])) == 'unencrypted'
|
assert pskToString(bytes([0x00])) == "unencrypted"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_pskToString_one_byte_non_zero_value():
|
def test_pskToString_one_byte_non_zero_value():
|
||||||
"""Test pskToString one byte that is non-zero"""
|
"""Test pskToString one byte that is non-zero"""
|
||||||
assert pskToString(bytes([0x01])) == 'default'
|
assert pskToString(bytes([0x01])) == "default"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_pskToString_many_bytes():
|
def test_pskToString_many_bytes():
|
||||||
"""Test pskToString many bytes"""
|
"""Test pskToString many bytes"""
|
||||||
assert pskToString(bytes([0x02, 0x01])) == 'secret'
|
assert pskToString(bytes([0x02, 0x01])) == "secret"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_pskToString_simple():
|
def test_pskToString_simple():
|
||||||
"""Test pskToString simple"""
|
"""Test pskToString simple"""
|
||||||
assert pskToString(bytes([0x03])) == 'simple2'
|
assert pskToString(bytes([0x03])) == "simple2"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@@ -114,8 +134,8 @@ def test_our_exit_zero_return_value(capsys):
|
|||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
our_exit("Warning: Some message", 0)
|
our_exit("Warning: Some message", 0)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Warning: Some message', out, re.MULTILINE)
|
assert re.search(r"Warning: Some message", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 0
|
assert pytest_wrapped_e.value.code == 0
|
||||||
|
|
||||||
@@ -126,8 +146,8 @@ def test_our_exit_non_zero_return_value(capsys):
|
|||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
our_exit("Error: Some message", 1)
|
our_exit("Error: Some message", 1)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Error: Some message', out, re.MULTILINE)
|
assert re.search(r"Error: Some message", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
|
||||||
@@ -145,21 +165,23 @@ def test_support_info(capsys):
|
|||||||
"""Test support_info"""
|
"""Test support_info"""
|
||||||
support_info()
|
support_info()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'System', out, re.MULTILINE)
|
assert re.search(r"System", out, re.MULTILINE)
|
||||||
assert re.search(r'Platform', out, re.MULTILINE)
|
assert re.search(r"Platform", out, re.MULTILINE)
|
||||||
assert re.search(r'Machine', out, re.MULTILINE)
|
assert re.search(r"Machine", out, re.MULTILINE)
|
||||||
assert re.search(r'Executable', out, re.MULTILINE)
|
assert re.search(r"Executable", out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_catchAndIgnore(caplog):
|
def test_catchAndIgnore(caplog):
|
||||||
"""Test catchAndIgnore() does not actually throw an exception, but just logs"""
|
"""Test catchAndIgnore() does not actually throw an exception, but just logs"""
|
||||||
|
|
||||||
def some_closure():
|
def some_closure():
|
||||||
raise Exception('foo')
|
raise Exception("foo")
|
||||||
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
catchAndIgnore("something", some_closure)
|
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
|
@pytest.mark.unitslow
|
||||||
@@ -171,35 +193,35 @@ def test_remove_keys_from_dict_empty_keys_empty_dict():
|
|||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_remove_keys_from_dict_empty_dict():
|
def test_remove_keys_from_dict_empty_dict():
|
||||||
"""Test when dict is empty"""
|
"""Test when dict is empty"""
|
||||||
assert not remove_keys_from_dict(('a'), {})
|
assert not remove_keys_from_dict(("a"), {})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_remove_keys_from_dict_empty_keys():
|
def test_remove_keys_from_dict_empty_keys():
|
||||||
"""Test when keys is empty"""
|
"""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
|
@pytest.mark.unitslow
|
||||||
def test_remove_keys_from_dict():
|
def test_remove_keys_from_dict():
|
||||||
"""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
|
@pytest.mark.unitslow
|
||||||
def test_remove_keys_from_dict_multiple_keys():
|
def test_remove_keys_from_dict_multiple_keys():
|
||||||
"""Test remove_keys_from_dict()"""
|
"""Test remove_keys_from_dict()"""
|
||||||
keys = ('a', 'b')
|
keys = ("a", "b")
|
||||||
adict = {'a': 1, 'b': 2, 'c': 3}
|
adict = {"a": 1, "b": 2, "c": 3}
|
||||||
assert remove_keys_from_dict(keys, adict) == {'c':3}
|
assert remove_keys_from_dict(keys, adict) == {"c": 3}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_remove_keys_from_dict_nested():
|
def test_remove_keys_from_dict_nested():
|
||||||
"""Test remove_keys_from_dict()"""
|
"""Test remove_keys_from_dict()"""
|
||||||
keys = ('b')
|
keys = "b"
|
||||||
adict = {'a': {'b': 1}, 'b': 2, 'c': 3}
|
adict = {"a": {"b": 1}, "b": 2, "c": 3}
|
||||||
exp = {'a': {}, 'c': 3}
|
exp = {"a": {}, "c": 3}
|
||||||
assert remove_keys_from_dict(keys, adict) == exp
|
assert remove_keys_from_dict(keys, adict) == exp
|
||||||
|
|
||||||
|
|
||||||
@@ -207,8 +229,8 @@ def test_remove_keys_from_dict_nested():
|
|||||||
def test_Timeout_not_found():
|
def test_Timeout_not_found():
|
||||||
"""Test Timeout()"""
|
"""Test Timeout()"""
|
||||||
to = Timeout(0.2)
|
to = Timeout(0.2)
|
||||||
attrs = ('foo')
|
attrs = "foo"
|
||||||
to.waitForSet('bar', attrs)
|
to.waitForSet("bar", attrs)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
@@ -216,59 +238,310 @@ def test_Timeout_found():
|
|||||||
"""Test Timeout()"""
|
"""Test Timeout()"""
|
||||||
to = Timeout(0.2)
|
to = Timeout(0.2)
|
||||||
attrs = ()
|
attrs = ()
|
||||||
to.waitForSet('bar', attrs)
|
to.waitForSet("bar", attrs)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_hexstr():
|
def test_hexstr():
|
||||||
"""Test hexstr()"""
|
"""Test hexstr()"""
|
||||||
assert hexstr(b'123') == '31:32:33'
|
assert hexstr(b"123") == "31:32:33"
|
||||||
assert hexstr(b'') == ''
|
assert hexstr(b"") == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_ipstr():
|
def test_ipstr():
|
||||||
"""Test ipstr()"""
|
"""Test ipstr()"""
|
||||||
assert ipstr(b'1234') == '49.50.51.52'
|
assert ipstr(b"1234") == "49.50.51.52"
|
||||||
assert ipstr(b'') == ''
|
assert ipstr(b"") == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_readnet_u16():
|
def test_readnet_u16():
|
||||||
"""Test readnet_u16()"""
|
"""Test readnet_u16()"""
|
||||||
assert readnet_u16(b'123456', 2) == 13108
|
assert readnet_u16(b"123456", 2) == 13108
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@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):
|
def test_findPorts_when_none_found(patch_comports):
|
||||||
"""Test findPorts()"""
|
"""Test findPorts()"""
|
||||||
assert not findPorts()
|
assert not findPorts()
|
||||||
patch_comports.assert_called()
|
patch_comports.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
@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"""
|
||||||
|
|
||||||
|
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")
|
||||||
|
patch_comports.return_value = [fake1, fake2]
|
||||||
|
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,
|
||||||
|
):
|
||||||
|
"""Test findPorts()"""
|
||||||
|
|
||||||
|
class TempPort:
|
||||||
|
"""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")
|
||||||
|
patch_comports.return_value = [fake2, fake1]
|
||||||
|
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_not_used(patch_comports):
|
||||||
|
"""Test findPorts()"""
|
||||||
|
|
||||||
|
class TempPort:
|
||||||
|
"""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")
|
||||||
|
patch_comports.return_value = [fake1, fake2]
|
||||||
|
assert findPorts() == ["/dev/cu.usbserial-1430", "/dev/cu.wchusbserial1430"]
|
||||||
|
patch_comports.assert_called()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_convert_mac_addr():
|
def test_convert_mac_addr():
|
||||||
"""Test convert_mac_addr()"""
|
"""Test convert_mac_addr()"""
|
||||||
assert convert_mac_addr('/c0gFyhb') == 'fd:cd:20:17:28:5b'
|
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("fd:cd:20:17:28:5b") == "fd:cd:20:17:28:5b"
|
||||||
assert convert_mac_addr('') == ''
|
assert convert_mac_addr("") == ""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_snake_to_camel():
|
def test_snake_to_camel():
|
||||||
"""Test snake_to_camel"""
|
"""Test snake_to_camel"""
|
||||||
assert snake_to_camel('') == ''
|
assert snake_to_camel("") == ""
|
||||||
assert snake_to_camel('foo') == 'foo'
|
assert snake_to_camel("foo") == "foo"
|
||||||
assert snake_to_camel('foo_bar') == 'fooBar'
|
assert snake_to_camel("foo_bar") == "fooBar"
|
||||||
assert snake_to_camel('fooBar') == 'fooBar'
|
assert snake_to_camel("fooBar") == "fooBar"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_camel_to_snake():
|
def test_camel_to_snake():
|
||||||
"""Test camel_to_snake"""
|
"""Test camel_to_snake"""
|
||||||
assert camel_to_snake('') == ''
|
assert camel_to_snake("") == ""
|
||||||
assert camel_to_snake('foo') == 'foo'
|
assert camel_to_snake("foo") == "foo"
|
||||||
assert camel_to_snake('Foo') == 'foo'
|
assert camel_to_snake("Foo") == "foo"
|
||||||
assert camel_to_snake('fooBar') == 'foo_bar'
|
assert camel_to_snake("fooBar") == "foo_bar"
|
||||||
assert camel_to_snake('fooBarBaz') == 'foo_bar_baz'
|
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"]
|
||||||
|
|
||||||
|
|
||||||
|
@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
|
||||||
|
patched_platform.assert_called()
|
||||||
|
patched_release.assert_called()
|
||||||
|
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")
|
||||||
|
def test_is_windows11_true2(patched_platform, patched_release, patched_version):
|
||||||
|
"""Test is_windows11()"""
|
||||||
|
assert is_windows11() is False
|
||||||
|
patched_platform.assert_called()
|
||||||
|
patched_release.assert_called()
|
||||||
|
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")
|
||||||
|
def test_is_windows11_false(patched_platform, patched_release, patched_version):
|
||||||
|
"""Test is_windows11()"""
|
||||||
|
assert is_windows11() is False
|
||||||
|
patched_platform.assert_called()
|
||||||
|
patched_release.assert_called()
|
||||||
|
patched_version.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@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
|
||||||
|
patched_platform.assert_called()
|
||||||
|
patched_release.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch("platform.system", return_value="Linux")
|
||||||
|
def test_active_ports_on_supported_devices_empty(mock_platform):
|
||||||
|
"""Test active_ports_on_supported_devices()"""
|
||||||
|
sds = set()
|
||||||
|
assert active_ports_on_supported_devices(sds) == set()
|
||||||
|
mock_platform.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@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"
|
||||||
|
)
|
||||||
|
fake_supported_devices = [fake_device]
|
||||||
|
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")
|
||||||
|
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-"
|
||||||
|
)
|
||||||
|
fake_supported_devices = [fake_device]
|
||||||
|
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")
|
||||||
|
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_supported_devices = [fake_device]
|
||||||
|
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
|
||||||
|
):
|
||||||
|
"""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"
|
||||||
|
)
|
||||||
|
fake_supported_devices = [fake_device]
|
||||||
|
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")
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
fake_supported_devices = [fake_device]
|
||||||
|
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 logging
|
||||||
import threading
|
|
||||||
import platform
|
import platform
|
||||||
from pubsub import pub
|
import threading
|
||||||
|
|
||||||
|
from pubsub import pub
|
||||||
from pytap2 import TapDevice
|
from pytap2 import TapDevice
|
||||||
|
|
||||||
from meshtastic import portnums_pb2
|
from meshtastic import portnums_pb2
|
||||||
from meshtastic.util import ipstr, readnet_u16
|
|
||||||
from meshtastic.globals import Globals
|
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."""
|
"""Callback for received tunneled messages from mesh."""
|
||||||
logging.debug(f'in onTunnelReceive()')
|
logging.debug(f"in onTunnelReceive()")
|
||||||
our_globals = Globals.getInstance()
|
our_globals = Globals.getInstance()
|
||||||
tunnelInstance = our_globals.get_tunnelInstance()
|
tunnelInstance = our_globals.get_tunnelInstance()
|
||||||
tunnelInstance.onReceive(packet)
|
tunnelInstance.onReceive(packet)
|
||||||
@@ -38,7 +38,7 @@ def onTunnelReceive(packet, interface): # pylint: disable=W0613
|
|||||||
class Tunnel:
|
class Tunnel:
|
||||||
"""A TUN based IP tunnel over meshtastic"""
|
"""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
|
Constructor
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ class Tunnel:
|
|||||||
self.iface = iface
|
self.iface = iface
|
||||||
self.subnetPrefix = subnet
|
self.subnetPrefix = subnet
|
||||||
|
|
||||||
if platform.system() != 'Linux':
|
if platform.system() != "Linux":
|
||||||
raise Exception("Tunnel() can only be run instantiated on a Linux system")
|
raise Exception("Tunnel() can only be run instantiated on a Linux system")
|
||||||
|
|
||||||
our_globals = Globals.getInstance()
|
our_globals = Globals.getInstance()
|
||||||
@@ -80,8 +80,10 @@ class Tunnel:
|
|||||||
self.LOG_TRACE = 5
|
self.LOG_TRACE = 5
|
||||||
|
|
||||||
# TODO: check if root?
|
# TODO: check if root?
|
||||||
logging.info("Starting IP to mesh tunnel (you must be root for this *pre-alpha* "\
|
logging.info(
|
||||||
"feature to work). Mesh members:")
|
"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")
|
pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP")
|
||||||
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
|
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
|
||||||
@@ -96,7 +98,9 @@ class Tunnel:
|
|||||||
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
|
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
|
||||||
self.tun = None
|
self.tun = None
|
||||||
if self.iface.noProto:
|
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:
|
else:
|
||||||
self.tun = TapDevice(name="mesh")
|
self.tun = TapDevice(name="mesh")
|
||||||
self.tun.up()
|
self.tun.up()
|
||||||
@@ -104,10 +108,14 @@ class Tunnel:
|
|||||||
|
|
||||||
self._rxThread = None
|
self._rxThread = None
|
||||||
if self.iface.noProto:
|
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:
|
else:
|
||||||
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
|
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()
|
self._rxThread.start()
|
||||||
|
|
||||||
def onReceive(self, packet):
|
def onReceive(self, packet):
|
||||||
@@ -132,15 +140,19 @@ class Tunnel:
|
|||||||
ignore = False # Assume we will be forwarding the packet
|
ignore = False # Assume we will be forwarding the packet
|
||||||
if protocol in self.protocolBlacklist:
|
if protocol in self.protocolBlacklist:
|
||||||
ignore = True
|
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
|
elif protocol == 0x01: # ICMP
|
||||||
icmpType = p[20]
|
icmpType = p[20]
|
||||||
icmpCode = p[21]
|
icmpCode = p[21]
|
||||||
checksum = p[22:24]
|
checksum = p[22:24]
|
||||||
# pylint: disable=line-too-long
|
# 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)
|
# 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)
|
# tap.write(pingback)
|
||||||
elif protocol == 0x11: # UDP
|
elif protocol == 0x11: # UDP
|
||||||
srcport = readnet_u16(p, subheader)
|
srcport = readnet_u16(p, subheader)
|
||||||
@@ -159,8 +171,10 @@ class Tunnel:
|
|||||||
else:
|
else:
|
||||||
logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
|
logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
|
||||||
else:
|
else:
|
||||||
logging.warning(f"forwarding unexpected protocol 0x{protocol:02x}, "\
|
logging.warning(
|
||||||
"src={ipstr(srcaddr)}, dest={ipstr(destAddr)}")
|
f"forwarding unexpected protocol 0x{protocol:02x}, "
|
||||||
|
"src={ipstr(srcaddr)}, dest={ipstr(destAddr)}"
|
||||||
|
)
|
||||||
|
|
||||||
return ignore
|
return ignore
|
||||||
|
|
||||||
@@ -169,7 +183,7 @@ class Tunnel:
|
|||||||
logging.debug("TUN reader running")
|
logging.debug("TUN reader running")
|
||||||
while True:
|
while True:
|
||||||
p = tap.read()
|
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]
|
destAddr = p[16:20]
|
||||||
|
|
||||||
if not self._shouldFilterPacket(p):
|
if not self._shouldFilterPacket(p):
|
||||||
@@ -179,11 +193,11 @@ class Tunnel:
|
|||||||
# We only consider the last 16 bits of the nodenum for IP address matching
|
# We only consider the last 16 bits of the nodenum for IP address matching
|
||||||
ipBits = ipAddr[2] * 256 + ipAddr[3]
|
ipBits = ipAddr[2] * 256 + ipAddr[3]
|
||||||
|
|
||||||
if ipBits == 0xffff:
|
if ipBits == 0xFFFF:
|
||||||
return "^all"
|
return "^all"
|
||||||
|
|
||||||
for node in self.iface.nodes.values():
|
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}")
|
# logging.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}")
|
||||||
if (nodeNum) == ipBits:
|
if (nodeNum) == ipBits:
|
||||||
return node["user"]["id"]
|
return node["user"]["id"]
|
||||||
@@ -196,11 +210,14 @@ class Tunnel:
|
|||||||
"""Forward the provided IP packet into the mesh"""
|
"""Forward the provided IP packet into the mesh"""
|
||||||
nodeId = self._ipToNodeId(destAddr)
|
nodeId = self._ipToNodeId(destAddr)
|
||||||
if nodeId is not None:
|
if nodeId is not None:
|
||||||
logging.debug(f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
logging.debug(
|
||||||
self.iface.sendData(
|
f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}"
|
||||||
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
|
)
|
||||||
|
self.iface.sendData(p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
|
||||||
else:
|
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):
|
def close(self):
|
||||||
"""Close"""
|
"""Close"""
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
"""Utility functions.
|
"""Utility functions.
|
||||||
"""
|
"""
|
||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
import os
|
|
||||||
import re
|
import pkg_resources
|
||||||
import sys
|
import requests
|
||||||
import base64
|
|
||||||
import time
|
|
||||||
import platform
|
|
||||||
import logging
|
|
||||||
import threading
|
|
||||||
import subprocess
|
|
||||||
import serial
|
import serial
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
import pkg_resources
|
|
||||||
from meshtastic.supported_device import get_unique_vendor_ids, get_devices_with_vendor_id
|
from meshtastic.supported_device import supported_devices
|
||||||
|
|
||||||
"""Some devices such as a seger jlink we never want to accidentally open"""
|
"""Some devices such as a seger jlink we never want to accidentally open"""
|
||||||
blacklistVids = dict.fromkeys([0x1366])
|
blacklistVids = dict.fromkeys([0x1366])
|
||||||
@@ -22,12 +25,13 @@ blacklistVids = dict.fromkeys([0x1366])
|
|||||||
|
|
||||||
def quoteBooleans(a_string):
|
def quoteBooleans(a_string):
|
||||||
"""Quote booleans
|
"""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 = a_string.replace(": true", ": 'true'")
|
||||||
tmp = tmp.replace(": false", ": 'false'")
|
tmp = tmp.replace(": false", ": 'false'")
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
|
|
||||||
def genPSK256():
|
def genPSK256():
|
||||||
"""Generate a random preshared key"""
|
"""Generate a random preshared key"""
|
||||||
return os.urandom(32)
|
return os.urandom(32)
|
||||||
@@ -60,9 +64,11 @@ def fromStr(valstr):
|
|||||||
"""
|
"""
|
||||||
if len(valstr) == 0: # Treat an emptystring as an empty bytes
|
if len(valstr) == 0: # Treat an emptystring as an empty bytes
|
||||||
val = bytes()
|
val = bytes()
|
||||||
elif valstr.startswith('0x'):
|
elif valstr.startswith("0x"):
|
||||||
# if needed convert to string with asBytes.decode('utf-8')
|
# if needed convert to string with asBytes.decode('utf-8')
|
||||||
val = bytes.fromhex(valstr[2:])
|
val = bytes.fromhex(valstr[2:])
|
||||||
|
elif valstr.startswith("base64:"):
|
||||||
|
val = base64.b64decode(valstr[7:])
|
||||||
elif valstr.lower() in {"t", "true", "yes"}:
|
elif valstr.lower() in {"t", "true", "yes"}:
|
||||||
val = True
|
val = True
|
||||||
elif valstr.lower() in {"f", "false", "no"}:
|
elif valstr.lower() in {"f", "false", "no"}:
|
||||||
@@ -97,7 +103,7 @@ def pskToString(psk: bytes):
|
|||||||
def stripnl(s):
|
def stripnl(s):
|
||||||
"""Remove newlines from a string (and remove extra whitespace)"""
|
"""Remove newlines from a string (and remove extra whitespace)"""
|
||||||
s = str(s).replace("\n", " ")
|
s = str(s).replace("\n", " ")
|
||||||
return ' '.join(s.split())
|
return " ".join(s.split())
|
||||||
|
|
||||||
|
|
||||||
def fixme(message):
|
def fixme(message):
|
||||||
@@ -113,21 +119,31 @@ def catchAndIgnore(reason, closure):
|
|||||||
logging.error(f"Exception thrown in {reason}: {ex}")
|
logging.error(f"Exception thrown in {reason}: {ex}")
|
||||||
|
|
||||||
|
|
||||||
def findPorts():
|
def findPorts(eliminate_duplicates=False):
|
||||||
"""Find all ports that might have meshtastic devices
|
"""Find all ports that might have meshtastic devices
|
||||||
|
eliminate_duplicates will run the eliminate_duplicate_port() on the collection
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list -- a list of device paths
|
list -- a list of device paths
|
||||||
"""
|
"""
|
||||||
l = list(map(lambda port: port.device,
|
l = list(
|
||||||
filter(lambda port: port.vid is not None and port.vid not in blacklistVids,
|
map(
|
||||||
serial.tools.list_ports.comports())))
|
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()
|
l.sort()
|
||||||
|
if eliminate_duplicates:
|
||||||
|
l = eliminate_duplicate_port(l)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
|
||||||
class dotdict(dict):
|
class dotdict(dict):
|
||||||
"""dot.notation access to dictionary attributes"""
|
"""dot.notation access to dictionary attributes"""
|
||||||
|
|
||||||
__getattr__ = dict.get
|
__getattr__ = dict.get
|
||||||
__setattr__ = dict.__setitem__
|
__setattr__ = dict.__setitem__
|
||||||
__delattr__ = dict.__delitem__
|
__delattr__ = dict.__delitem__
|
||||||
@@ -135,6 +151,7 @@ class dotdict(dict):
|
|||||||
|
|
||||||
class Timeout:
|
class Timeout:
|
||||||
"""Timeout class"""
|
"""Timeout class"""
|
||||||
|
|
||||||
def __init__(self, maxSecs=20):
|
def __init__(self, maxSecs=20):
|
||||||
self.expireTime = 0
|
self.expireTime = 0
|
||||||
self.sleepInterval = 0.1
|
self.sleepInterval = 0.1
|
||||||
@@ -153,8 +170,60 @@ class Timeout:
|
|||||||
time.sleep(self.sleepInterval)
|
time.sleep(self.sleepInterval)
|
||||||
return False
|
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"""
|
"""A thread that accepts closures to run, and runs them as they are received"""
|
||||||
|
|
||||||
def __init__(self, name=None):
|
def __init__(self, name=None):
|
||||||
@@ -164,7 +233,7 @@ class DeferredExecution():
|
|||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
def queueWork(self, runnable):
|
def queueWork(self, runnable):
|
||||||
""" Queue up the work"""
|
"""Queue up the work"""
|
||||||
self.queue.put(runnable)
|
self.queue.put(runnable)
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
@@ -173,13 +242,15 @@ class DeferredExecution():
|
|||||||
o = self.queue.get()
|
o = self.queue.get()
|
||||||
o()
|
o()
|
||||||
except:
|
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())
|
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.
|
"""Print the message and return a value.
|
||||||
return_value defaults to 1 (non-successful)
|
return_value defaults to 1 (non-successful)
|
||||||
"""
|
"""
|
||||||
print(message)
|
print(message)
|
||||||
sys.exit(return_value)
|
sys.exit(return_value)
|
||||||
@@ -187,28 +258,36 @@ def our_exit(message, return_value = 1):
|
|||||||
|
|
||||||
def support_info():
|
def support_info():
|
||||||
"""Print out info that helps troubleshooting of the cli."""
|
"""Print out info that helps troubleshooting of the cli."""
|
||||||
print('')
|
print("")
|
||||||
print('If having issues with meshtastic cli or python library')
|
print("If having issues with meshtastic cli or python library")
|
||||||
print('or wish to make feature requests, visit:')
|
print("or wish to make feature requests, visit:")
|
||||||
print('https://github.com/meshtastic/Meshtastic-python/issues')
|
print("https://github.com/meshtastic/python/issues")
|
||||||
print('When adding an issue, be sure to include the following info:')
|
print("When adding an issue, be sure to include the following info:")
|
||||||
print(f' System: {platform.system()}')
|
print(f" System: {platform.system()}")
|
||||||
print(f' Platform: {platform.platform()}')
|
print(f" Platform: {platform.platform()}")
|
||||||
print(f' Release: {platform.uname().release}')
|
print(f" Release: {platform.uname().release}")
|
||||||
print(f' Machine: {platform.uname().machine}')
|
print(f" Machine: {platform.uname().machine}")
|
||||||
print(f' Encoding (stdin): {sys.stdin.encoding}')
|
print(f" Encoding (stdin): {sys.stdin.encoding}")
|
||||||
print(f' Encoding (stdout): {sys.stdout.encoding}')
|
print(f" Encoding (stdout): {sys.stdout.encoding}")
|
||||||
the_version = pkg_resources.get_distribution("meshtastic").version
|
the_version = pkg_resources.get_distribution("meshtastic").version
|
||||||
print(f' meshtastic: v{the_version}')
|
pypi_version = check_if_newer_version()
|
||||||
print(f' Executable: {sys.argv[0]}')
|
if pypi_version:
|
||||||
print(f' Python: {platform.python_version()} {platform.python_implementation()} {platform.python_compiler()}')
|
print(
|
||||||
print('')
|
f" meshtastic: v{the_version} (*** newer version v{pypi_version} available ***)"
|
||||||
print('Please add the output from the command: meshtastic --info')
|
)
|
||||||
|
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):
|
def remove_keys_from_dict(keys, adict):
|
||||||
"""Return a dictionary without some keys in it.
|
"""Return a dictionary without some keys in it.
|
||||||
Will removed nested keys.
|
Will removed nested keys.
|
||||||
"""
|
"""
|
||||||
for key in keys:
|
for key in keys:
|
||||||
try:
|
try:
|
||||||
@@ -223,12 +302,12 @@ def remove_keys_from_dict(keys, adict):
|
|||||||
|
|
||||||
def hexstr(barray):
|
def hexstr(barray):
|
||||||
"""Print a string of hex digits"""
|
"""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):
|
def ipstr(barray):
|
||||||
"""Print a string of ip digits"""
|
"""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):
|
def readnet_u16(p, offset):
|
||||||
@@ -238,8 +317,8 @@ def readnet_u16(p, offset):
|
|||||||
|
|
||||||
def convert_mac_addr(val):
|
def convert_mac_addr(val):
|
||||||
"""Convert the base 64 encoded value to a mac address
|
"""Convert the base 64 encoded value to a mac address
|
||||||
val - base64 encoded value (ex: '/c0gFyhb'))
|
val - base64 encoded value (ex: '/c0gFyhb'))
|
||||||
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
|
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):
|
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)
|
val_as_bytes = base64.b64decode(val)
|
||||||
@@ -250,21 +329,23 @@ def convert_mac_addr(val):
|
|||||||
def snake_to_camel(a_string):
|
def snake_to_camel(a_string):
|
||||||
"""convert snake_case to camelCase"""
|
"""convert snake_case to camelCase"""
|
||||||
# split underscore using split
|
# split underscore using split
|
||||||
temp = a_string.split('_')
|
temp = a_string.split("_")
|
||||||
# joining result
|
# 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
|
return result
|
||||||
|
|
||||||
|
|
||||||
def camel_to_snake(a_string):
|
def camel_to_snake(a_string):
|
||||||
"""convert camelCase to snake_case"""
|
"""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():
|
def detect_supported_devices():
|
||||||
"""detect supported devices"""
|
"""detect supported devices based on vendor id"""
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
#print(f'system:{system}')
|
# print(f'system:{system}')
|
||||||
|
|
||||||
possible_devices = set()
|
possible_devices = set()
|
||||||
if system == "Linux":
|
if system == "Linux":
|
||||||
@@ -272,86 +353,52 @@ def detect_supported_devices():
|
|||||||
|
|
||||||
# linux: use lsusb
|
# linux: use lsusb
|
||||||
# Bus 001 Device 091: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
|
# 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()
|
vids = get_unique_vendor_ids()
|
||||||
for vid in vids:
|
for vid in vids:
|
||||||
#print(f'looking for {vid}...')
|
# print(f'looking for {vid}...')
|
||||||
search = f' {vid}:'
|
search = f" {vid}:"
|
||||||
#print(f'search:"{search}"')
|
# print(f'search:"{search}"')
|
||||||
if re.search(search, lsusb_output, re.MULTILINE):
|
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)
|
devices = get_devices_with_vendor_id(vid)
|
||||||
# check device id
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
|
possible_devices.add(device)
|
||||||
if device.usb_product_id_in_hex:
|
|
||||||
search = f' {vid}:{device.usb_product_id_in_hex} '
|
|
||||||
#print(f'search:"{search}"')
|
|
||||||
if re.search(search, lsusb_output, re.MULTILINE):
|
|
||||||
# concatenate the devices with vendor id to possibles
|
|
||||||
possible_devices.add(device)
|
|
||||||
else:
|
|
||||||
# if there is a supported device witout a product id, then it
|
|
||||||
# might be a match... so, concatenate
|
|
||||||
possible_devices.add(device)
|
|
||||||
|
|
||||||
elif system == "Windows":
|
elif system == "Windows":
|
||||||
# if windows, run Get-PnpDevice
|
# if windows, run Get-PnpDevice
|
||||||
_, sp_output = subprocess.getstatusoutput('powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
|
_, sp_output = subprocess.getstatusoutput(
|
||||||
'Get-PnpDevice -PresentOnly | Format-List"')
|
'powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
|
||||||
#print(f'sp_output:{sp_output}')
|
'Get-PnpDevice -PresentOnly | Format-List"'
|
||||||
|
)
|
||||||
|
# print(f'sp_output:{sp_output}')
|
||||||
vids = get_unique_vendor_ids()
|
vids = get_unique_vendor_ids()
|
||||||
for vid in vids:
|
for vid in vids:
|
||||||
#print(f'looking for {vid.upper()}...')
|
# print(f'looking for {vid.upper()}...')
|
||||||
search = f'DeviceID.*{vid.upper()}&'
|
search = f"DeviceID.*{vid.upper()}&"
|
||||||
#search = f'{vid.upper()}'
|
# search = f'{vid.upper()}'
|
||||||
#print(f'search:"{search}"')
|
# print(f'search:"{search}"')
|
||||||
if re.search(search, sp_output, re.MULTILINE):
|
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)
|
devices = get_devices_with_vendor_id(vid)
|
||||||
# check device id
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
|
possible_devices.add(device)
|
||||||
if device.usb_product_id_in_hex:
|
|
||||||
search = f'DeviceID.*{vid.upper()}&PID_{device.usb_product_id_in_hex.upper()}'
|
|
||||||
#print(f'search:"{search}"')
|
|
||||||
if re.search(search, sp_output, re.MULTILINE):
|
|
||||||
# concatenate the devices with vendor id to possibles
|
|
||||||
possible_devices.add(device)
|
|
||||||
# do a check to see if there is a Windows driver issue
|
|
||||||
if detect_windows_needs_driver(device, False):
|
|
||||||
print("WARNING: Need to install driver.")
|
|
||||||
else:
|
|
||||||
# if there is a supported device witout a product id, then it
|
|
||||||
# might be a match... so, concatenate
|
|
||||||
possible_devices.add(device)
|
|
||||||
|
|
||||||
elif system == "Darwin":
|
elif system == "Darwin":
|
||||||
# run: system_profiler SPUSBDataType
|
# run: system_profiler SPUSBDataType
|
||||||
# Note: If in boot mode, the 19003 reports same product ID as 5005.
|
# 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()
|
vids = get_unique_vendor_ids()
|
||||||
for vid in vids:
|
for vid in vids:
|
||||||
#print(f'looking for {vid}...')
|
# print(f'looking for {vid}...')
|
||||||
search = f'Vendor ID: 0x{vid}'
|
search = f"Vendor ID: 0x{vid}"
|
||||||
#print(f'search:"{search}"')
|
# print(f'search:"{search}"')
|
||||||
if re.search(search, sp_output, re.MULTILINE):
|
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)
|
devices = get_devices_with_vendor_id(vid)
|
||||||
# check device id
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
|
possible_devices.add(device)
|
||||||
if device.usb_product_id_in_hex:
|
|
||||||
search = f'Product ID: 0x{device.usb_product_id_in_hex}'
|
|
||||||
#print(f'search:"{search}"')
|
|
||||||
if re.search(search, sp_output, re.MULTILINE):
|
|
||||||
# concatenate the devices with vendor id to possibles
|
|
||||||
possible_devices.add(device)
|
|
||||||
else:
|
|
||||||
# if there is a supported device witout a product id, then it
|
|
||||||
# might be a match... so, concatenate
|
|
||||||
possible_devices.add(device)
|
|
||||||
return possible_devices
|
return possible_devices
|
||||||
|
|
||||||
|
|
||||||
@@ -361,7 +408,7 @@ def detect_windows_needs_driver(sd, print_reason=False):
|
|||||||
|
|
||||||
if sd:
|
if sd:
|
||||||
system = platform.system()
|
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 system == "Windows":
|
||||||
# if windows, see if we can find a DeviceId with the vendor id
|
# if windows, see if we can find a DeviceId with the vendor id
|
||||||
@@ -370,14 +417,191 @@ def detect_windows_needs_driver(sd, print_reason=False):
|
|||||||
command += f"'*{sd.usb_vendor_id_in_hex.upper()}*'"
|
command += f"'*{sd.usb_vendor_id_in_hex.upper()}*'"
|
||||||
command += ')} | Format-List"'
|
command += ')} | Format-List"'
|
||||||
|
|
||||||
#print(f'command:{command}')
|
# print(f'command:{command}')
|
||||||
_, sp_output = subprocess.getstatusoutput(command)
|
_, sp_output = subprocess.getstatusoutput(command)
|
||||||
#print(f'sp_output:{sp_output}')
|
# print(f'sp_output:{sp_output}')
|
||||||
search = f'CM_PROB_FAILED_INSTALL'
|
search = f"CM_PROB_FAILED_INSTALL"
|
||||||
#print(f'search:"{search}"')
|
# print(f'search:"{search}"')
|
||||||
if re.search(search, sp_output, re.MULTILINE):
|
if re.search(search, sp_output, re.MULTILINE):
|
||||||
need_to_install_driver = True
|
need_to_install_driver = True
|
||||||
# if the want to see the reason
|
# if the want to see the reason
|
||||||
if print_reason:
|
if print_reason:
|
||||||
print(sp_output)
|
print(sp_output)
|
||||||
return need_to_install_driver
|
return need_to_install_driver
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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]:
|
||||||
|
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]:
|
||||||
|
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]:
|
||||||
|
new_ports.append(ports[1])
|
||||||
|
else:
|
||||||
|
new_ports = ports
|
||||||
|
return new_ports
|
||||||
|
|
||||||
|
|
||||||
|
def is_windows11():
|
||||||
|
"""Detect if Windows 11"""
|
||||||
|
is_win11 = False
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
if float(platform.release()) >= 10.0:
|
||||||
|
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}")
|
||||||
|
return is_win11
|
||||||
|
|
||||||
|
|
||||||
|
def get_unique_vendor_ids():
|
||||||
|
"""Return a set of unique vendor ids"""
|
||||||
|
vids = set()
|
||||||
|
for d in supported_devices:
|
||||||
|
if d.usb_vendor_id_in_hex:
|
||||||
|
vids.add(d.usb_vendor_id_in_hex)
|
||||||
|
return vids
|
||||||
|
|
||||||
|
|
||||||
|
def get_devices_with_vendor_id(vid):
|
||||||
|
"""Return a set of unique devices with the vendor id"""
|
||||||
|
sd = set()
|
||||||
|
for d in supported_devices:
|
||||||
|
if d.usb_vendor_id_in_hex == vid:
|
||||||
|
sd.add(d)
|
||||||
|
return sd
|
||||||
|
|
||||||
|
|
||||||
|
def active_ports_on_supported_devices(sds, eliminate_duplicates=False):
|
||||||
|
"""Return a set of active ports based on the supplied supported devices"""
|
||||||
|
ports = set()
|
||||||
|
baseports = set()
|
||||||
|
system = platform.system()
|
||||||
|
|
||||||
|
# figure out what possible base ports there are
|
||||||
|
for d in sds:
|
||||||
|
if system == "Linux":
|
||||||
|
baseports.add(d.baseport_on_linux)
|
||||||
|
elif system == "Darwin":
|
||||||
|
baseports.add(d.baseport_on_mac)
|
||||||
|
elif system == "Windows":
|
||||||
|
baseports.add(d.baseport_on_windows)
|
||||||
|
|
||||||
|
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}')
|
||||||
|
_, ls_output = subprocess.getstatusoutput(command)
|
||||||
|
# print(f'ls_output:{ls_output}')
|
||||||
|
# if we got output, there are ports
|
||||||
|
if len(ls_output) > 0:
|
||||||
|
# print('got output')
|
||||||
|
# for each line of output
|
||||||
|
lines = ls_output.split("\n")
|
||||||
|
# print(f'lines:{lines}')
|
||||||
|
for line in lines:
|
||||||
|
parts = line.split(" ")
|
||||||
|
# print(f'parts:{parts}')
|
||||||
|
port = parts[-1]
|
||||||
|
# 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}')
|
||||||
|
_, ls_output = subprocess.getstatusoutput(command)
|
||||||
|
# print(f'ls_output:{ls_output}')
|
||||||
|
# if we got output, there are ports
|
||||||
|
if len(ls_output) > 0:
|
||||||
|
# print('got output')
|
||||||
|
# for each line of output
|
||||||
|
lines = ls_output.split("\n")
|
||||||
|
# print(f'lines:{lines}')
|
||||||
|
for line in lines:
|
||||||
|
parts = line.split(" ")
|
||||||
|
# print(f'parts:{parts}')
|
||||||
|
port = parts[-1]
|
||||||
|
# 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}')
|
||||||
|
# add all ports
|
||||||
|
for com_port in com_ports:
|
||||||
|
ports.add(com_port)
|
||||||
|
if eliminate_duplicates:
|
||||||
|
ports = eliminate_duplicate_port(list(ports))
|
||||||
|
ports.sort()
|
||||||
|
ports = set(ports)
|
||||||
|
return ports
|
||||||
|
|
||||||
|
|
||||||
|
def detect_windows_port(sd):
|
||||||
|
"""detect if Windows port"""
|
||||||
|
ports = set()
|
||||||
|
|
||||||
|
if sd:
|
||||||
|
system = platform.system()
|
||||||
|
|
||||||
|
if system == "Windows":
|
||||||
|
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}')
|
||||||
|
_, sp_output = subprocess.getstatusoutput(command)
|
||||||
|
# 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}")
|
||||||
|
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
|
||||||
|
|||||||
38
meshtastic/xmodem_pb2.py
Normal file
38
meshtastic/xmodem_pb2.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: meshtastic/xmodem.proto
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_XMODEM = DESCRIPTOR.message_types_by_name['XModem']
|
||||||
|
_XMODEM_CONTROL = _XMODEM.enum_types_by_name['Control']
|
||||||
|
XModem = _reflection.GeneratedProtocolMessageType('XModem', (_message.Message,), {
|
||||||
|
'DESCRIPTOR' : _XMODEM,
|
||||||
|
'__module__' : 'meshtastic.xmodem_pb2'
|
||||||
|
# @@protoc_insertion_point(class_scope:XModem)
|
||||||
|
})
|
||||||
|
_sym_db.RegisterMessage(XModem)
|
||||||
|
|
||||||
|
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 792027be93
1
protobufs
Submodule
1
protobufs
Submodule
Submodule protobufs added at 468ff2e245
@@ -7,6 +7,7 @@ pyqrcode
|
|||||||
tabulate
|
tabulate
|
||||||
timeago
|
timeago
|
||||||
webencodings
|
webencodings
|
||||||
|
requests
|
||||||
pyparsing
|
pyparsing
|
||||||
twine
|
twine
|
||||||
autopep8
|
autopep8
|
||||||
|
|||||||
42
setup.py
42
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.
|
# Note: you shouldn't need to run this script manually. It is run implicitly by the pip3 install command.
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
# The directory containing this file
|
# The directory containing this file
|
||||||
@@ -12,37 +13,44 @@ with open("README.md", "r") as fh:
|
|||||||
# This call to setup() does all the work
|
# This call to setup() does all the work
|
||||||
setup(
|
setup(
|
||||||
name="meshtastic",
|
name="meshtastic",
|
||||||
version="1.2.80",
|
version="2.2.1",
|
||||||
description="Python API & client shell for talking to Meshtastic devices",
|
description="Python API & client shell for talking to Meshtastic devices",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url="https://github.com/meshtastic/Meshtastic-python",
|
url="https://github.com/meshtastic/python",
|
||||||
author="Kevin Hester",
|
author="Meshtastic Developers",
|
||||||
author_email="kevinh@geeksville.com",
|
author_email="contact@meshtastic.org",
|
||||||
license="MIT",
|
license="GPL-3.0-only",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
"Programming Language :: Python :: 3",
|
"Development Status :: 4 - Beta",
|
||||||
"Programming Language :: Python :: 3.6",
|
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
],
|
],
|
||||||
packages=["meshtastic"],
|
packages=["meshtastic"],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=["pyserial>=3.4", "protobuf>=3.13.0",
|
install_requires=[
|
||||||
"pypubsub>=4.0.3", "dotmap>=1.3.14", "pexpect>=4.6.0", "pyqrcode>=1.2.1",
|
"pyserial>=3.4",
|
||||||
"tabulate>=0.8.9", "timeago>=1.0.15", "pyyaml",
|
"protobuf>=3.13.0",
|
||||||
"pygatt>=4.0.5 ; platform_system=='Linux'"],
|
"requests>=2.25.0",
|
||||||
extras_require={
|
"pypubsub>=4.0.3",
|
||||||
'tunnel': ["pytap2>=2.0.0"]
|
"dotmap>=1.3.14",
|
||||||
},
|
"pexpect>=4.6.0",
|
||||||
python_requires='>=3.6',
|
"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={
|
entry_points={
|
||||||
"console_scripts": [
|
"console_scripts": [
|
||||||
"meshtastic=meshtastic.__main__:main",
|
"meshtastic=meshtastic.__main__:main",
|
||||||
"mesh-tunnel=meshtastic.__main__:tunnelMain [tunnel]"
|
"mesh-tunnel=meshtastic.__main__:tunnelMain [tunnel]",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
readme.txt for single standalone executable zip files that can be
|
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
|
If you do not want to install python and/or the python libraries, you can download one of these
|
||||||
zip files to run the Meshtastic command line interface (CLI) as a standalone executable.
|
files to run the Meshtastic command line interface (CLI) as a standalone executable.
|
||||||
|
|
||||||
See https://meshtastic.org/docs/software/python/python-standalone for more info.
|
See https://meshtastic.org/docs/software/python/python-standalone for more info.
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
import datetime
|
||||||
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import meshtastic
|
|
||||||
import datetime, logging
|
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
|
||||||
#logging.basicConfig(level=logging.DEBUG)
|
import meshtastic
|
||||||
|
|
||||||
|
# logging.basicConfig(level=logging.DEBUG)
|
||||||
print(str(datetime.datetime.now()) + ": start")
|
print(str(datetime.datetime.now()) + ": start")
|
||||||
interface = meshtastic.TCPInterface(sys.argv[1])
|
interface = meshtastic.TCPInterface(sys.argv[1])
|
||||||
print(str(datetime.datetime.now()) + ": middle")
|
print(str(datetime.datetime.now()) + ": middle")
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import meshtastic
|
|
||||||
import time
|
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.sendText("hello mesh")
|
||||||
interface.close()
|
interface.close()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user