mirror of
https://github.com/meshtastic/python.git
synced 2025-12-25 17:07:53 -05:00
Compare commits
220 Commits
1.2.80
...
1.3alpha.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Linting and Tests
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -35,21 +35,21 @@ jobs:
|
||||
which meshtastic
|
||||
meshtastic --version
|
||||
- name: Run pylint
|
||||
run: pylint meshtastic
|
||||
run: pylint meshtastic examples/
|
||||
- name: Run tests with pytest
|
||||
run: pytest --cov=meshtastic
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
pytest --cov=meshtastic --cov-report=xml
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./coverage.xml
|
||||
files: ./coverage.xml
|
||||
flags: unittests
|
||||
name: codecov-umbrella
|
||||
yml: ./codecov.yml
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
||||
111
.github/workflows/release.yml
vendored
111
.github/workflows/release.yml
vendored
@@ -1,66 +1,79 @@
|
||||
name: Make Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Release version (Example: 1.0.0, must match 'version' in setup.py)"
|
||||
required: true
|
||||
default: '1.0.0'
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
release_create:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.get_version.outputs.version }}
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
new_sha: ${{ steps.commit_updated.outputs.sha }}
|
||||
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
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: ${{ github.event.inputs.version}}
|
||||
tag_name: ${{ github.event.inputs.version}}
|
||||
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 }}
|
||||
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
publish_to_pypi:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install pypa/build
|
||||
run: >-
|
||||
python -m
|
||||
pip install
|
||||
build
|
||||
--user
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: >-
|
||||
python -m
|
||||
build
|
||||
--sdist
|
||||
--wheel
|
||||
--outdir dist/
|
||||
.
|
||||
|
||||
- name: Install pypa/build
|
||||
run: >-
|
||||
python -m
|
||||
pip install
|
||||
build
|
||||
--user
|
||||
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: >-
|
||||
python -m
|
||||
build
|
||||
--sdist
|
||||
--wheel
|
||||
--outdir dist/
|
||||
.
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
|
||||
build-and-publish-mac:
|
||||
@@ -69,7 +82,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ needs.release_create.outputs.new_sha }}
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
@@ -114,7 +129,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ needs.release_create.outputs.new_sha }}
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
@@ -137,7 +154,7 @@ jobs:
|
||||
asset_path: dist/meshtastic
|
||||
asset_name: meshtastic_ubuntu
|
||||
asset_content_type: application/zip
|
||||
|
||||
|
||||
- name: Add readme.txt to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -146,7 +163,7 @@ jobs:
|
||||
upload_url: ${{ needs.release_create.outputs.upload_url }}
|
||||
asset_path: standalone_readme.txt
|
||||
asset_name: readme.txt
|
||||
asset_content_type: text/plain
|
||||
asset_content_type: text/plain
|
||||
|
||||
build-and-publish-windows:
|
||||
runs-on: windows-latest
|
||||
@@ -154,7 +171,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ needs.release_create.outputs.new_sha }}
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
7
.github/workflows/update_protobufs.yml
vendored
7
.github/workflows/update_protobufs.yml
vendored
@@ -18,9 +18,9 @@ jobs:
|
||||
|
||||
- name: Download nanopb
|
||||
run: |
|
||||
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.4-linux-x86.tar.gz
|
||||
tar xvzf nanopb-0.4.4-linux-x86.tar.gz
|
||||
mv nanopb-0.4.4-linux-x86 nanopb-0.4.4
|
||||
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.6-linux-x86.tar.gz
|
||||
tar xvzf nanopb-0.4.6-linux-x86.tar.gz
|
||||
mv nanopb-0.4.6-linux-x86 nanopb-0.4.6
|
||||
|
||||
- name: Re-generate protocol buffers
|
||||
run: |
|
||||
@@ -32,4 +32,5 @@ jobs:
|
||||
git config --global user.email 'bot@noreply.github.com'
|
||||
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
|
||||
git add proto
|
||||
git add meshtastic
|
||||
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
|
||||
log_*
|
||||
.eggs
|
||||
nanopb-0.4.4
|
||||
nanopb-*
|
||||
.*swp
|
||||
.coverage
|
||||
*.py-E
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
# Add files or directories matching the regex patterns to the blacklist. The
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=mqtt_pb2.py,channel_pb2.py,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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
# --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,no-self-use,pointless-string-statement,too-few-public-methods,broad-except,no-else-return,no-else-raise,bare-except,too-many-public-methods
|
||||
|
||||
[BASIC]
|
||||
|
||||
@@ -41,7 +40,7 @@ bad-names=foo,bar,baz,toto,tutu,tata
|
||||
max-line-length=150
|
||||
|
||||
# Maximum number of lines in a module
|
||||
max-module-lines=1200
|
||||
max-module-lines=1600
|
||||
|
||||
|
||||
|
||||
|
||||
68
.vscode/launch.json
vendored
68
.vscode/launch.json
vendored
@@ -42,7 +42,71 @@
|
||||
"request": "launch",
|
||||
"module": "meshtastic",
|
||||
"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", "1"]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
@@ -92,7 +156,7 @@
|
||||
"module": "meshtastic",
|
||||
"justMyCode": true,
|
||||
"args": ["--debug", "--sendtext", "pytest"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "meshtastic showNodes",
|
||||
"type": "python",
|
||||
|
||||
12
Makefile
12
Makefile
@@ -6,6 +6,10 @@ test:
|
||||
virt:
|
||||
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
|
||||
install:
|
||||
pip install .
|
||||
@@ -16,12 +20,18 @@ docs:
|
||||
|
||||
# lint the codebase
|
||||
lint:
|
||||
pylint meshtastic
|
||||
pylint meshtastic examples
|
||||
|
||||
# show the slowest unit tests
|
||||
slow:
|
||||
pytest -m unit --durations=5
|
||||
|
||||
proto: FORCE
|
||||
git submodule update --init --recursive
|
||||
git pull --rebase
|
||||
git submodule update --remote --merge
|
||||
./bin/regen-protos.sh
|
||||
|
||||
# run the coverage report and open results in a browser
|
||||
cov:
|
||||
pytest --cov-report html --cov=meshtastic
|
||||
|
||||
26
README.md
26
README.md
@@ -1,15 +1,25 @@
|
||||
# Meshtastic-python
|
||||
# Meshtastic Python
|
||||
|
||||
[](https://open.vscode.dev/meshtastic/Meshtastic-python)
|
||||

|
||||
[](https://codecov.io/gh/meshtastic/Meshtastic-python)
|
||||

|
||||
[](https://github.com/meshtastic/Meshtastic-python/actions/workflows/ci.yml)
|
||||
[](https://cla-assistant.io/meshtastic/Meshtastic-python)
|
||||
[](https://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).
|
||||
|
||||
The library api is documented [here](https://meshtastic-python.vercel.app/meshtastic/index.html)
|
||||
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.
|
||||
|
||||
|
||||
[](https://vercel.com?utm_source=meshtastic&utm_campaign=oss)
|
||||
**[Getting Started Guide](https://meshtastic.org/docs/software/python/python-installation)**
|
||||
|
||||
**[Documentation/API Reference](https://python.meshtastic.org/)**
|
||||
|
||||
|
||||
## 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
|
||||
- make command line options for displaying/changing config
|
||||
- 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
|
||||
- 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
|
||||
|
||||
25
bin/bump_version.py
Executable file
25
bin/bump_version.py
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
"""Bump the version number"""
|
||||
|
||||
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(".")
|
||||
ver = f'{v[0]}.{v[1]}.{int(v[2]) + 1}'
|
||||
f.write(f' version="{ver}",\n')
|
||||
else:
|
||||
f.write(line)
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
./nanopb-0.4.4/generator-bin/protoc -I=proto --python_out meshtastic `ls proto/*.proto`
|
||||
./nanopb-0.4.6/generator-bin/protoc -I=proto --python_out meshtastic `ls proto/*.proto`
|
||||
|
||||
# workaround for import bug in protoc https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-690618628
|
||||
|
||||
|
||||
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()}')
|
||||
@@ -12,6 +12,5 @@ location:
|
||||
userPrefs:
|
||||
region: 1
|
||||
isAlwaysPowered: 'true'
|
||||
sendOwnerInterval: 2
|
||||
screenOnSecs: 31536000
|
||||
waitBluetoothSecs: 31536000
|
||||
|
||||
@@ -12,7 +12,6 @@ location:
|
||||
user_prefs:
|
||||
region: 1
|
||||
is_always_powered: 'true'
|
||||
send_owner_interval: 2
|
||||
screen_on_secs: 31536000
|
||||
wait_bluetooth_secs: 31536000
|
||||
location_share: 'LocEnabled'
|
||||
|
||||
23
examples/info_example.py
Normal file
23
examples/info_example.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""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.myInfo:
|
||||
#print(f'myInfo:{iface.myInfo}')
|
||||
print(f'firmware_version:{iface.myInfo.firmware_version}')
|
||||
|
||||
if iface.nodes:
|
||||
for n in iface.nodes.values():
|
||||
if n['num'] == iface.myInfo.my_node_num:
|
||||
print(n['user']['hwModel'])
|
||||
break
|
||||
|
||||
iface.close()
|
||||
@@ -13,7 +13,7 @@ if len(sys.argv) < 2:
|
||||
print(f"usage: {sys.argv[0]} host")
|
||||
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."""
|
||||
print(interface.myInfo)
|
||||
interface.close()
|
||||
|
||||
@@ -14,11 +14,11 @@ if len(sys.argv) < 2:
|
||||
print(f"usage: {sys.argv[0]} host")
|
||||
sys.exit(1)
|
||||
|
||||
def onReceive(packet, interface):
|
||||
def onReceive(packet, interface): # pylint: disable=unused-argument
|
||||
"""called when a packet arrives"""
|
||||
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"""
|
||||
# defaults to broadcast, specify a destination ID if you wish
|
||||
interface.sendText("hello mesh")
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
import meshtastic
|
||||
from meshtastic.supported_device import get_unique_vendor_ids, active_ports_on_supported_devices
|
||||
from meshtastic.util import detect_supported_devices
|
||||
from meshtastic.util import detect_supported_devices, get_unique_vendor_ids, active_ports_on_supported_devices
|
||||
|
||||
# simple arg check
|
||||
if len(sys.argv) != 1:
|
||||
|
||||
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
|
||||
<snip>
|
||||
> | +-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 : =>
|
||||
=>
|
||||
@@ -7,7 +7,7 @@ Source code on [github](https://github.com/meshtastic/Meshtastic-python)
|
||||
|
||||
properties of SerialInterface:
|
||||
|
||||
- radioConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
|
||||
- localConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
|
||||
the device.
|
||||
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
|
||||
node in the mesh. This is a read-only datastructure.
|
||||
@@ -79,8 +79,8 @@ from google.protobuf.json_format import MessageToJson
|
||||
from meshtastic.util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
||||
from meshtastic.node import Node
|
||||
from meshtastic import (mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2,
|
||||
environmental_measurement_pb2, remote_hardware_pb2,
|
||||
channel_pb2, radioconfig_pb2, util)
|
||||
telemetry_pb2, remote_hardware_pb2,
|
||||
channel_pb2, config_pb2, util)
|
||||
|
||||
|
||||
# Note: To follow PEP224, comments should be after the module variable.
|
||||
@@ -94,7 +94,7 @@ BROADCAST_NUM = 0xffffffff
|
||||
BROADCAST_ADDR = "^all"
|
||||
"""A special ID that means broadcast"""
|
||||
|
||||
OUR_APP_VERSION = 20200
|
||||
OUR_APP_VERSION = 20300
|
||||
"""The numeric buildnumber (shared with android apps) specifying the
|
||||
level of device code we are guaranteed to understand
|
||||
|
||||
@@ -181,7 +181,6 @@ protocols = {
|
||||
portnums_pb2.PortNum.NODEINFO_APP: KnownProtocol("user", mesh_pb2.User, _onNodeInfoReceive),
|
||||
portnums_pb2.PortNum.ADMIN_APP: KnownProtocol("admin", admin_pb2.AdminMessage),
|
||||
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
|
||||
portnums_pb2.PortNum.ENVIRONMENTAL_MEASUREMENT_APP: KnownProtocol("environmental", environmental_measurement_pb2.EnvironmentalMeasurement),
|
||||
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
|
||||
"remotehw", remote_hardware_pb2.HardwareMessage)
|
||||
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol("telemetry", telemetry_pb2.Telemetry),
|
||||
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol("remotehw", remote_hardware_pb2.HardwareMessage)
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ import yaml
|
||||
from pubsub import pub
|
||||
import pyqrcode
|
||||
import pkg_resources
|
||||
from google.protobuf.json_format import MessageToDict
|
||||
import meshtastic.util
|
||||
import meshtastic.test
|
||||
from meshtastic import remote_hardware
|
||||
from meshtastic.ble_interface import BLEInterface
|
||||
from meshtastic import portnums_pb2, channel_pb2, radioconfig_pb2
|
||||
from meshtastic import portnums_pb2, channel_pb2, config_pb2
|
||||
from meshtastic.globals import Globals
|
||||
from meshtastic.__init__ import BROADCAST_ADDR
|
||||
|
||||
@@ -52,74 +53,73 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=W0613
|
||||
print(f"Connection changed: {topic.getName()}")
|
||||
|
||||
|
||||
def getPref(attributes, name):
|
||||
def getPref(config, comp_name):
|
||||
"""Get a channel or preferences value"""
|
||||
|
||||
camel_name = meshtastic.util.snake_to_camel(name)
|
||||
name = splitCompoundName(comp_name)
|
||||
|
||||
camel_name = meshtastic.util.snake_to_camel(name[1])
|
||||
# Note: protobufs has the keys in snake_case, so snake internally
|
||||
snake_name = meshtastic.util.camel_to_snake(name)
|
||||
snake_name = meshtastic.util.camel_to_snake(name[1])
|
||||
logging.debug(f'snake_name:{snake_name} camel_name:{camel_name}')
|
||||
logging.debug(f'use camel:{Globals.getInstance().get_camel_case()}')
|
||||
|
||||
objDesc = attributes.DESCRIPTOR
|
||||
field = objDesc.fields_by_name.get(snake_name)
|
||||
if not field:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not get it.")
|
||||
else:
|
||||
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not get it.")
|
||||
print(f"Choices in sorted order are:")
|
||||
names = []
|
||||
for f in objDesc.fields:
|
||||
tmp_name = f'{f.name}'
|
||||
if Globals.getInstance().get_camel_case():
|
||||
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
|
||||
names.append(tmp_name)
|
||||
for temp_name in sorted(names):
|
||||
print(f" {temp_name}")
|
||||
return
|
||||
objDesc = config.DESCRIPTOR
|
||||
config_type = objDesc.fields_by_name.get(name[0])
|
||||
pref = False
|
||||
if config_type:
|
||||
pref = config_type.message_type.fields_by_name.get(snake_name)
|
||||
|
||||
if (not pref) or (not config_type):
|
||||
return False
|
||||
|
||||
# read the value
|
||||
val = getattr(attributes, snake_name)
|
||||
config_values = getattr(config, config_type.name)
|
||||
pref_value = getattr(config_values, pref.name)
|
||||
|
||||
if Globals.getInstance().get_camel_case():
|
||||
print(f"{camel_name}: {str(val)}")
|
||||
logging.debug(f"{camel_name}: {str(val)}")
|
||||
print(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}")
|
||||
logging.debug(f"{str(config_type.name)}.{camel_name}: {str(pref_value)}")
|
||||
else:
|
||||
print(f"{snake_name}: {str(val)}")
|
||||
logging.debug(f"{snake_name}: {str(val)}")
|
||||
print(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}")
|
||||
logging.debug(f"{str(config_type.name)}.{snake_name}: {str(pref_value)}")
|
||||
return True
|
||||
|
||||
def splitCompoundName(comp_name):
|
||||
"""Split compound (dot separated) preference name into parts"""
|
||||
name = comp_name.split(".",1)
|
||||
if len(name) != 2:
|
||||
name[0]=comp_name
|
||||
name.append(comp_name)
|
||||
return name
|
||||
|
||||
def setPref(attributes, name, valStr):
|
||||
def setPref(config, comp_name, valStr):
|
||||
"""Set a channel or preferences value"""
|
||||
|
||||
snake_name = meshtastic.util.camel_to_snake(name)
|
||||
camel_name = meshtastic.util.snake_to_camel(name)
|
||||
name = splitCompoundName(comp_name)
|
||||
|
||||
snake_name = meshtastic.util.camel_to_snake(name[1])
|
||||
camel_name = meshtastic.util.snake_to_camel(name[1])
|
||||
logging.debug(f'snake_name:{snake_name}')
|
||||
logging.debug(f'camel_name:{camel_name}')
|
||||
|
||||
objDesc = attributes.DESCRIPTOR
|
||||
field = objDesc.fields_by_name.get(snake_name)
|
||||
if not field:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not set it.")
|
||||
else:
|
||||
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not set it.")
|
||||
print(f"Choices in sorted order are:")
|
||||
names = []
|
||||
for f in objDesc.fields:
|
||||
tmp_name = f'{f.name}'
|
||||
if Globals.getInstance().get_camel_case():
|
||||
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
|
||||
names.append(tmp_name)
|
||||
for temp_name in sorted(names):
|
||||
print(f" {temp_name}")
|
||||
return
|
||||
objDesc = config.DESCRIPTOR
|
||||
config_type = objDesc.fields_by_name.get(name[0])
|
||||
pref = False
|
||||
if config_type:
|
||||
pref = config_type.message_type.fields_by_name.get(snake_name)
|
||||
|
||||
if (not pref) or (not config_type):
|
||||
return False
|
||||
|
||||
val = meshtastic.util.fromStr(valStr)
|
||||
logging.debug(f'valStr:{valStr} val:{val}')
|
||||
|
||||
enumType = field.enum_type
|
||||
if snake_name == 'psk' and len(valStr) < 8:
|
||||
print(f"Warning: wifi.psk must be 8 or more characters.")
|
||||
return False
|
||||
|
||||
enumType = pref.enum_type
|
||||
# pylint: disable=C0123
|
||||
if enumType and type(val) == str:
|
||||
# We've failed so far to convert this string into an enum, try to find it by reflection
|
||||
@@ -128,9 +128,9 @@ def setPref(attributes, name, valStr):
|
||||
val = e.number
|
||||
else:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
print(f"{camel_name} does not have an enum called {val}, so you can not set it.")
|
||||
print(f"{name[0]}.{camel_name} does not have an enum called {val}, so you can not set it.")
|
||||
else:
|
||||
print(f"{snake_name} does not have an enum called {val}, so you can not set it.")
|
||||
print(f"{name[0]}.{snake_name} does not have an enum called {val}, so you can not set it.")
|
||||
print(f"Choices in sorted order are:")
|
||||
names = []
|
||||
for f in enumType.values:
|
||||
@@ -138,17 +138,32 @@ def setPref(attributes, name, valStr):
|
||||
names.append(f'{f.name}')
|
||||
for temp_name in sorted(names):
|
||||
print(f" {temp_name}")
|
||||
return
|
||||
try:
|
||||
setattr(attributes, snake_name, val)
|
||||
except TypeError:
|
||||
# The setter didn't like our arg type guess try again as a string
|
||||
setattr(attributes, snake_name, valStr)
|
||||
return False
|
||||
|
||||
# note: 'ignore_incoming' is a repeating field
|
||||
if snake_name != 'ignore_incoming':
|
||||
try:
|
||||
config_values = getattr(config, config_type.name)
|
||||
setattr(config_values, pref.name, val)
|
||||
except TypeError:
|
||||
# The setter didn't like our arg type guess try again as a string
|
||||
config_values = getattr(config, config_type.name)
|
||||
setattr(config_values, pref.name, valStr)
|
||||
else:
|
||||
if val == 0:
|
||||
# clear values
|
||||
print("Clearing ignore_incoming list")
|
||||
del config_type.message_type.ignore_incoming[:]
|
||||
else:
|
||||
print(f"Adding '{val}' to the ignore_incoming list")
|
||||
config_type.message_type.ignore_incoming.extend([val])
|
||||
|
||||
if Globals.getInstance().get_camel_case():
|
||||
print(f"Set {camel_name} to {valStr}")
|
||||
print(f"Set {name[0]}.{camel_name} to {valStr}")
|
||||
else:
|
||||
print(f"Set {snake_name} to {valStr}")
|
||||
print(f"Set {name[0]}.{snake_name} to {valStr}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def onConnected(interface):
|
||||
@@ -168,18 +183,18 @@ def onConnected(interface):
|
||||
alt = 0
|
||||
lat = 0.0
|
||||
lon = 0.0
|
||||
prefs = interface.localNode.radioConfig.preferences
|
||||
localConfig = interface.localNode.localConfig
|
||||
if args.setalt:
|
||||
alt = int(args.setalt)
|
||||
prefs.fixed_position = True
|
||||
localConfig.fixed_position = True
|
||||
print(f"Fixing altitude at {alt} meters")
|
||||
if args.setlat:
|
||||
lat = float(args.setlat)
|
||||
prefs.fixed_position = True
|
||||
localConfig.fixed_position = True
|
||||
print(f"Fixing latitude at {lat} degrees")
|
||||
if args.setlon:
|
||||
lon = float(args.setlon)
|
||||
prefs.fixed_position = True
|
||||
localConfig.fixed_position = True
|
||||
print(f"Fixing longitude at {lon} degrees")
|
||||
|
||||
print("Setting device position")
|
||||
@@ -200,52 +215,45 @@ def onConnected(interface):
|
||||
print(f"Setting device owner short to {args.set_owner_short}")
|
||||
interface.getNode(args.dest).setOwner(long_name=None, short_name=args.set_owner_short)
|
||||
|
||||
# TODO: add to export-config and configure
|
||||
if args.set_canned_message:
|
||||
closeNow = True
|
||||
print(f"Setting canned plugin message to {args.set_canned_message}")
|
||||
interface.getNode(args.dest).set_canned_message(args.set_canned_message)
|
||||
|
||||
if args.pos_fields:
|
||||
# If --pos-fields invoked with args, set position fields
|
||||
closeNow = True
|
||||
prefs = interface.getNode(args.dest).radioConfig.preferences
|
||||
localConfig = interface.getNode(args.dest).localConfig
|
||||
allFields = 0
|
||||
|
||||
try:
|
||||
for field in args.pos_fields:
|
||||
v_field = radioconfig_pb2.PositionFlags.Value(field)
|
||||
v_field = config_pb2.PositionFlags.Value(field)
|
||||
allFields |= v_field
|
||||
|
||||
except ValueError:
|
||||
print("ERROR: supported position fields are:")
|
||||
print(radioconfig_pb2.PositionFlags.keys())
|
||||
print(config_pb2.PositionFlags.keys())
|
||||
print("If no fields are specified, will read and display current value.")
|
||||
|
||||
else:
|
||||
print(f"Setting position fields to {allFields}")
|
||||
setPref(prefs, 'position_flags', f'{allFields:d}')
|
||||
setPref(localConfig, 'position_flags', f'{allFields:d}')
|
||||
print("Writing modified preferences to device")
|
||||
interface.getNode(args.dest).writeConfig()
|
||||
|
||||
elif args.pos_fields is not None:
|
||||
# If --pos-fields invoked without args, read and display current value
|
||||
closeNow = True
|
||||
prefs = interface.getNode(args.dest).radioConfig.preferences
|
||||
localConfig = interface.getNode(args.dest).localConfig
|
||||
|
||||
fieldNames = []
|
||||
for bit in radioconfig_pb2.PositionFlags.values():
|
||||
if prefs.position_flags & bit:
|
||||
fieldNames.append(radioconfig_pb2.PositionFlags.Name(bit))
|
||||
for bit in config_pb2.PositionFlags.values():
|
||||
if localConfig.position_flags & bit:
|
||||
fieldNames.append(config_pb2.PositionFlags.Name(bit))
|
||||
print(' '.join(fieldNames))
|
||||
|
||||
if args.set_team:
|
||||
closeNow = True
|
||||
try:
|
||||
v_team = meshtastic.mesh_pb2.Team.Value(args.set_team.upper())
|
||||
except ValueError:
|
||||
v_team = 0
|
||||
print(f"ERROR: Team \'{args.set_team}\' not found.")
|
||||
print("Try a team name from the sorted list below, or use 'CLEAR' for unaffiliated:")
|
||||
print(sorted(meshtastic.mesh_pb2.Team.keys()))
|
||||
else:
|
||||
print(f"Setting team to {meshtastic.mesh_pb2.Team.Name(v_team)}")
|
||||
interface.getNode(args.dest).setOwner(team=v_team)
|
||||
|
||||
if args.set_ham:
|
||||
closeNow = True
|
||||
print(f"Setting Ham ID to {args.set_ham} and turning off encryption")
|
||||
@@ -260,6 +268,10 @@ def onConnected(interface):
|
||||
if args.shutdown:
|
||||
closeNow = True
|
||||
interface.getNode(args.dest).shutdown()
|
||||
|
||||
if args.device_metadata:
|
||||
closeNow = True
|
||||
interface.getNode(args.dest).getMetadata()
|
||||
|
||||
if args.sendtext:
|
||||
closeNow = True
|
||||
@@ -318,14 +330,23 @@ def onConnected(interface):
|
||||
# handle settings
|
||||
if args.set:
|
||||
closeNow = True
|
||||
prefs = interface.getNode(args.dest).radioConfig.preferences
|
||||
node = interface.getNode(args.dest)
|
||||
|
||||
# Handle the int/float/bool arguments
|
||||
pref = None
|
||||
for pref in args.set:
|
||||
setPref(prefs, pref[0], pref[1])
|
||||
found = setPref(node.localConfig, pref[0], pref[1])
|
||||
if not found:
|
||||
found = setPref(node.moduleConfig, pref[0], pref[1])
|
||||
|
||||
print("Writing modified preferences to device")
|
||||
interface.getNode(args.dest).writeConfig()
|
||||
if found:
|
||||
print("Writing modified preferences to device")
|
||||
interface.getNode(args.dest).writeConfig()
|
||||
else:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
print(f"{node.localConfig.__class__.__name__} and {node.moduleConfig.__class__.__name__} do not have an attribute {pref[0]}.")
|
||||
else:
|
||||
print(f"{node.localConfig.__class__.__name__} and {node.moduleConfig.__class__.__name__} do not have attribute {pref[0]}.")
|
||||
|
||||
if args.configure:
|
||||
with open(args.configure[0], encoding='utf8') as file:
|
||||
@@ -356,35 +377,35 @@ def onConnected(interface):
|
||||
alt = 0
|
||||
lat = 0.0
|
||||
lon = 0.0
|
||||
prefs = interface.localNode.radioConfig.preferences
|
||||
localConfig = interface.localNode.localConfig
|
||||
|
||||
if 'alt' in configuration['location']:
|
||||
alt = int(configuration['location']['alt'])
|
||||
prefs.fixed_position = True
|
||||
localConfig.fixed_position = True
|
||||
print(f"Fixing altitude at {alt} meters")
|
||||
if 'lat' in configuration['location']:
|
||||
lat = float(configuration['location']['lat'])
|
||||
prefs.fixed_position = True
|
||||
localConfig.fixed_position = True
|
||||
print(f"Fixing latitude at {lat} degrees")
|
||||
if 'lon' in configuration['location']:
|
||||
lon = float(configuration['location']['lon'])
|
||||
prefs.fixed_position = True
|
||||
localConfig.fixed_position = True
|
||||
print(f"Fixing longitude at {lon} degrees")
|
||||
print("Setting device position")
|
||||
interface.sendPosition(lat, lon, alt)
|
||||
interface.localNode.writeConfig()
|
||||
|
||||
if 'user_prefs' in configuration:
|
||||
prefs = interface.getNode(args.dest).radioConfig.preferences
|
||||
localConfig = interface.getNode(args.dest).localConfig
|
||||
for pref in configuration['user_prefs']:
|
||||
setPref(prefs, pref, str(configuration['user_prefs'][pref]))
|
||||
setPref(localConfig, pref, str(configuration['user_prefs'][pref]))
|
||||
print("Writing modified preferences to device")
|
||||
interface.getNode(args.dest).writeConfig()
|
||||
|
||||
if 'userPrefs' in configuration:
|
||||
prefs = interface.getNode(args.dest).radioConfig.preferences
|
||||
localConfig = interface.getNode(args.dest).localConfig
|
||||
for pref in configuration['userPrefs']:
|
||||
setPref(prefs, pref, str(configuration['userPrefs'][pref]))
|
||||
setPref(localConfig, pref, str(configuration['userPrefs'][pref]))
|
||||
print("Writing modified preferences to device")
|
||||
interface.getNode(args.dest).writeConfig()
|
||||
|
||||
@@ -433,66 +454,56 @@ def onConnected(interface):
|
||||
print(f"Deleting channel {channelIndex}")
|
||||
ch = interface.getNode(args.dest).deleteChannel(channelIndex)
|
||||
|
||||
ch_changes = [args.ch_longslow, args.ch_longfast,
|
||||
args.ch_mediumslow, args.ch_mediumfast,
|
||||
args.ch_shortslow, args.ch_shortfast]
|
||||
any_primary_channel_changes = any(x for x in ch_changes)
|
||||
if args.ch_set or any_primary_channel_changes or args.ch_enable or args.ch_disable:
|
||||
def setSimpleConfig(modem_preset):
|
||||
"""Set one of the simple modem_config"""
|
||||
# Completely new radio settings
|
||||
chs = config_pb2.Config.LoRaConfig()
|
||||
chs.modem_preset = modem_preset
|
||||
prefs = interface.getNode(args.dest).localConfig
|
||||
prefs.lora.CopyFrom(chs)
|
||||
interface.getNode(args.dest).writeConfig()
|
||||
|
||||
# handle the simple radio set commands
|
||||
if args.ch_vlongslow:
|
||||
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.VLongSlow)
|
||||
|
||||
if args.ch_longslow:
|
||||
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.LongSlow)
|
||||
|
||||
if args.ch_longfast:
|
||||
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.LongFast)
|
||||
|
||||
if args.ch_midslow:
|
||||
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.MidSlow)
|
||||
|
||||
if args.ch_midfast:
|
||||
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.MidFast)
|
||||
|
||||
if args.ch_shortslow:
|
||||
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.ShortSlow)
|
||||
|
||||
if args.ch_shortfast:
|
||||
setSimpleConfig(config_pb2.Config.LoRaConfig.ModemPreset.ShortFast)
|
||||
|
||||
if args.ch_set or args.ch_enable or args.ch_disable:
|
||||
closeNow = True
|
||||
|
||||
channelIndex = our_globals.get_channel_index()
|
||||
if channelIndex is None:
|
||||
if any_primary_channel_changes:
|
||||
# we assume that they want the primary channel if they're setting range values
|
||||
channelIndex = 0
|
||||
else:
|
||||
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
|
||||
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
|
||||
ch = interface.getNode(args.dest).channels[channelIndex]
|
||||
|
||||
if any_primary_channel_changes or args.ch_enable or args.ch_disable:
|
||||
if args.ch_enable or args.ch_disable:
|
||||
|
||||
if channelIndex == 0 and not any_primary_channel_changes:
|
||||
if channelIndex == 0:
|
||||
meshtastic.util.our_exit("Warning: Cannot enable/disable PRIMARY channel.")
|
||||
|
||||
if channelIndex != 0:
|
||||
if any_primary_channel_changes:
|
||||
meshtastic.util.our_exit("Warning: Standard channel settings can only be applied to the PRIMARY channel")
|
||||
|
||||
enable = True # default to enable
|
||||
if args.ch_enable:
|
||||
enable = True
|
||||
if args.ch_disable:
|
||||
enable = False
|
||||
|
||||
def setSimpleChannel(modem_config):
|
||||
"""Set one of the simple modem_config only based channels"""
|
||||
|
||||
# Completely new channel settings
|
||||
chs = channel_pb2.ChannelSettings()
|
||||
chs.modem_config = modem_config
|
||||
chs.psk = bytes([1]) # Use default channel psk 1
|
||||
|
||||
ch.settings.CopyFrom(chs)
|
||||
|
||||
# handle the simple channel set commands
|
||||
if args.ch_longslow:
|
||||
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr48Sf4096)
|
||||
|
||||
if args.ch_longfast:
|
||||
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512)
|
||||
|
||||
if args.ch_mediumslow:
|
||||
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr46Sf2048)
|
||||
|
||||
if args.ch_mediumfast:
|
||||
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr47Sf1024)
|
||||
|
||||
if args.ch_shortslow:
|
||||
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr45Sf128)
|
||||
|
||||
if args.ch_shortfast:
|
||||
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw500Cr45Sf128)
|
||||
|
||||
# Handle the channel settings
|
||||
for pref in (args.ch_set or []):
|
||||
if pref[0] == "psk":
|
||||
@@ -510,6 +521,11 @@ def onConnected(interface):
|
||||
print(f"Writing modified channels to device")
|
||||
interface.getNode(args.dest).writeChannel(channelIndex)
|
||||
|
||||
if args.get_canned_message:
|
||||
closeNow = True
|
||||
print("")
|
||||
interface.getNode(args.dest).get_canned_message()
|
||||
|
||||
if args.info:
|
||||
print("")
|
||||
# If we aren't trying to talk to our local node, don't show it
|
||||
@@ -523,11 +539,20 @@ def onConnected(interface):
|
||||
|
||||
if args.get:
|
||||
closeNow = True
|
||||
prefs = interface.getNode(args.dest).radioConfig.preferences
|
||||
localConfig = interface.getNode(args.dest).localConfig
|
||||
moduleConfig = interface.getNode(args.dest).moduleConfig
|
||||
|
||||
# Handle the int/float/bool arguments
|
||||
for pref in args.get:
|
||||
getPref(prefs, pref[0])
|
||||
found = getPref(localConfig, pref[0])
|
||||
if not found:
|
||||
found = getPref(moduleConfig, pref[0])
|
||||
|
||||
if not found:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have an attribute {pref[0]}.")
|
||||
else:
|
||||
print(f"{localConfig.__class__.__name__} and {moduleConfig.__class__.__name__} do not have attribute {pref[0]}.")
|
||||
|
||||
print("Completed getting preferences")
|
||||
|
||||
@@ -580,6 +605,8 @@ def subscribe():
|
||||
|
||||
def export_config(interface):
|
||||
"""used in--export-config"""
|
||||
configObj = {}
|
||||
|
||||
owner = interface.getLongName()
|
||||
owner_short = interface.getShortName()
|
||||
channel_url = interface.localNode.getURL()
|
||||
@@ -593,38 +620,34 @@ def export_config(interface):
|
||||
lon = pos.get('longitude')
|
||||
alt = pos.get('altitude')
|
||||
|
||||
config = "# start of Meshtastic configure yaml\n"
|
||||
if owner:
|
||||
config += f"owner: {owner}\n\n"
|
||||
configObj["owner"] = owner
|
||||
if owner_short:
|
||||
config += f"owner_short: {owner_short}\n\n"
|
||||
configObj["owner_short"] = owner_short
|
||||
if channel_url:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
config += f"channelUrl: {channel_url}\n\n"
|
||||
configObj["channelUrl"] = channel_url
|
||||
else:
|
||||
config += f"channel_url: {channel_url}\n\n"
|
||||
configObj["channel_url"] = channel_url
|
||||
if lat or lon or alt:
|
||||
config += "location:\n"
|
||||
if lat:
|
||||
config += f" lat: {lat}\n"
|
||||
if lon:
|
||||
config += f" lon: {lon}\n"
|
||||
if alt:
|
||||
config += f" alt: {alt}\n"
|
||||
config += "\n"
|
||||
preferences = f'{interface.localNode.radioConfig.preferences}'
|
||||
prefs = preferences.splitlines()
|
||||
if prefs:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
config += "userPrefs:\n"
|
||||
else:
|
||||
config += "user_prefs:\n"
|
||||
for pref in prefs:
|
||||
configObj["location"] = { "lat": lat, "lon": lon, "alt": alt }
|
||||
preferences = MessageToDict(interface.localNode.localConfig)
|
||||
if preferences:
|
||||
# Convert inner keys to correct snake/camelCase
|
||||
prefs = {}
|
||||
for pref in preferences:
|
||||
if Globals.getInstance().get_camel_case():
|
||||
# Note: This may not work if the value has '_'
|
||||
config += f" {meshtastic.util.snake_to_camel(meshtastic.util.quoteBooleans(pref))}\n"
|
||||
prefs[meshtastic.util.snake_to_camel(pref)] = preferences[pref]
|
||||
else:
|
||||
config += f" {meshtastic.util.quoteBooleans(pref)}\n"
|
||||
# TODO: Possibly convert camel to snake?
|
||||
prefs[pref] = preferences[pref]
|
||||
if Globals.getInstance().get_camel_case():
|
||||
configObj["userPrefs"] = preferences
|
||||
else:
|
||||
configObj["user_prefs"] = preferences
|
||||
|
||||
config = "# start of Meshtastic configure yaml\n"
|
||||
config += yaml.dump(configObj)
|
||||
print(config)
|
||||
return config
|
||||
|
||||
@@ -746,6 +769,9 @@ def initParser():
|
||||
parser.add_argument("--info", help="Read and display the radio config information",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument("--get-canned-message", help="Show the canned message plugin message",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument("--nodes", help="Print Node List in a pretty formatted table",
|
||||
action="store_true")
|
||||
|
||||
@@ -787,6 +813,9 @@ def initParser():
|
||||
"own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."),
|
||||
nargs=2, action='append')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-vlongslow", help="Change to the very long-range and slow channel", action='store_true')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-longslow", help="Change to the long-range and slow channel", action='store_true')
|
||||
|
||||
@@ -794,10 +823,10 @@ def initParser():
|
||||
"--ch-longfast", help="Change to the long-range and fast channel", action='store_true')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-mediumslow", help="Change to the medium-range and slow channel", action='store_true')
|
||||
"--ch-midslow", help="Change to the mid-range and slow channel", action='store_true')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-mediumfast", help="Change to the medium-range and fast channel", action='store_true')
|
||||
"--ch-midfast", help="Change to the mid-range and fast channel", action='store_true')
|
||||
|
||||
parser.add_argument(
|
||||
"--ch-shortslow", help="Change to the short-range and slow channel", action='store_true')
|
||||
@@ -809,10 +838,10 @@ def initParser():
|
||||
"--set-owner", help="Set device owner name", action="store")
|
||||
|
||||
parser.add_argument(
|
||||
"--set-owner-short", help="Set device owner short name", action="store")
|
||||
"--set-canned-message", help="Set the canned messages plugin message (up to 1000 characters).", action="store")
|
||||
|
||||
parser.add_argument(
|
||||
"--set-team", help="Set team affiliation (an invalid team will list valid values)", action="store")
|
||||
"--set-owner-short", help="Set device owner short name", action="store")
|
||||
|
||||
parser.add_argument(
|
||||
"--set-ham", help="Set licensed Ham ID and turn off encryption", action="store")
|
||||
@@ -832,6 +861,9 @@ def initParser():
|
||||
parser.add_argument(
|
||||
"--shutdown", help="Tell the destination node to shutdown", action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
"--device-metadata", help="Get the device metadata from the node", action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
"--reply", help="Reply to received messages",
|
||||
action="store_true")
|
||||
@@ -875,13 +907,6 @@ def initParser():
|
||||
parser.add_argument("--noproto", help="Don't start the API, just function as a dumb serial terminal.",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument('--setchan', dest='deprecated', nargs=2, action='append',
|
||||
help='Deprecated, use "--ch-set param value" instead')
|
||||
parser.add_argument('--set-router', dest='deprecated',
|
||||
action='store_true', help='Deprecated, use "--set is_router true" instead')
|
||||
parser.add_argument('--unset-router', dest='deprecated',
|
||||
action='store_false', help='Deprecated, use "--set is_router false" instead')
|
||||
|
||||
have_tunnel = platform.system() == 'Linux'
|
||||
if have_tunnel:
|
||||
parser.add_argument('--tunnel', action='store_true',
|
||||
|
||||
@@ -12,17 +12,20 @@ from google.protobuf import symbol_database as _symbol_database
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from . import cannedmessages_pb2 as cannedmessages__pb2
|
||||
from . import channel_pb2 as channel__pb2
|
||||
from . import config_pb2 as config__pb2
|
||||
from . import device_metadata_pb2 as device__metadata__pb2
|
||||
from . import mesh_pb2 as mesh__pb2
|
||||
from . import radioconfig_pb2 as radioconfig__pb2
|
||||
from . import module_config_pb2 as 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\x0b\x61\x64min.proto\x1a\rchannel.proto\x1a\x0c\x63onfig.proto\x1a\x15\x64\x65vice_metadata.proto\x1a\nmesh.proto\x1a\x13module_config.proto\"\xc7\r\n\x0c\x41\x64minMessage\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\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\x36\n\x12get_config_request\x18\n \x01(\x0e\x32\x18.AdminMessage.ConfigTypeH\x00\x12&\n\x13get_config_response\x18\x0b \x01(\x0b\x32\x07.ConfigH\x00\x12\x1d\n\nset_config\x18\x0c \x01(\x0b\x32\x07.ConfigH\x00\x12\x1c\n\x12\x63onfirm_set_config\x18\r \x01(\x08H\x00\x12\x43\n\x19get_module_config_request\x18\x0e \x01(\x0e\x32\x1e.AdminMessage.ModuleConfigTypeH\x00\x12\x33\n\x1aget_module_config_response\x18\x0f \x01(\x0b\x32\r.ModuleConfigH\x00\x12*\n\x11set_module_config\x18\x10 \x01(\x0b\x32\r.ModuleConfigH\x00\x12#\n\x19\x63onfirm_set_module_config\x18\x11 \x01(\x08H\x00\x12!\n\x17get_all_channel_request\x18\x12 \x01(\x08H\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_module_part1_request\x18$ \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part1_response\x18% \x01(\tH\x00\x12\x31\n\'get_canned_message_module_part2_request\x18& \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part2_response\x18\' \x01(\tH\x00\x12\x31\n\'get_canned_message_module_part3_request\x18( \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part3_response\x18) \x01(\tH\x00\x12\x31\n\'get_canned_message_module_part4_request\x18* \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part4_response\x18+ \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part1\x18, \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part2\x18- \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part3\x18. \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part4\x18/ \x01(\tH\x00\x12\x1a\n\x10shutdown_seconds\x18\x33 \x01(\x05H\x00\x12%\n\x1bget_device_metadata_request\x18\x34 \x01(\rH\x00\x12\x37\n\x1cget_device_metadata_response\x18\x35 \x01(\x0b\x32\x0f.DeviceMetadataH\x00\"|\n\nConfigType\x12\x11\n\rDEVICE_CONFIG\x10\x00\x12\x13\n\x0fPOSITION_CONFIG\x10\x01\x12\x10\n\x0cPOWER_CONFIG\x10\x02\x12\x0f\n\x0bWIFI_CONFIG\x10\x03\x12\x12\n\x0e\x44ISPLAY_CONFIG\x10\x04\x12\x0f\n\x0bLORA_CONFIG\x10\x05\"\xa6\x01\n\x10ModuleConfigType\x12\x0f\n\x0bMQTT_CONFIG\x10\x00\x12\x11\n\rSERIAL_CONFIG\x10\x01\x12\x13\n\x0f\x45XTNOTIF_CONFIG\x10\x02\x12\x17\n\x13STOREFORWARD_CONFIG\x10\x03\x12\x14\n\x10RANGETEST_CONFIG\x10\x04\x12\x14\n\x10TELEMETRY_CONFIG\x10\x05\x12\x14\n\x10\x43\x41NNEDMSG_CONFIG\x10\x06\x42\t\n\x07variantBG\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_ADMINMESSAGE = DESCRIPTOR.message_types_by_name['AdminMessage']
|
||||
_ADMINMESSAGE_CONFIGTYPE = _ADMINMESSAGE.enum_types_by_name['ConfigType']
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE = _ADMINMESSAGE.enum_types_by_name['ModuleConfigType']
|
||||
AdminMessage = _reflection.GeneratedProtocolMessageType('AdminMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ADMINMESSAGE,
|
||||
'__module__' : 'admin_pb2'
|
||||
@@ -34,6 +37,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\013AdminProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_ADMINMESSAGE._serialized_start=84
|
||||
_ADMINMESSAGE._serialized_end=1627
|
||||
_ADMINMESSAGE._serialized_start=101
|
||||
_ADMINMESSAGE._serialized_end=1836
|
||||
_ADMINMESSAGE_CONFIGTYPE._serialized_start=1532
|
||||
_ADMINMESSAGE_CONFIGTYPE._serialized_end=1656
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_start=1659
|
||||
_ADMINMESSAGE_MODULECONFIGTYPE._serialized_end=1825
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -13,9 +13,10 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from . import channel_pb2 as channel__pb2
|
||||
from . import config_pb2 as 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\rapponly.proto\x1a\rchannel.proto\x1a\x0c\x63onfig.proto\"Y\n\nChannelSet\x12\"\n\x08settings\x18\x01 \x03(\x0b\x32\x10.ChannelSettings\x12\'\n\x0blora_config\x18\x02 \x01(\x0b\x32\x12.Config.LoRaConfigBI\n\x13\x63om.geeksville.meshB\rAppOnlyProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +32,6 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_CHANNELSET._serialized_start=32
|
||||
_CHANNELSET._serialized_end=80
|
||||
_CHANNELSET._serialized_start=46
|
||||
_CHANNELSET._serialized_end=135
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -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\x14\x63\x61nnedmessages.proto\"w\n\x19\x43\x61nnedMessageModuleConfig\x12\x15\n\rmessagesPart1\x18\x0b \x01(\t\x12\x15\n\rmessagesPart2\x18\x0c \x01(\t\x12\x15\n\rmessagesPart3\x18\r \x01(\t\x12\x15\n\rmessagesPart4\x18\x0e \x01(\tBU\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_CANNEDMESSAGEPLUGINMESSAGEPART1 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart1']
|
||||
_CANNEDMESSAGEPLUGINMESSAGEPART2 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart2']
|
||||
_CANNEDMESSAGEPLUGINMESSAGEPART3 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart3']
|
||||
_CANNEDMESSAGEPLUGINMESSAGEPART4 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart4']
|
||||
_CANNEDMESSAGEPLUGINMESSAGEPART5 = DESCRIPTOR.message_types_by_name['CannedMessagePluginMessagePart5']
|
||||
CannedMessagePluginMessagePart1 = _reflection.GeneratedProtocolMessageType('CannedMessagePluginMessagePart1', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CANNEDMESSAGEPLUGINMESSAGEPART1,
|
||||
_CANNEDMESSAGEMODULECONFIG = DESCRIPTOR.message_types_by_name['CannedMessageModuleConfig']
|
||||
CannedMessageModuleConfig = _reflection.GeneratedProtocolMessageType('CannedMessageModuleConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CANNEDMESSAGEMODULECONFIG,
|
||||
'__module__' : 'cannedmessages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:CannedMessagePluginMessagePart1)
|
||||
# @@protoc_insertion_point(class_scope:CannedMessageModuleConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(CannedMessagePluginMessagePart1)
|
||||
|
||||
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)
|
||||
_sym_db.RegisterMessage(CannedMessageModuleConfig)
|
||||
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_CANNEDMESSAGEPLUGINMESSAGEPART1._serialized_start=24
|
||||
_CANNEDMESSAGEPLUGINMESSAGEPART1._serialized_end=71
|
||||
_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
|
||||
_CANNEDMESSAGEMODULECONFIG._serialized_start=24
|
||||
_CANNEDMESSAGEMODULECONFIG._serialized_end=143
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -14,13 +14,12 @@ _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\rchannel.proto\"\x7f\n\x0f\x43hannelSettings\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\"\x8b\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\"\n\x08settings\x18\x02 \x01(\x0b\x32\x10.ChannelSettings\x12\x1b\n\x04role\x18\x03 \x01(\x0e\x32\r.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42I\n\x13\x63om.geeksville.meshB\rChannelProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_CHANNELSETTINGS = DESCRIPTOR.message_types_by_name['ChannelSettings']
|
||||
_CHANNEL = DESCRIPTOR.message_types_by_name['Channel']
|
||||
_CHANNELSETTINGS_MODEMCONFIG = _CHANNELSETTINGS.enum_types_by_name['ModemConfig']
|
||||
_CHANNEL_ROLE = _CHANNEL.enum_types_by_name['Role']
|
||||
ChannelSettings = _reflection.GeneratedProtocolMessageType('ChannelSettings', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CHANNELSETTINGS,
|
||||
@@ -40,12 +39,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_CHANNELSETTINGS._serialized_start=18
|
||||
_CHANNELSETTINGS._serialized_end=419
|
||||
_CHANNELSETTINGS_MODEMCONFIG._serialized_start=281
|
||||
_CHANNELSETTINGS_MODEMCONFIG._serialized_end=419
|
||||
_CHANNEL._serialized_start=422
|
||||
_CHANNEL._serialized_end=561
|
||||
_CHANNEL_ROLE._serialized_start=513
|
||||
_CHANNEL_ROLE._serialized_end=561
|
||||
_CHANNELSETTINGS._serialized_start=17
|
||||
_CHANNELSETTINGS._serialized_end=144
|
||||
_CHANNEL._serialized_start=147
|
||||
_CHANNEL._serialized_end=286
|
||||
_CHANNEL_ROLE._serialized_start=238
|
||||
_CHANNEL_ROLE._serialized_end=286
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
122
meshtastic/config_pb2.py
Normal file
122
meshtastic/config_pb2.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: config.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\x0c\x63onfig.proto\"\xec\x12\n\x06\x43onfig\x12&\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32\x14.Config.DeviceConfigH\x00\x12*\n\x08position\x18\x02 \x01(\x0b\x32\x16.Config.PositionConfigH\x00\x12$\n\x05power\x18\x03 \x01(\x0b\x32\x13.Config.PowerConfigH\x00\x12\"\n\x04wifi\x18\x04 \x01(\x0b\x32\x12.Config.WiFiConfigH\x00\x12(\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfigH\x00\x12\"\n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfigH\x00\x1a\xd8\x01\n\x0c\x44\x65viceConfig\x12\'\n\x04role\x18\x01 \x01(\x0e\x32\x19.Config.DeviceConfig.Role\x12\x17\n\x0fserial_disabled\x18\x02 \x01(\x08\x12\x15\n\rfactory_reset\x18\x03 \x01(\x08\x12\x19\n\x11\x64\x65\x62ug_log_enabled\x18\x04 \x01(\x08\x12\x12\n\nntp_server\x18\x05 \x01(\t\"@\n\x04Role\x12\n\n\x06\x43lient\x10\x00\x12\x0e\n\nClientMute\x10\x01\x12\n\n\x06Router\x10\x02\x12\x10\n\x0cRouterClient\x10\x03\x1a\xa8\x03\n\x0ePositionConfig\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12)\n!position_broadcast_smart_disabled\x18\x02 \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\x03 \x01(\x08\x12\x14\n\x0cgps_disabled\x18\x05 \x01(\x08\x12\x1b\n\x13gps_update_interval\x18\x06 \x01(\r\x12\x18\n\x10gps_attempt_time\x18\x07 \x01(\r\x12\x16\n\x0eposition_flags\x18\n \x01(\r\"\xcc\x01\n\rPositionFlags\x12\x11\n\rPOS_UNDEFINED\x10\x00\x12\x10\n\x0cPOS_ALTITUDE\x10\x01\x12\x0f\n\x0bPOS_ALT_MSL\x10\x02\x12\x0f\n\x0bPOS_GEO_SEP\x10\x04\x12\x0b\n\x07POS_DOP\x10\x08\x12\r\n\tPOS_HVDOP\x10\x10\x12\x11\n\rPOS_SATINVIEW\x10 \x12\x0f\n\x0bPOS_SEQ_NOS\x10@\x12\x12\n\rPOS_TIMESTAMP\x10\x80\x01\x12\x10\n\x0bPOS_HEADING\x10\x80\x02\x12\x0e\n\tPOS_SPEED\x10\x80\x04\x1a\xf4\x03\n\x0bPowerConfig\x12\x39\n\x0e\x63harge_current\x18\x01 \x01(\x0e\x32!.Config.PowerConfig.ChargeCurrent\x12\x17\n\x0fis_power_saving\x18\x02 \x01(\x08\x12&\n\x1eon_battery_shutdown_after_secs\x18\x04 \x01(\r\x12\x1f\n\x17\x61\x64\x63_multiplier_override\x18\x06 \x01(\x02\x12\x1b\n\x13wait_bluetooth_secs\x18\x07 \x01(\r\x12\x1d\n\x15mesh_sds_timeout_secs\x18\t \x01(\r\x12\x10\n\x08sds_secs\x18\n \x01(\r\x12\x0f\n\x07ls_secs\x18\x0b \x01(\r\x12\x15\n\rmin_wake_secs\x18\x0c \x01(\r\"\xd1\x01\n\rChargeCurrent\x12\x0b\n\x07MAUnset\x10\x00\x12\t\n\x05MA100\x10\x01\x12\t\n\x05MA190\x10\x02\x12\t\n\x05MA280\x10\x03\x12\t\n\x05MA360\x10\x04\x12\t\n\x05MA450\x10\x05\x12\t\n\x05MA550\x10\x06\x12\t\n\x05MA630\x10\x07\x12\t\n\x05MA700\x10\x08\x12\t\n\x05MA780\x10\t\x12\t\n\x05MA880\x10\n\x12\t\n\x05MA960\x10\x0b\x12\n\n\x06MA1000\x10\x0c\x12\n\n\x06MA1080\x10\r\x12\n\n\x06MA1160\x10\x0e\x12\n\n\x06MA1240\x10\x0f\x12\n\n\x06MA1320\x10\x10\x1a\xa3\x01\n\nWiFiConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12)\n\x04mode\x18\x02 \x01(\x0e\x32\x1b.Config.WiFiConfig.WiFiMode\x12\x0c\n\x04ssid\x18\x03 \x01(\t\x12\x0b\n\x03psk\x18\x04 \x01(\t\">\n\x08WiFiMode\x12\n\n\x06\x43lient\x10\x00\x12\x0f\n\x0b\x41\x63\x63\x65ssPoint\x10\x01\x12\x15\n\x11\x41\x63\x63\x65ssPointHidden\x10\x02\x1a\xaa\x02\n\rDisplayConfig\x12\x16\n\x0escreen_on_secs\x18\x01 \x01(\r\x12=\n\ngps_format\x18\x02 \x01(\x0e\x32).Config.DisplayConfig.GpsCoordinateFormat\x12!\n\x19\x61uto_screen_carousel_secs\x18\x03 \x01(\r\x12\x19\n\x11\x63ompass_north_top\x18\x04 \x01(\x08\"\x83\x01\n\x13GpsCoordinateFormat\x12\x10\n\x0cGpsFormatDec\x10\x00\x12\x10\n\x0cGpsFormatDMS\x10\x01\x12\x10\n\x0cGpsFormatUTM\x10\x02\x12\x11\n\rGpsFormatMGRS\x10\x03\x12\x10\n\x0cGpsFormatOLC\x10\x04\x12\x11\n\rGpsFormatOSGR\x10\x05\x1a\x93\x04\n\nLoRaConfig\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x34\n\x0cmodem_preset\x18\x02 \x01(\x0e\x32\x1e.Config.LoRaConfig.ModemPreset\x12\x11\n\tbandwidth\x18\x03 \x01(\r\x12\x15\n\rspread_factor\x18\x04 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x05 \x01(\r\x12\x18\n\x10\x66requency_offset\x18\x06 \x01(\x02\x12-\n\x06region\x18\x07 \x01(\x0e\x32\x1d.Config.LoRaConfig.RegionCode\x12\x11\n\thop_limit\x18\x08 \x01(\r\x12\x13\n\x0btx_disabled\x18\t \x01(\x08\x12\x17\n\x0fignore_incoming\x18g \x03(\r\"\x81\x01\n\nRegionCode\x12\t\n\x05Unset\x10\x00\x12\x06\n\x02US\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x45U868\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08\x12\x06\n\x02RU\x10\t\x12\x06\n\x02IN\x10\n\x12\t\n\x05NZ865\x10\x0b\x12\x06\n\x02TH\x10\x0c\"p\n\x0bModemPreset\x12\x0c\n\x08LongFast\x10\x00\x12\x0c\n\x08LongSlow\x10\x01\x12\r\n\tVLongSlow\x10\x02\x12\x0b\n\x07MedSlow\x10\x03\x12\x0b\n\x07MedFast\x10\x04\x12\r\n\tShortSlow\x10\x05\x12\r\n\tShortFast\x10\x06\x42\x10\n\x0epayloadVariantBH\n\x13\x63om.geeksville.meshB\x0c\x43onfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_CONFIG = DESCRIPTOR.message_types_by_name['Config']
|
||||
_CONFIG_DEVICECONFIG = _CONFIG.nested_types_by_name['DeviceConfig']
|
||||
_CONFIG_POSITIONCONFIG = _CONFIG.nested_types_by_name['PositionConfig']
|
||||
_CONFIG_POWERCONFIG = _CONFIG.nested_types_by_name['PowerConfig']
|
||||
_CONFIG_WIFICONFIG = _CONFIG.nested_types_by_name['WiFiConfig']
|
||||
_CONFIG_DISPLAYCONFIG = _CONFIG.nested_types_by_name['DisplayConfig']
|
||||
_CONFIG_LORACONFIG = _CONFIG.nested_types_by_name['LoRaConfig']
|
||||
_CONFIG_DEVICECONFIG_ROLE = _CONFIG_DEVICECONFIG.enum_types_by_name['Role']
|
||||
_CONFIG_POSITIONCONFIG_POSITIONFLAGS = _CONFIG_POSITIONCONFIG.enum_types_by_name['PositionFlags']
|
||||
_CONFIG_POWERCONFIG_CHARGECURRENT = _CONFIG_POWERCONFIG.enum_types_by_name['ChargeCurrent']
|
||||
_CONFIG_WIFICONFIG_WIFIMODE = _CONFIG_WIFICONFIG.enum_types_by_name['WiFiMode']
|
||||
_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT = _CONFIG_DISPLAYCONFIG.enum_types_by_name['GpsCoordinateFormat']
|
||||
_CONFIG_LORACONFIG_REGIONCODE = _CONFIG_LORACONFIG.enum_types_by_name['RegionCode']
|
||||
_CONFIG_LORACONFIG_MODEMPRESET = _CONFIG_LORACONFIG.enum_types_by_name['ModemPreset']
|
||||
Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
|
||||
|
||||
'DeviceConfig' : _reflection.GeneratedProtocolMessageType('DeviceConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONFIG_DEVICECONFIG,
|
||||
'__module__' : 'config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Config.DeviceConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'PositionConfig' : _reflection.GeneratedProtocolMessageType('PositionConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONFIG_POSITIONCONFIG,
|
||||
'__module__' : 'config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Config.PositionConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'PowerConfig' : _reflection.GeneratedProtocolMessageType('PowerConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONFIG_POWERCONFIG,
|
||||
'__module__' : 'config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Config.PowerConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'WiFiConfig' : _reflection.GeneratedProtocolMessageType('WiFiConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONFIG_WIFICONFIG,
|
||||
'__module__' : 'config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Config.WiFiConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'DisplayConfig' : _reflection.GeneratedProtocolMessageType('DisplayConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONFIG_DISPLAYCONFIG,
|
||||
'__module__' : 'config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Config.DisplayConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'LoRaConfig' : _reflection.GeneratedProtocolMessageType('LoRaConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CONFIG_LORACONFIG,
|
||||
'__module__' : 'config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Config.LoRaConfig)
|
||||
})
|
||||
,
|
||||
'DESCRIPTOR' : _CONFIG,
|
||||
'__module__' : 'config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Config)
|
||||
})
|
||||
_sym_db.RegisterMessage(Config)
|
||||
_sym_db.RegisterMessage(Config.DeviceConfig)
|
||||
_sym_db.RegisterMessage(Config.PositionConfig)
|
||||
_sym_db.RegisterMessage(Config.PowerConfig)
|
||||
_sym_db.RegisterMessage(Config.WiFiConfig)
|
||||
_sym_db.RegisterMessage(Config.DisplayConfig)
|
||||
_sym_db.RegisterMessage(Config.LoRaConfig)
|
||||
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014ConfigProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_CONFIG._serialized_start=17
|
||||
_CONFIG._serialized_end=2429
|
||||
_CONFIG_DEVICECONFIG._serialized_start=264
|
||||
_CONFIG_DEVICECONFIG._serialized_end=480
|
||||
_CONFIG_DEVICECONFIG_ROLE._serialized_start=416
|
||||
_CONFIG_DEVICECONFIG_ROLE._serialized_end=480
|
||||
_CONFIG_POSITIONCONFIG._serialized_start=483
|
||||
_CONFIG_POSITIONCONFIG._serialized_end=907
|
||||
_CONFIG_POSITIONCONFIG_POSITIONFLAGS._serialized_start=703
|
||||
_CONFIG_POSITIONCONFIG_POSITIONFLAGS._serialized_end=907
|
||||
_CONFIG_POWERCONFIG._serialized_start=910
|
||||
_CONFIG_POWERCONFIG._serialized_end=1410
|
||||
_CONFIG_POWERCONFIG_CHARGECURRENT._serialized_start=1201
|
||||
_CONFIG_POWERCONFIG_CHARGECURRENT._serialized_end=1410
|
||||
_CONFIG_WIFICONFIG._serialized_start=1413
|
||||
_CONFIG_WIFICONFIG._serialized_end=1576
|
||||
_CONFIG_WIFICONFIG_WIFIMODE._serialized_start=1514
|
||||
_CONFIG_WIFICONFIG_WIFIMODE._serialized_end=1576
|
||||
_CONFIG_DISPLAYCONFIG._serialized_start=1579
|
||||
_CONFIG_DISPLAYCONFIG._serialized_end=1877
|
||||
_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_start=1746
|
||||
_CONFIG_DISPLAYCONFIG_GPSCOORDINATEFORMAT._serialized_end=1877
|
||||
_CONFIG_LORACONFIG._serialized_start=1880
|
||||
_CONFIG_LORACONFIG._serialized_end=2411
|
||||
_CONFIG_LORACONFIG_REGIONCODE._serialized_start=2168
|
||||
_CONFIG_LORACONFIG_REGIONCODE._serialized_end=2297
|
||||
_CONFIG_LORACONFIG_MODEMPRESET._serialized_start=2299
|
||||
_CONFIG_LORACONFIG_MODEMPRESET._serialized_end=2411
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
35
meshtastic/device_metadata_pb2.py
Normal file
35
meshtastic/device_metadata_pb2.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: 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()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x64\x65vice_metadata.proto\"H\n\x0e\x44\x65viceMetadata\x12\x18\n\x10\x66irmware_version\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65vice_state_version\x18\x02 \x01(\rBP\n\x13\x63om.geeksville.meshB\x14\x44\x65viceMetadataProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_DEVICEMETADATA = DESCRIPTOR.message_types_by_name['DeviceMetadata']
|
||||
DeviceMetadata = _reflection.GeneratedProtocolMessageType('DeviceMetadata', (_message.Message,), {
|
||||
'DESCRIPTOR' : _DEVICEMETADATA,
|
||||
'__module__' : '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\024DeviceMetadataProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_DEVICEMETADATA._serialized_start=25
|
||||
_DEVICEMETADATA._serialized_end=97
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -2,6 +2,7 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: deviceonly.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
|
||||
@@ -14,32 +15,20 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
from . import channel_pb2 as channel__pb2
|
||||
from . import mesh_pb2 as mesh__pb2
|
||||
from . import radioconfig_pb2 as radioconfig__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_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\x10\x64\x65viceonly.proto\x1a\rchannel.proto\x1a\nmesh.proto\"\xe6\x01\n\x0b\x44\x65viceState\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08J\x04\x08\x0c\x10\r\":\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\x84\x01\n\x08OEMStore\x12\x16\n\x0eoem_icon_width\x18\x01 \x01(\r\x12\x17\n\x0foem_icon_height\x18\x02 \x01(\r\x12\x15\n\roem_icon_bits\x18\x03 \x01(\x0c\x12\x1e\n\x08oem_font\x18\x04 \x01(\x0e\x32\x0c.ScreenFonts\x12\x10\n\x08oem_text\x18\x05 \x01(\t*>\n\x0bScreenFonts\x12\x0e\n\nFONT_SMALL\x10\x00\x12\x0f\n\x0b\x46ONT_MEDIUM\x10\x01\x12\x0e\n\nFONT_LARGE\x10\x02\x42\x46\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
_SCREENFONTS = DESCRIPTOR.enum_types_by_name['ScreenFonts']
|
||||
ScreenFonts = enum_type_wrapper.EnumTypeWrapper(_SCREENFONTS)
|
||||
FONT_SMALL = 0
|
||||
FONT_MEDIUM = 1
|
||||
FONT_LARGE = 2
|
||||
|
||||
|
||||
|
||||
_LEGACYRADIOCONFIG = DESCRIPTOR.message_types_by_name['LegacyRadioConfig']
|
||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES = _LEGACYRADIOCONFIG.nested_types_by_name['LegacyPreferences']
|
||||
_DEVICESTATE = DESCRIPTOR.message_types_by_name['DeviceState']
|
||||
_CHANNELFILE = DESCRIPTOR.message_types_by_name['ChannelFile']
|
||||
LegacyRadioConfig = _reflection.GeneratedProtocolMessageType('LegacyRadioConfig', (_message.Message,), {
|
||||
|
||||
'LegacyPreferences' : _reflection.GeneratedProtocolMessageType('LegacyPreferences', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LEGACYRADIOCONFIG_LEGACYPREFERENCES,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LegacyRadioConfig.LegacyPreferences)
|
||||
})
|
||||
,
|
||||
'DESCRIPTOR' : _LEGACYRADIOCONFIG,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LegacyRadioConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(LegacyRadioConfig)
|
||||
_sym_db.RegisterMessage(LegacyRadioConfig.LegacyPreferences)
|
||||
|
||||
_OEMSTORE = DESCRIPTOR.message_types_by_name['OEMStore']
|
||||
DeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message.Message,), {
|
||||
'DESCRIPTOR' : _DEVICESTATE,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
@@ -54,16 +43,23 @@ ChannelFile = _reflection.GeneratedProtocolMessageType('ChannelFile', (_message.
|
||||
})
|
||||
_sym_db.RegisterMessage(ChannelFile)
|
||||
|
||||
OEMStore = _reflection.GeneratedProtocolMessageType('OEMStore', (_message.Message,), {
|
||||
'DESCRIPTOR' : _OEMSTORE,
|
||||
'__module__' : 'deviceonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:OEMStore)
|
||||
})
|
||||
_sym_db.RegisterMessage(OEMStore)
|
||||
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_LEGACYRADIOCONFIG._serialized_start=67
|
||||
_LEGACYRADIOCONFIG._serialized_end=195
|
||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES._serialized_start=147
|
||||
_LEGACYRADIOCONFIG_LEGACYPREFERENCES._serialized_end=195
|
||||
_DEVICESTATE._serialized_start=198
|
||||
_DEVICESTATE._serialized_end=694
|
||||
_CHANNELFILE._serialized_start=696
|
||||
_CHANNELFILE._serialized_end=737
|
||||
_SCREENFONTS._serialized_start=475
|
||||
_SCREENFONTS._serialized_end=537
|
||||
_DEVICESTATE._serialized_start=48
|
||||
_DEVICESTATE._serialized_end=278
|
||||
_CHANNELFILE._serialized_start=280
|
||||
_CHANNELFILE._serialized_end=338
|
||||
_OEMSTORE._serialized_start=341
|
||||
_OEMSTORE._serialized_end=473
|
||||
# @@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)
|
||||
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: 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 . import config_pb2 as config__pb2
|
||||
from . import module_config_pb2 as module__config__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0flocalonly.proto\x1a\x0c\x63onfig.proto\x1a\x13module_config.proto\"\xfe\x01\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\x04wifi\x18\x04 \x01(\x0b\x32\x12.Config.WiFiConfig\x12&\n\x07\x64isplay\x18\x05 \x01(\x0b\x32\x15.Config.DisplayConfig\x12 \n\x04lora\x18\x06 \x01(\x0b\x32\x12.Config.LoRaConfig\x12\x0f\n\x07version\x18\x07 \x01(\r\"\x9a\x03\n\x11LocalModuleConfig\x12&\n\x04mqtt\x18\x01 \x01(\x0b\x32\x18.ModuleConfig.MQTTConfig\x12*\n\x06serial\x18\x02 \x01(\x0b\x32\x1a.ModuleConfig.SerialConfig\x12G\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32(.ModuleConfig.ExternalNotificationConfig\x12\x37\n\rstore_forward\x18\x04 \x01(\x0b\x32 .ModuleConfig.StoreForwardConfig\x12\x31\n\nrange_test\x18\x05 \x01(\x0b\x32\x1d.ModuleConfig.RangeTestConfig\x12\x30\n\ttelemetry\x18\x06 \x01(\x0b\x32\x1d.ModuleConfig.TelemetryConfig\x12\x39\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32!.ModuleConfig.CannedMessageConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBK\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_LOCALCONFIG = DESCRIPTOR.message_types_by_name['LocalConfig']
|
||||
_LOCALMODULECONFIG = DESCRIPTOR.message_types_by_name['LocalModuleConfig']
|
||||
LocalConfig = _reflection.GeneratedProtocolMessageType('LocalConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LOCALCONFIG,
|
||||
'__module__' : 'localonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LocalConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(LocalConfig)
|
||||
|
||||
LocalModuleConfig = _reflection.GeneratedProtocolMessageType('LocalModuleConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LOCALMODULECONFIG,
|
||||
'__module__' : 'localonly_pb2'
|
||||
# @@protoc_insertion_point(class_scope:LocalModuleConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(LocalModuleConfig)
|
||||
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_LOCALCONFIG._serialized_start=55
|
||||
_LOCALCONFIG._serialized_end=309
|
||||
_LOCALMODULECONFIG._serialized_start=312
|
||||
_LOCALMODULECONFIG._serialized_end=722
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -254,11 +254,12 @@ class MeshInterface:
|
||||
meshPacket.decoded.payload = data
|
||||
meshPacket.decoded.portnum = portNum
|
||||
meshPacket.decoded.want_response = wantResponse
|
||||
meshPacket.id = self._generatePacketId()
|
||||
|
||||
if onResponse is not None:
|
||||
self._addResponseHandler(meshPacket.id, onResponse)
|
||||
p = self._sendPacket(meshPacket, destinationId,
|
||||
wantAck=wantAck, hopLimit=hopLimit)
|
||||
if onResponse is not None:
|
||||
self._addResponseHandler(p.id, onResponse)
|
||||
return p
|
||||
|
||||
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
|
||||
@@ -421,8 +422,8 @@ class MeshInterface:
|
||||
"""We need to send a heartbeat message to the device every X seconds"""
|
||||
def callback():
|
||||
self.heartbeatTimer = None
|
||||
prefs = self.localNode.radioConfig.preferences
|
||||
i = prefs.phone_timeout_secs / 2
|
||||
prefs = self.localNode.localConfig
|
||||
i = prefs.power.ls_secs / 2
|
||||
logging.debug(f"Sending heartbeat, interval {i}")
|
||||
if i != 0:
|
||||
self.heartbeatTimer = threading.Timer(i, callback)
|
||||
@@ -531,14 +532,44 @@ class MeshInterface:
|
||||
# stream API fromRadio.config_complete_id
|
||||
logging.debug(f"Config complete ID {self.configId}")
|
||||
self._handleConfigComplete()
|
||||
|
||||
elif fromRadio.HasField("packet"):
|
||||
self._handlePacketFromRadio(fromRadio.packet)
|
||||
|
||||
elif fromRadio.rebooted:
|
||||
# Tell clients the device went away. Careful not to call the overridden
|
||||
# subclass version that closes the serial port
|
||||
MeshInterface._disconnected(self)
|
||||
|
||||
self._startConfig() # redownload the node db etc...
|
||||
|
||||
elif fromRadio.config or fromRadio.moduleConfig:
|
||||
if fromRadio.config.HasField("device"):
|
||||
self.localNode.localConfig.device.CopyFrom(fromRadio.config.device)
|
||||
elif fromRadio.config.HasField("position"):
|
||||
self.localNode.localConfig.position.CopyFrom(fromRadio.config.position)
|
||||
elif fromRadio.config.HasField("power"):
|
||||
self.localNode.localConfig.power.CopyFrom(fromRadio.config.power)
|
||||
elif fromRadio.config.HasField("wifi"):
|
||||
self.localNode.localConfig.wifi.CopyFrom(fromRadio.config.wifi)
|
||||
elif fromRadio.config.HasField("display"):
|
||||
self.localNode.localConfig.display.CopyFrom(fromRadio.config.display)
|
||||
elif fromRadio.config.HasField("lora"):
|
||||
self.localNode.localConfig.lora.CopyFrom(fromRadio.config.lora)
|
||||
|
||||
elif fromRadio.moduleConfig.HasField("mqtt"):
|
||||
self.localNode.moduleConfig.mqtt.CopyFrom(fromRadio.moduleConfig.mqtt)
|
||||
elif fromRadio.moduleConfig.HasField("serial"):
|
||||
self.localNode.moduleConfig.serial.CopyFrom(fromRadio.moduleConfig.serial)
|
||||
elif fromRadio.moduleConfig.HasField("external_notification"):
|
||||
self.localNode.moduleConfig.external_notification.CopyFrom(fromRadio.moduleConfig.external_notification)
|
||||
elif fromRadio.moduleConfig.HasField("range_test"):
|
||||
self.localNode.moduleConfig.range_test.CopyFrom(fromRadio.moduleConfig.range_test)
|
||||
elif fromRadio.moduleConfig.HasField("telemetry"):
|
||||
self.localNode.moduleConfig.telemetry.CopyFrom(fromRadio.moduleConfig.telemetry)
|
||||
elif fromRadio.moduleConfig.HasField("canned_message"):
|
||||
self.localNode.moduleConfig.canned_message.CopyFrom(fromRadio.moduleConfig.canned_message)
|
||||
|
||||
else:
|
||||
logging.debug("Unexpected FromRadio payload")
|
||||
|
||||
@@ -631,11 +662,13 @@ class MeshInterface:
|
||||
# asObj = DotMap(asDict)
|
||||
topic = "meshtastic.receive" # Generic unknown packet type
|
||||
|
||||
decoded = asDict["decoded"]
|
||||
# The default MessageToDict converts byte arrays into base64 strings.
|
||||
# We don't want that - it messes up data payload. So slam in the correct
|
||||
# byte array.
|
||||
decoded["payload"] = meshPacket.decoded.payload
|
||||
decoded = None
|
||||
if 'decoded' in asDict:
|
||||
decoded = asDict["decoded"]
|
||||
# The default MessageToDict converts byte arrays into base64 strings.
|
||||
# We don't want that - it messes up data payload. So slam in the correct
|
||||
# byte array.
|
||||
decoded["payload"] = meshPacket.decoded.payload
|
||||
|
||||
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not
|
||||
# set it will not be populated at all to make API usage easier, set
|
||||
|
||||
File diff suppressed because one or more lines are too long
122
meshtastic/module_config_pb2.py
Normal file
122
meshtastic/module_config_pb2.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: module_config.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 . import telemetry_pb2 as telemetry__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13module_config.proto\x1a\x0ftelemetry.proto\"\xa9\x11\n\x0cModuleConfig\x12(\n\x04mqtt\x18\x01 \x01(\x0b\x32\x18.ModuleConfig.MQTTConfigH\x00\x12,\n\x06serial\x18\x02 \x01(\x0b\x32\x1a.ModuleConfig.SerialConfigH\x00\x12I\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32(.ModuleConfig.ExternalNotificationConfigH\x00\x12\x39\n\rstore_forward\x18\x04 \x01(\x0b\x32 .ModuleConfig.StoreForwardConfigH\x00\x12\x33\n\nrange_test\x18\x05 \x01(\x0b\x32\x1d.ModuleConfig.RangeTestConfigH\x00\x12\x32\n\ttelemetry\x18\x06 \x01(\x0b\x32\x1d.ModuleConfig.TelemetryConfigH\x00\x12;\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32!.ModuleConfig.CannedMessageConfigH\x00\x1ao\n\nMQTTConfig\x12\x10\n\x08\x64isabled\x18\x01 \x01(\x08\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x10\n\x08password\x18\x04 \x01(\t\x12\x1a\n\x12\x65ncryption_enabled\x18\x05 \x01(\x08\x1a\x93\x04\n\x0cSerialConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0c\n\x04\x65\x63ho\x18\x02 \x01(\x08\x12\x0b\n\x03rxd\x18\x03 \x01(\r\x12\x0b\n\x03txd\x18\x04 \x01(\r\x12\x34\n\x04\x62\x61ud\x18\x05 \x01(\x0e\x32&.ModuleConfig.SerialConfig.Serial_Baud\x12\x0f\n\x07timeout\x18\x06 \x01(\r\x12\x34\n\x04mode\x18\x07 \x01(\x0e\x32&.ModuleConfig.SerialConfig.Serial_Mode\"\x8a\x02\n\x0bSerial_Baud\x12\x10\n\x0c\x42\x41UD_Default\x10\x00\x12\x0c\n\x08\x42\x41UD_110\x10\x01\x12\x0c\n\x08\x42\x41UD_300\x10\x02\x12\x0c\n\x08\x42\x41UD_600\x10\x03\x12\r\n\tBAUD_1200\x10\x04\x12\r\n\tBAUD_2400\x10\x05\x12\r\n\tBAUD_4800\x10\x06\x12\r\n\tBAUD_9600\x10\x07\x12\x0e\n\nBAUD_19200\x10\x08\x12\x0e\n\nBAUD_38400\x10\t\x12\x0e\n\nBAUD_57600\x10\n\x12\x0f\n\x0b\x42\x41UD_115200\x10\x0b\x12\x0f\n\x0b\x42\x41UD_230400\x10\x0c\x12\x0f\n\x0b\x42\x41UD_460800\x10\r\x12\x0f\n\x0b\x42\x41UD_576000\x10\x0e\x12\x0f\n\x0b\x42\x41UD_921600\x10\x0f\"@\n\x0bSerial_Mode\x12\x10\n\x0cMODE_Default\x10\x00\x12\x0f\n\x0bMODE_SIMPLE\x10\x01\x12\x0e\n\nMODE_PROTO\x10\x02\x1a\x8b\x01\n\x1a\x45xternalNotificationConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\toutput_ms\x18\x02 \x01(\r\x12\x0e\n\x06output\x18\x03 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x04 \x01(\x08\x12\x15\n\ralert_message\x18\x05 \x01(\x08\x12\x12\n\nalert_bell\x18\x06 \x01(\x08\x1a\x84\x01\n\x12StoreForwardConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\theartbeat\x18\x02 \x01(\x08\x12\x0f\n\x07records\x18\x03 \x01(\r\x12\x1a\n\x12history_return_max\x18\x04 \x01(\r\x12\x1d\n\x15history_return_window\x18\x05 \x01(\r\x1a@\n\x0fRangeTestConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0e\n\x06sender\x18\x02 \x01(\r\x12\x0c\n\x04save\x18\x03 \x01(\x08\x1a\xcb\x01\n\x0fTelemetryConfig\x12\x1e\n\x16\x64\x65vice_update_interval\x18\x01 \x01(\r\x12#\n\x1b\x65nvironment_update_interval\x18\x02 \x01(\r\x12\'\n\x1f\x65nvironment_measurement_enabled\x18\x03 \x01(\x08\x12\"\n\x1a\x65nvironment_screen_enabled\x18\x04 \x01(\x08\x12&\n\x1e\x65nvironment_display_fahrenheit\x18\x07 \x01(\x08\x1a\xd6\x04\n\x13\x43\x61nnedMessageConfig\x12\x17\n\x0frotary1_enabled\x18\x01 \x01(\x08\x12\x19\n\x11inputbroker_pin_a\x18\x02 \x01(\r\x12\x19\n\x11inputbroker_pin_b\x18\x03 \x01(\r\x12\x1d\n\x15inputbroker_pin_press\x18\x04 \x01(\r\x12N\n\x14inputbroker_event_cw\x18\x05 \x01(\x0e\x32\x30.ModuleConfig.CannedMessageConfig.InputEventChar\x12O\n\x15inputbroker_event_ccw\x18\x06 \x01(\x0e\x32\x30.ModuleConfig.CannedMessageConfig.InputEventChar\x12Q\n\x17inputbroker_event_press\x18\x07 \x01(\x0e\x32\x30.ModuleConfig.CannedMessageConfig.InputEventChar\x12\x17\n\x0fupdown1_enabled\x18\x08 \x01(\x08\x12\x0f\n\x07\x65nabled\x18\t \x01(\x08\x12\x1a\n\x12\x61llow_input_source\x18\n \x01(\t\x12\x11\n\tsend_bell\x18\x0b \x01(\x08\"\x83\x01\n\x0eInputEventChar\x12\x0c\n\x08KEY_NONE\x10\x00\x12\n\n\x06KEY_UP\x10\x11\x12\x0c\n\x08KEY_DOWN\x10\x12\x12\x0c\n\x08KEY_LEFT\x10\x13\x12\r\n\tKEY_RIGHT\x10\x14\x12\x0e\n\nKEY_SELECT\x10\n\x12\x0c\n\x08KEY_BACK\x10\x1b\x12\x0e\n\nKEY_CANCEL\x10\x18\x42\x10\n\x0epayloadVariantBN\n\x13\x63om.geeksville.meshB\x12ModuleConfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
|
||||
|
||||
_MODULECONFIG = DESCRIPTOR.message_types_by_name['ModuleConfig']
|
||||
_MODULECONFIG_MQTTCONFIG = _MODULECONFIG.nested_types_by_name['MQTTConfig']
|
||||
_MODULECONFIG_SERIALCONFIG = _MODULECONFIG.nested_types_by_name['SerialConfig']
|
||||
_MODULECONFIG_EXTERNALNOTIFICATIONCONFIG = _MODULECONFIG.nested_types_by_name['ExternalNotificationConfig']
|
||||
_MODULECONFIG_STOREFORWARDCONFIG = _MODULECONFIG.nested_types_by_name['StoreForwardConfig']
|
||||
_MODULECONFIG_RANGETESTCONFIG = _MODULECONFIG.nested_types_by_name['RangeTestConfig']
|
||||
_MODULECONFIG_TELEMETRYCONFIG = _MODULECONFIG.nested_types_by_name['TelemetryConfig']
|
||||
_MODULECONFIG_CANNEDMESSAGECONFIG = _MODULECONFIG.nested_types_by_name['CannedMessageConfig']
|
||||
_MODULECONFIG_SERIALCONFIG_SERIAL_BAUD = _MODULECONFIG_SERIALCONFIG.enum_types_by_name['Serial_Baud']
|
||||
_MODULECONFIG_SERIALCONFIG_SERIAL_MODE = _MODULECONFIG_SERIALCONFIG.enum_types_by_name['Serial_Mode']
|
||||
_MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR = _MODULECONFIG_CANNEDMESSAGECONFIG.enum_types_by_name['InputEventChar']
|
||||
ModuleConfig = _reflection.GeneratedProtocolMessageType('ModuleConfig', (_message.Message,), {
|
||||
|
||||
'MQTTConfig' : _reflection.GeneratedProtocolMessageType('MQTTConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MODULECONFIG_MQTTCONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig.MQTTConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'SerialConfig' : _reflection.GeneratedProtocolMessageType('SerialConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MODULECONFIG_SERIALCONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig.SerialConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'ExternalNotificationConfig' : _reflection.GeneratedProtocolMessageType('ExternalNotificationConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MODULECONFIG_EXTERNALNOTIFICATIONCONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig.ExternalNotificationConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'StoreForwardConfig' : _reflection.GeneratedProtocolMessageType('StoreForwardConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MODULECONFIG_STOREFORWARDCONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig.StoreForwardConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'RangeTestConfig' : _reflection.GeneratedProtocolMessageType('RangeTestConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MODULECONFIG_RANGETESTCONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig.RangeTestConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'TelemetryConfig' : _reflection.GeneratedProtocolMessageType('TelemetryConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MODULECONFIG_TELEMETRYCONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig.TelemetryConfig)
|
||||
})
|
||||
,
|
||||
|
||||
'CannedMessageConfig' : _reflection.GeneratedProtocolMessageType('CannedMessageConfig', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MODULECONFIG_CANNEDMESSAGECONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig.CannedMessageConfig)
|
||||
})
|
||||
,
|
||||
'DESCRIPTOR' : _MODULECONFIG,
|
||||
'__module__' : 'module_config_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ModuleConfig)
|
||||
})
|
||||
_sym_db.RegisterMessage(ModuleConfig)
|
||||
_sym_db.RegisterMessage(ModuleConfig.MQTTConfig)
|
||||
_sym_db.RegisterMessage(ModuleConfig.SerialConfig)
|
||||
_sym_db.RegisterMessage(ModuleConfig.ExternalNotificationConfig)
|
||||
_sym_db.RegisterMessage(ModuleConfig.StoreForwardConfig)
|
||||
_sym_db.RegisterMessage(ModuleConfig.RangeTestConfig)
|
||||
_sym_db.RegisterMessage(ModuleConfig.TelemetryConfig)
|
||||
_sym_db.RegisterMessage(ModuleConfig.CannedMessageConfig)
|
||||
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\022ModuleConfigProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_MODULECONFIG._serialized_start=41
|
||||
_MODULECONFIG._serialized_end=2258
|
||||
_MODULECONFIG_MQTTCONFIG._serialized_start=445
|
||||
_MODULECONFIG_MQTTCONFIG._serialized_end=556
|
||||
_MODULECONFIG_SERIALCONFIG._serialized_start=559
|
||||
_MODULECONFIG_SERIALCONFIG._serialized_end=1090
|
||||
_MODULECONFIG_SERIALCONFIG_SERIAL_BAUD._serialized_start=758
|
||||
_MODULECONFIG_SERIALCONFIG_SERIAL_BAUD._serialized_end=1024
|
||||
_MODULECONFIG_SERIALCONFIG_SERIAL_MODE._serialized_start=1026
|
||||
_MODULECONFIG_SERIALCONFIG_SERIAL_MODE._serialized_end=1090
|
||||
_MODULECONFIG_EXTERNALNOTIFICATIONCONFIG._serialized_start=1093
|
||||
_MODULECONFIG_EXTERNALNOTIFICATIONCONFIG._serialized_end=1232
|
||||
_MODULECONFIG_STOREFORWARDCONFIG._serialized_start=1235
|
||||
_MODULECONFIG_STOREFORWARDCONFIG._serialized_end=1367
|
||||
_MODULECONFIG_RANGETESTCONFIG._serialized_start=1369
|
||||
_MODULECONFIG_RANGETESTCONFIG._serialized_end=1433
|
||||
_MODULECONFIG_TELEMETRYCONFIG._serialized_start=1436
|
||||
_MODULECONFIG_TELEMETRYCONFIG._serialized_end=1639
|
||||
_MODULECONFIG_CANNEDMESSAGECONFIG._serialized_start=1642
|
||||
_MODULECONFIG_CANNEDMESSAGECONFIG._serialized_end=2240
|
||||
_MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR._serialized_start=2109
|
||||
_MODULECONFIG_CANNEDMESSAGECONFIG_INPUTEVENTCHAR._serialized_end=2240
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -3,27 +3,38 @@
|
||||
|
||||
import logging
|
||||
import base64
|
||||
import time
|
||||
from google.protobuf.json_format import MessageToJson
|
||||
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
|
||||
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2, localonly_pb2
|
||||
from meshtastic.util import pskToString, stripnl, Timeout, our_exit, fromPSK
|
||||
|
||||
|
||||
class Node:
|
||||
"""A model of a (local or remote) node in the mesh
|
||||
|
||||
Includes methods for radioConfig and channels
|
||||
Includes methods for localConfig, moduleConfig and channels
|
||||
"""
|
||||
|
||||
def __init__(self, iface, nodeNum, noProto=False):
|
||||
"""Constructor"""
|
||||
self.iface = iface
|
||||
self.nodeNum = nodeNum
|
||||
self.radioConfig = None
|
||||
self.localConfig = localonly_pb2.LocalConfig()
|
||||
self.moduleConfig = localonly_pb2.LocalModuleConfig()
|
||||
self.channels = None
|
||||
self._timeout = Timeout(maxSecs=300)
|
||||
self.partialChannels = None
|
||||
self.noProto = noProto
|
||||
|
||||
self.cannedPluginMessage = None
|
||||
|
||||
self.cannedPluginMessagePart1 = None
|
||||
self.cannedPluginMessagePart2 = None
|
||||
self.cannedPluginMessagePart3 = None
|
||||
self.cannedPluginMessagePart4 = None
|
||||
|
||||
self.gotResponse = None
|
||||
|
||||
def showChannels(self):
|
||||
"""Show human readable description of our channels."""
|
||||
print("Channels:")
|
||||
@@ -44,19 +55,22 @@ class Node:
|
||||
def showInfo(self):
|
||||
"""Show human readable description of our node"""
|
||||
prefs = ""
|
||||
if self.radioConfig and self.radioConfig.preferences:
|
||||
prefs = stripnl(MessageToJson(self.radioConfig.preferences))
|
||||
if self.localConfig:
|
||||
prefs = stripnl(MessageToJson(self.localConfig))
|
||||
print(f"Preferences: {prefs}\n")
|
||||
prefs = ""
|
||||
if self.moduleConfig:
|
||||
prefs = stripnl(MessageToJson(self.moduleConfig))
|
||||
print(f"Module preferences: {prefs}\n")
|
||||
self.showChannels()
|
||||
|
||||
def requestConfig(self):
|
||||
"""Send regular MeshPackets to ask for settings and channels."""
|
||||
logging.debug(f"requestConfig for nodeNum:{self.nodeNum}")
|
||||
self.radioConfig = None
|
||||
self.channels = None
|
||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||
|
||||
self._requestSettings()
|
||||
self._requestChannel(0)
|
||||
|
||||
def turnOffEncryptionOnPrimaryChannel(self):
|
||||
"""Turn off encryption on primary channel."""
|
||||
@@ -64,27 +78,111 @@ class Node:
|
||||
print("Writing modified channels to device")
|
||||
self.writeChannel(0)
|
||||
|
||||
def waitForConfig(self):
|
||||
def waitForConfig(self, attribute='channels'):
|
||||
"""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):
|
||||
"""Write the current (edited) radioConfig to the device"""
|
||||
if self.radioConfig is None:
|
||||
our_exit("Error: No RadioConfig has been read")
|
||||
"""Write the current (edited) localConfig to the device"""
|
||||
if self.localConfig is None:
|
||||
our_exit("Error: No localConfig has been read")
|
||||
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_radio.CopyFrom(self.radioConfig)
|
||||
if self.localConfig.device:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.device.CopyFrom(self.localConfig.device)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote device")
|
||||
time.sleep(0.3)
|
||||
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote config")
|
||||
if self.localConfig.position:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.position.CopyFrom(self.localConfig.position)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote position")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.power:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.power.CopyFrom(self.localConfig.power)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote power")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.wifi:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.wifi.CopyFrom(self.localConfig.wifi)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote wifi")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.display:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.display.CopyFrom(self.localConfig.display)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote display")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.localConfig.lora:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_config.lora.CopyFrom(self.localConfig.lora)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote lora")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.mqtt:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.mqtt.CopyFrom(self.moduleConfig.mqtt)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: mqtt")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.serial:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.serial.CopyFrom(self.moduleConfig.serial)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: serial")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.external_notification:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.external_notification.CopyFrom(self.moduleConfig.external_notification)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: external_notification")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.store_forward:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.store_forward.CopyFrom(self.moduleConfig.store_forward)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: store_forward")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.range_test:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.range_test.CopyFrom(self.moduleConfig.range_test)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: range_test")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.telemetry:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.telemetry.CopyFrom(self.moduleConfig.telemetry)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: telemetry")
|
||||
time.sleep(0.3)
|
||||
|
||||
if self.moduleConfig.canned_message:
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_module_config.canned_message.CopyFrom(self.moduleConfig.canned_message)
|
||||
self._sendAdmin(p)
|
||||
logging.debug("Wrote module: canned_message")
|
||||
time.sleep(0.3)
|
||||
|
||||
def writeChannel(self, channelIndex, adminIndex=0):
|
||||
"""Write the current (edited) channel to the device"""
|
||||
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.set_channel.CopyFrom(self.channels[channelIndex])
|
||||
|
||||
self._sendAdmin(p, adminIndex=adminIndex)
|
||||
logging.debug(f"Wrote channel {channelIndex}")
|
||||
|
||||
@@ -145,7 +243,7 @@ class Node:
|
||||
else:
|
||||
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"""
|
||||
logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
|
||||
nChars = 3
|
||||
@@ -174,14 +272,11 @@ class Node:
|
||||
short_name = short_name[:nChars]
|
||||
p.set_owner.short_name = short_name
|
||||
p.set_owner.is_licensed = is_licensed
|
||||
if team is not None:
|
||||
p.set_owner.team = team
|
||||
|
||||
# Note: These debug lines are used in unit tests
|
||||
logging.debug(f'p.set_owner.long_name:{p.set_owner.long_name}:')
|
||||
logging.debug(f'p.set_owner.short_name:{p.set_owner.short_name}:')
|
||||
logging.debug(f'p.set_owner.is_licensed:{p.set_owner.is_licensed}')
|
||||
logging.debug(f'p.set_owner.team:{p.set_owner.team}')
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def getURL(self, includeAll: bool = True):
|
||||
@@ -194,12 +289,12 @@ class Node:
|
||||
channelSet.settings.append(c.settings)
|
||||
some_bytes = channelSet.SerializeToString()
|
||||
s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
|
||||
return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
|
||||
return f"https://www.meshtastic.org/e/#{s}".replace("=", "")
|
||||
|
||||
def setURL(self, url):
|
||||
"""Set mesh network URL"""
|
||||
if self.radioConfig is None:
|
||||
our_exit("Warning: No RadioConfig has been read")
|
||||
if self.localConfig is None:
|
||||
our_exit("Warning: No Config has been read")
|
||||
|
||||
# URLs are of the form https://www.meshtastic.org/d/#{base64_channel_set}
|
||||
# Split on '/#' to find the base64 encoded channel settings
|
||||
@@ -232,41 +327,155 @@ class Node:
|
||||
self.writeChannel(ch.index)
|
||||
i = i + 1
|
||||
|
||||
|
||||
def onResponseRequestSettings(self, p):
|
||||
"""Handle the response packet for requesting settings _requestSettings()"""
|
||||
logging.debug(f'onResponseRequestSetting() p:{p}')
|
||||
def onResponseRequestCannedMessagePluginMessagePart1(self, p):
|
||||
"""Handle the response packet for requesting canned message plugin message part 1"""
|
||||
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart1() p:{p}')
|
||||
errorFound = False
|
||||
if 'routing' in p["decoded"]:
|
||||
if "routing" in p["decoded"]:
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
errorFound = True
|
||||
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
|
||||
if errorFound is False:
|
||||
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
|
||||
logging.debug(f'self.radioConfig:{self.radioConfig}')
|
||||
logging.debug("Received radio config, now fetching channels...")
|
||||
self._timeout.reset() # We made foreward progress
|
||||
self._requestChannel(0) # now start fetching channels
|
||||
if "decoded" in p:
|
||||
if "admin" in p["decoded"]:
|
||||
if "raw" in p["decoded"]["admin"]:
|
||||
self.cannedPluginMessagePart1 = p["decoded"]["admin"]["raw"].get_canned_message_module_part1_response
|
||||
logging.debug(f'self.cannedPluginMessagePart1:{self.cannedPluginMessagePart1}')
|
||||
self.gotResponse = True
|
||||
|
||||
def onResponseRequestCannedMessagePluginMessagePart2(self, p):
|
||||
"""Handle the response packet for requesting canned message plugin message part 2"""
|
||||
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart2() 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.cannedPluginMessagePart2 = p["decoded"]["admin"]["raw"].get_canned_message_module_part2_response
|
||||
logging.debug(f'self.cannedPluginMessagePart2:{self.cannedPluginMessagePart2}')
|
||||
self.gotResponse = True
|
||||
|
||||
def _requestSettings(self):
|
||||
"""Done with initial config messages, now send regular
|
||||
MeshPackets to ask for settings."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_radio_request = True
|
||||
def onResponseRequestCannedMessagePluginMessagePart3(self, p):
|
||||
"""Handle the response packet for requesting canned message plugin message part 3"""
|
||||
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart3() 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.cannedPluginMessagePart3 = p["decoded"]["admin"]["raw"].get_canned_message_module_part3_response
|
||||
logging.debug(f'self.cannedPluginMessagePart3:{self.cannedPluginMessagePart3}')
|
||||
self.gotResponse = True
|
||||
|
||||
# TODO: should we check that localNode has an 'admin' channel?
|
||||
# Show progress message for super slow operations
|
||||
if self != self.iface.localNode:
|
||||
print("Requesting preferences from remote node.")
|
||||
print("Be sure:")
|
||||
print(" 1. There is a SECONDARY channel named 'admin'.")
|
||||
print(" 2. The '--seturl' was used to configure.")
|
||||
print(" 3. All devices have the same modem config. (i.e., '--ch-longfast')")
|
||||
print(" 4. All devices have been rebooted after all of the above. (optional, but recommended)")
|
||||
print("Note: This could take a while (it requests remote channel configs, then writes config)")
|
||||
def onResponseRequestCannedMessagePluginMessagePart4(self, p):
|
||||
"""Handle the response packet for requesting canned message plugin message part 4"""
|
||||
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart4() 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.cannedPluginMessagePart4 = p["decoded"]["admin"]["raw"].get_canned_message_module_part4_response
|
||||
logging.debug(f'self.cannedPluginMessagePart4:{self.cannedPluginMessagePart4}')
|
||||
self.gotResponse = True
|
||||
|
||||
return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings)
|
||||
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_part1_request = True
|
||||
self.gotResponse = False
|
||||
self._sendAdmin(p1, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart1)
|
||||
while self.gotResponse is False:
|
||||
time.sleep(0.1)
|
||||
|
||||
p2 = admin_pb2.AdminMessage()
|
||||
p2.get_canned_message_module_part2_request = True
|
||||
self.gotResponse = False
|
||||
self._sendAdmin(p2, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart2)
|
||||
while self.gotResponse is False:
|
||||
time.sleep(0.1)
|
||||
|
||||
p3 = admin_pb2.AdminMessage()
|
||||
p3.get_canned_message_module_part3_request = True
|
||||
self.gotResponse = False
|
||||
self._sendAdmin(p3, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart3)
|
||||
while self.gotResponse is False:
|
||||
time.sleep(0.1)
|
||||
|
||||
p4 = admin_pb2.AdminMessage()
|
||||
p4.get_canned_message_module_part4_request = True
|
||||
self.gotResponse = False
|
||||
self._sendAdmin(p4, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart4)
|
||||
while self.gotResponse is False:
|
||||
time.sleep(0.1)
|
||||
|
||||
# TODO: This feels wrong to have a sleep here. Is there a way to ensure that
|
||||
# all requests are complete? Perhaps change to a while loop any parts are None... maybe?
|
||||
time.sleep(3)
|
||||
|
||||
logging.debug(f'self.cannedPluginMessagePart1:{self.cannedPluginMessagePart1}')
|
||||
logging.debug(f'self.cannedPluginMessagePart2:{self.cannedPluginMessagePart2}')
|
||||
logging.debug(f'self.cannedPluginMessagePart3:{self.cannedPluginMessagePart3}')
|
||||
logging.debug(f'self.cannedPluginMessagePart4:{self.cannedPluginMessagePart4}')
|
||||
|
||||
self.cannedPluginMessage = ""
|
||||
if self.cannedPluginMessagePart1:
|
||||
self.cannedPluginMessage += self.cannedPluginMessagePart1
|
||||
if self.cannedPluginMessagePart2:
|
||||
self.cannedPluginMessage += self.cannedPluginMessagePart2
|
||||
if self.cannedPluginMessagePart3:
|
||||
self.cannedPluginMessage += self.cannedPluginMessagePart3
|
||||
if self.cannedPluginMessagePart4:
|
||||
self.cannedPluginMessage += self.cannedPluginMessagePart4
|
||||
|
||||
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. Split into parts of 200 chars each."""
|
||||
|
||||
if len(message) > 800:
|
||||
our_exit("Warning: The canned message must be less than 800 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_part1 = chunk
|
||||
elif i == 1:
|
||||
p.set_canned_message_module_part2 = chunk
|
||||
elif i == 2:
|
||||
p.set_canned_message_module_part3 = chunk
|
||||
elif i == 3:
|
||||
p.set_canned_message_module_part4 = chunk
|
||||
|
||||
logging.debug(f"Setting canned message '{chunk}' part {i+1}")
|
||||
self._sendAdmin(p)
|
||||
|
||||
def exitSimulator(self):
|
||||
"""Tell a simulator node to exit (this message
|
||||
@@ -293,6 +502,14 @@ class Node:
|
||||
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def getMetadata(self, secs: int = 10):
|
||||
"""Tell the node to shutdown."""
|
||||
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 _fixupChannels(self):
|
||||
"""Fixup indexes and add disabled channels as needed"""
|
||||
|
||||
@@ -316,6 +533,15 @@ class Node:
|
||||
index += 1
|
||||
|
||||
|
||||
def onRequestGetMetadata(self, p):
|
||||
"""Handle the response packet for requesting device metadata getMetadata()"""
|
||||
logging.debug(f'onRequestGetMetadata() p:{p}')
|
||||
c = p["decoded"]["admin"]["raw"].get_device_metadata_response
|
||||
self._timeout.reset() # We made foreward progress
|
||||
logging.debug(f"Received metadata {stripnl(c)}")
|
||||
print(f"\nfirmware_version: {c.firmware_version}")
|
||||
print(f"device_state_version: {c.device_state_version}")
|
||||
|
||||
def onResponseRequestChannel(self, p):
|
||||
"""Handle the response packet for requesting a channel _requestChannel()"""
|
||||
logging.debug(f'onResponseRequestChannel() p:{p}')
|
||||
|
||||
@@ -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\x0eportnums.proto*\xee\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\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42\x44\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
_PORTNUM = DESCRIPTOR.enum_types_by_name['PortNum']
|
||||
PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM)
|
||||
@@ -26,12 +26,14 @@ POSITION_APP = 3
|
||||
NODEINFO_APP = 4
|
||||
ROUTING_APP = 5
|
||||
ADMIN_APP = 6
|
||||
TEXT_MESSAGE_COMPRESSED_APP = 7
|
||||
WAYPOINT_APP = 8
|
||||
REPLY_APP = 32
|
||||
IP_TUNNEL_APP = 33
|
||||
SERIAL_APP = 64
|
||||
STORE_FORWARD_APP = 65
|
||||
RANGE_TEST_APP = 66
|
||||
ENVIRONMENTAL_MEASUREMENT_APP = 67
|
||||
TELEMETRY_APP = 67
|
||||
ZPS_APP = 68
|
||||
PRIVATE_APP = 256
|
||||
ATAK_FORWARDER = 257
|
||||
@@ -43,5 +45,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\010PortnumsH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_PORTNUM._serialized_start=19
|
||||
_PORTNUM._serialized_end=350
|
||||
_PORTNUM._serialized_end=385
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -24,8 +24,10 @@ class SerialInterface(StreamInterface):
|
||||
"""
|
||||
self.noProto = noProto
|
||||
|
||||
if devPath is None:
|
||||
ports = meshtastic.util.findPorts()
|
||||
self.devPath = devPath
|
||||
|
||||
if self.devPath is None:
|
||||
ports = meshtastic.util.findPorts(True)
|
||||
logging.debug(f"ports:{ports}")
|
||||
if len(ports) == 0:
|
||||
meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
|
||||
@@ -34,21 +36,21 @@ class SerialInterface(StreamInterface):
|
||||
message += f" Ports detected:{ports}"
|
||||
meshtastic.util.our_exit(message)
|
||||
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
|
||||
# see https://github.com/pyserial/pyserial/issues/124
|
||||
if platform.system() != 'Windows':
|
||||
with open(devPath, encoding='utf8') as f:
|
||||
with open(self.devPath, encoding='utf8') as f:
|
||||
attrs = termios.tcgetattr(f)
|
||||
attrs[2] = attrs[2] & ~termios.HUPCL
|
||||
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
|
||||
f.close()
|
||||
time.sleep(0.1)
|
||||
|
||||
self.stream = serial.Serial(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()
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import serial
|
||||
|
||||
|
||||
from meshtastic.mesh_interface import MeshInterface
|
||||
from meshtastic.util import stripnl
|
||||
from meshtastic.util import stripnl, is_windows11
|
||||
|
||||
|
||||
START1 = 0x94
|
||||
@@ -38,6 +38,8 @@ class StreamInterface(MeshInterface):
|
||||
self._rxBuf = bytes() # empty
|
||||
self._wantExit = False
|
||||
|
||||
self.is_windows11 = is_windows11()
|
||||
|
||||
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
||||
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
|
||||
|
||||
@@ -88,8 +90,12 @@ class StreamInterface(MeshInterface):
|
||||
if self.stream: # ignore writes when stream is closed
|
||||
self.stream.write(b)
|
||||
self.stream.flush()
|
||||
# we sleep here to give the TBeam a chance to work
|
||||
time.sleep(0.1)
|
||||
# win11 might need a bit more time, too
|
||||
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):
|
||||
"""Read an array of bytes from our stream"""
|
||||
@@ -125,9 +131,9 @@ class StreamInterface(MeshInterface):
|
||||
|
||||
try:
|
||||
while not self._wantExit:
|
||||
logging.debug("reading character")
|
||||
#logging.debug("reading character")
|
||||
b = self._readBytes(1)
|
||||
logging.debug("In reader loop")
|
||||
#logging.debug("In reader loop")
|
||||
#logging.debug(f"read returned {b}")
|
||||
if len(b) > 0:
|
||||
c = b[0]
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
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
|
||||
# without installing any libraries that are not currently in the python meshtastic library
|
||||
|
||||
@@ -42,16 +38,13 @@ tbeam_M8N = SupportedDevice(name="T-Beam", version="M8N", for_firmware="tbeam",
|
||||
tbeam_M8N_SX1262 = SupportedDevice(name="T-Beam", version="M8N_SX1262", for_firmware="tbeam",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tlora_v1_1 = SupportedDevice(name="T-Lora", version="1.1", for_firmware="tlora-v1",
|
||||
tlora_v1 = SupportedDevice(name="T-Lora", version="1", for_firmware="tlora-v1",
|
||||
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial",
|
||||
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
|
||||
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_0 = SupportedDevice(name="T-Lora", version="2.0", for_firmware="tlora-v2-1",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
tlora_v2_1 = SupportedDevice(name="T-Lora", version="2.1", for_firmware="tlora-v2-1",
|
||||
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",
|
||||
@@ -66,6 +59,9 @@ heltec_v2_0 = SupportedDevice(name="Heltec", version="2.0", for_firmware="heltec
|
||||
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")
|
||||
@@ -77,116 +73,21 @@ rak4631_5005 = SupportedDevice(name="RAK 4631 5005", version="", for_firmware="r
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
|
||||
rak4631_5005_epaper = SupportedDevice(name="RAK 4631 5005 14000 epaper", version="", for_firmware="rak4631_5005_epaper",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
|
||||
# Note: The 19003 reports same product id as 5005 in boot mode
|
||||
rak4631_19003 = SupportedDevice(name="RAK 4631 19003", version="", for_firmware="rak4631_19003",
|
||||
device_class="nrf52",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="8029")
|
||||
nano_g1 = SupportedDevice(name="Nano G1", version="", for_firmware="nano-g1",
|
||||
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
|
||||
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
|
||||
|
||||
supported_devices = [tbeam_v0_7, tbeam_v1_1, tbeam_M8N, tbeam_M8N_SX1262,
|
||||
tlora_v1_1, tlora_v1_3, tlora_v2_0, tlora_v2_1, tlora_v2_1_1_6,
|
||||
tlora_v1, tlora_v1_3, tlora_v2, tlora_v2_1_1_6,
|
||||
heltec_v1, heltec_v2_0, heltec_v2_1,
|
||||
meshtastic_diy_v1, techo_1, rak4631_5005, rak4631_19003]
|
||||
|
||||
|
||||
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):
|
||||
"""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
|
||||
meshtastic_diy_v1, techo_1, rak4631_5005, rak4631_5005_epaper, rak4631_19003,
|
||||
rak11200, nano_g1]
|
||||
|
||||
67
meshtastic/telemetry_pb2.py
Normal file
67
meshtastic/telemetry_pb2.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: 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\x0ftelemetry.proto\"i\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\"\x9b\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\"\x82\x01\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12(\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x0e.DeviceMetricsH\x00\x12\x32\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x13.EnvironmentMetricsH\x00\x42\t\n\x07variant*j\n\x13TelemetrySensorType\x12\n\n\x06NotSet\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\x42K\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3')
|
||||
|
||||
_TELEMETRYSENSORTYPE = DESCRIPTOR.enum_types_by_name['TelemetrySensorType']
|
||||
TelemetrySensorType = enum_type_wrapper.EnumTypeWrapper(_TELEMETRYSENSORTYPE)
|
||||
NotSet = 0
|
||||
BME280 = 1
|
||||
BME680 = 2
|
||||
MCP9808 = 3
|
||||
INA260 = 4
|
||||
INA219 = 5
|
||||
BMP280 = 6
|
||||
|
||||
|
||||
_DEVICEMETRICS = DESCRIPTOR.message_types_by_name['DeviceMetrics']
|
||||
_ENVIRONMENTMETRICS = DESCRIPTOR.message_types_by_name['EnvironmentMetrics']
|
||||
_TELEMETRY = DESCRIPTOR.message_types_by_name['Telemetry']
|
||||
DeviceMetrics = _reflection.GeneratedProtocolMessageType('DeviceMetrics', (_message.Message,), {
|
||||
'DESCRIPTOR' : _DEVICEMETRICS,
|
||||
'__module__' : 'telemetry_pb2'
|
||||
# @@protoc_insertion_point(class_scope:DeviceMetrics)
|
||||
})
|
||||
_sym_db.RegisterMessage(DeviceMetrics)
|
||||
|
||||
EnvironmentMetrics = _reflection.GeneratedProtocolMessageType('EnvironmentMetrics', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ENVIRONMENTMETRICS,
|
||||
'__module__' : 'telemetry_pb2'
|
||||
# @@protoc_insertion_point(class_scope:EnvironmentMetrics)
|
||||
})
|
||||
_sym_db.RegisterMessage(EnvironmentMetrics)
|
||||
|
||||
Telemetry = _reflection.GeneratedProtocolMessageType('Telemetry', (_message.Message,), {
|
||||
'DESCRIPTOR' : _TELEMETRY,
|
||||
'__module__' : 'telemetry_pb2'
|
||||
# @@protoc_insertion_point(class_scope:Telemetry)
|
||||
})
|
||||
_sym_db.RegisterMessage(Telemetry)
|
||||
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosH\003Z!github.com/meshtastic/gomeshproto'
|
||||
_TELEMETRYSENSORTYPE._serialized_start=417
|
||||
_TELEMETRYSENSORTYPE._serialized_end=523
|
||||
_DEVICEMETRICS._serialized_start=19
|
||||
_DEVICEMETRICS._serialized_end=124
|
||||
_ENVIRONMENTMETRICS._serialized_start=127
|
||||
_ENVIRONMENTMETRICS._serialized_end=282
|
||||
_TELEMETRY._serialized_start=285
|
||||
_TELEMETRY._serialized_end=415
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -149,7 +149,7 @@ def testAll(numTests=5):
|
||||
This is called from the cli with the "--test" option.
|
||||
|
||||
"""
|
||||
ports = meshtastic.util.findPorts()
|
||||
ports = meshtastic.util.findPorts(True)
|
||||
if len(ports) < 2:
|
||||
meshtastic.util.our_exit("Warning: Must have at least two devices connected to USB.")
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,8 @@ from ..mesh_interface import MeshInterface
|
||||
from ..node import Node
|
||||
from .. import mesh_pb2
|
||||
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
|
||||
from ..radioconfig_pb2 import RadioConfig
|
||||
# TODO
|
||||
#from ..config import Config
|
||||
from ..util import Timeout
|
||||
|
||||
|
||||
@@ -177,32 +178,34 @@ def test_sendPosition(caplog):
|
||||
assert re.search(r'p.time:', caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_globals")
|
||||
def test_close_with_heartbeatTimer(caplog):
|
||||
"""Test close() with heartbeatTimer"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
anode = Node('foo', 'bar')
|
||||
radioConfig = RadioConfig()
|
||||
radioConfig.preferences.phone_timeout_secs = 10
|
||||
anode.radioConfig = radioConfig
|
||||
iface.localNode = anode
|
||||
assert iface.heartbeatTimer is None
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
iface._startHeartbeat()
|
||||
assert iface.heartbeatTimer is not None
|
||||
iface.close()
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#@pytest.mark.usefixtures("reset_globals")
|
||||
#def test_close_with_heartbeatTimer(caplog):
|
||||
# """Test close() with heartbeatTimer"""
|
||||
# iface = MeshInterface(noProto=True)
|
||||
# anode = Node('foo', 'bar')
|
||||
# aconfig = Config()
|
||||
# aonfig.preferences.phone_timeout_secs = 10
|
||||
# anode.config = aconfig
|
||||
# iface.localNode = anode
|
||||
# assert iface.heartbeatTimer is None
|
||||
# with caplog.at_level(logging.DEBUG):
|
||||
# iface._startHeartbeat()
|
||||
# assert iface.heartbeatTimer is not None
|
||||
# iface.close()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_globals")
|
||||
def test_handleFromRadio_empty_payload(caplog):
|
||||
"""Test _handleFromRadio"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
iface._handleFromRadio(b'')
|
||||
iface.close()
|
||||
assert re.search(r'Unexpected FromRadio payload', caplog.text, re.MULTILINE)
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#@pytest.mark.usefixtures("reset_globals")
|
||||
#def test_handleFromRadio_empty_payload(caplog):
|
||||
# """Test _handleFromRadio"""
|
||||
# iface = MeshInterface(noProto=True)
|
||||
# with caplog.at_level(logging.DEBUG):
|
||||
# iface._handleFromRadio(b'')
|
||||
# iface.close()
|
||||
# assert re.search(r'Unexpected FromRadio payload', caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -213,13 +216,13 @@ def test_handleFromRadio_with_my_info(caplog):
|
||||
# It "translates" to this:
|
||||
# my_info {
|
||||
# my_node_num: 682584012
|
||||
# num_bands: 13
|
||||
# firmware_version: "1.2.49.5354c49"
|
||||
# reboot_count: 13
|
||||
# bitrate: 17.088470458984375
|
||||
# message_timeout_msec: 300000
|
||||
# min_app_version: 20200
|
||||
# max_channels: 8
|
||||
# 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'
|
||||
iface = MeshInterface(noProto=True)
|
||||
@@ -227,7 +230,6 @@ def test_handleFromRadio_with_my_info(caplog):
|
||||
iface._handleFromRadio(from_radio_bytes)
|
||||
iface.close()
|
||||
assert re.search(r'Received myinfo', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'num_bands: 13', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'max_channels: 8', caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
@@ -605,11 +607,12 @@ def test_getOrCreateByNum(iface_with_nodes):
|
||||
assert tmp['num'] == 2475227164
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_enter():
|
||||
"""Test __enter__()"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
assert iface == iface.__enter__()
|
||||
# TODO
|
||||
#@pytest.mark.unit
|
||||
#def test_enter():
|
||||
# """Test __enter__()"""
|
||||
# iface = MeshInterface(noProto=True)
|
||||
# assert iface == iface.__enter__()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -160,7 +160,7 @@ def test_smoke1_send_hello():
|
||||
def test_smoke1_port():
|
||||
"""Test --port"""
|
||||
# first, get the ports
|
||||
ports = findPorts()
|
||||
ports = findPorts(True)
|
||||
# hopefully there is just one
|
||||
assert len(ports) == 1
|
||||
port = ports[0]
|
||||
@@ -170,20 +170,6 @@ def test_smoke1_port():
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_set_is_router_true():
|
||||
"""Test --set is_router true"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --set is_router true')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Set is_router to true', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
|
||||
assert re.search(r'^is_router: True', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_set_location_info():
|
||||
"""Test --setlat, --setlon and --setalt """
|
||||
@@ -202,20 +188,6 @@ def test_smoke1_set_location_info():
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_set_is_router_false():
|
||||
"""Test --set is_router false"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --set is_router false')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Set is_router to false', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --get is_router')
|
||||
assert re.search(r'^is_router: False', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_set_owner():
|
||||
"""Test --set-owner name"""
|
||||
@@ -243,38 +215,42 @@ def test_smoke1_set_owner():
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_set_team():
|
||||
"""Test --set-team """
|
||||
# unset the team
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --set-team CLEAR')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
|
||||
def test_smoke1_ch_set_modem_config():
|
||||
"""Test --ch-set modem_config"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config MidFast')
|
||||
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'MidFast', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_REBOOT)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --set-team CYAN')
|
||||
assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config MidFast --ch-index 0')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Set modem_config to MidFast', 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 re.search(r'MidFast', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
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
|
||||
"""
|
||||
exp = {
|
||||
'--ch-longslow': 'Bw125Cr48Sf4096',
|
||||
'--ch-longfast': 'Bw31_25Cr48Sf512',
|
||||
'--ch-mediumslow': 'Bw250Cr46Sf2048',
|
||||
'--ch-mediumfast': 'Bw250Cr47Sf1024',
|
||||
# for some reason, this value does not show any modemConfig
|
||||
'--ch-shortslow': '{ "psk',
|
||||
'--ch-shortfast': 'Bw500Cr45Sf128'
|
||||
'--ch-vlongslow': '{ "psk": "AQ==" }',
|
||||
'--ch-longslow': 'LongSlow',
|
||||
'--ch-longfast': 'LongFast',
|
||||
'--ch-midslow': 'MidSlow',
|
||||
'--ch-midfast': 'MidFast',
|
||||
'--ch-shortslow': 'ShortSlow',
|
||||
'--ch-shortfast': 'ShortFast'
|
||||
}
|
||||
|
||||
for key, val in exp.items():
|
||||
@@ -578,30 +554,6 @@ def test_smoke1_ensure_ch_del_third_of_three_channels():
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_ch_set_modem_config():
|
||||
"""Test --ch-set modem_config"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512')
|
||||
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
|
||||
assert return_value == 1
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
||||
assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --ch-set modem_config Bw31_25Cr48Sf512 --ch-index 0')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
||||
assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smoke1
|
||||
def test_smoke1_seturl_default():
|
||||
"""Test --seturl with default value"""
|
||||
@@ -650,7 +602,6 @@ def test_smoke1_configure():
|
||||
assert re.search('^Setting device position', out, re.MULTILINE)
|
||||
assert re.search('^Set region to 1', out, re.MULTILINE)
|
||||
assert re.search('^Set is_always_powered to true', out, re.MULTILINE)
|
||||
assert re.search('^Set send_owner_interval to 2', out, re.MULTILINE)
|
||||
assert re.search('^Set screen_on_secs to 31536000', out, re.MULTILINE)
|
||||
assert re.search('^Set wait_bluetooth_secs to 31536000', out, re.MULTILINE)
|
||||
assert re.search('^Writing modified preferences to device', out, re.MULTILINE)
|
||||
|
||||
@@ -21,7 +21,7 @@ from ..util import findPorts
|
||||
|
||||
# seconds to pause after running a meshtastic command
|
||||
PAUSE_AFTER_COMMAND = 0.1
|
||||
PAUSE_AFTER_REBOOT = 0.1
|
||||
PAUSE_AFTER_REBOOT = 0.2
|
||||
|
||||
|
||||
#TODO: need to fix the virtual device to have a reboot. When you issue the command
|
||||
@@ -175,20 +175,6 @@ def test_smokevirt_port():
|
||||
assert len(ports) == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_is_router_true():
|
||||
"""Test --set is_router true"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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 --host localhost --get is_router')
|
||||
assert re.search(r'^is_router: True', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_location_info():
|
||||
"""Test --setlat, --setlon and --setalt """
|
||||
@@ -207,20 +193,6 @@ def test_smokevirt_set_location_info():
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_is_router_false():
|
||||
"""Test --set is_router false"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --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 --host localhost --get is_router')
|
||||
assert re.search(r'^is_router: False', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_owner():
|
||||
"""Test --set-owner name"""
|
||||
@@ -247,38 +219,18 @@ def test_smokevirt_set_owner():
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_set_team():
|
||||
"""Test --set-team """
|
||||
# unset the team
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-team CLEAR')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_REBOOT)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-team CYAN')
|
||||
assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_REBOOT)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
|
||||
assert re.search(r'CYAN', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_values():
|
||||
"""Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
|
||||
--ch-shortslow, and --ch-shortfast arguments
|
||||
"""
|
||||
exp = {
|
||||
'--ch-longslow': 'Bw125Cr48Sf4096',
|
||||
'--ch-longfast': 'Bw31_25Cr48Sf512',
|
||||
'--ch-mediumslow': 'Bw250Cr46Sf2048',
|
||||
'--ch-mediumfast': 'Bw250Cr47Sf1024',
|
||||
'--ch-shortslow': '{ "psk',
|
||||
'--ch-shortfast': 'Bw500Cr45Sf128'
|
||||
'--ch-longslow': 'LongSlow',
|
||||
'--ch-longfast': 'LongFast',
|
||||
'--ch-midslow': 'MidSlow',
|
||||
'--ch-midfast': 'MidFast',
|
||||
'--ch-shortslow': 'ShortSlow',
|
||||
'--ch-shortfast': 'ShortFast'
|
||||
}
|
||||
|
||||
for key, val in exp.items():
|
||||
@@ -358,6 +310,11 @@ def test_smokevirt_ch_set_downlink_and_uplink():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_add_and_ch_del():
|
||||
"""Test --ch-add"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
|
||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_REBOOT)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
|
||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
@@ -375,7 +332,7 @@ def test_smokevirt_ch_add_and_ch_del():
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_REBOOT)
|
||||
# make sure the secondar channel is not there
|
||||
# make sure the secondary channel is not there
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert not re.search(r'SECONDARY', out, re.MULTILINE)
|
||||
@@ -386,6 +343,11 @@ def test_smokevirt_ch_add_and_ch_del():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_enable_and_disable():
|
||||
"""Test --ch-enable and --ch-disable"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
|
||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_REBOOT)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
|
||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
@@ -434,6 +396,11 @@ def test_smokevirt_ch_enable_and_disable():
|
||||
@pytest.mark.smokevirt
|
||||
def test_smokevirt_ch_del_a_disabled_non_primary_channel():
|
||||
"""Test --ch-del will work on a disabled non-primary channel."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
|
||||
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_REBOOT)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
|
||||
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
@@ -596,14 +563,14 @@ def test_smokevirt_ch_set_modem_config():
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set modem_config Bw31_25Cr48Sf512 --ch-index 0')
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set modem_config MidSlow --ch-index 0')
|
||||
assert re.match(r'Connected to radio', out)
|
||||
assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
|
||||
assert re.search(r'^Set modem_config to MidSlow', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
# pause for the radio
|
||||
time.sleep(PAUSE_AFTER_COMMAND)
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
|
||||
assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
|
||||
assert re.search(r'MidSlow', out, re.MULTILINE)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Meshtastic unit tests for stream_interface.py"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
#import re
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
import pytest
|
||||
@@ -35,48 +35,49 @@ def test_StreamInterface_with_noProto(caplog):
|
||||
assert data == test_data
|
||||
|
||||
|
||||
## Note: This takes a bit, so moving from unit to slow
|
||||
## Tip: If you want to see the print output, run with '-s' flag:
|
||||
## pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
|
||||
@pytest.mark.unitslow
|
||||
@pytest.mark.usefixtures("reset_globals")
|
||||
def test_sendToRadioImpl(caplog):
|
||||
"""Test _sendToRadioImpl()"""
|
||||
|
||||
# def add_header(b):
|
||||
# """Add header stuffs for radio"""
|
||||
# bufLen = len(b)
|
||||
# header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
||||
# return header + b
|
||||
|
||||
# captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
|
||||
raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
|
||||
raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
|
||||
# pylint: disable=C0301
|
||||
raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
|
||||
raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
|
||||
# pylint: disable=C0301
|
||||
raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
|
||||
# pylint: disable=C0301
|
||||
raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
|
||||
# pylint: disable=C0301
|
||||
raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
|
||||
raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
|
||||
raw_blank = b''
|
||||
|
||||
test_data = b'hello'
|
||||
stream = MagicMock()
|
||||
#stream.read.return_value = add_header(test_data)
|
||||
stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
|
||||
raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
|
||||
raw_blank, raw_blank]
|
||||
toRadio = MagicMock()
|
||||
toRadio.SerializeToString.return_value = test_data
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
iface = StreamInterface(noProto=True, connectNow=False)
|
||||
iface.stream = stream
|
||||
iface.connect()
|
||||
iface._sendToRadioImpl(toRadio)
|
||||
assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
||||
assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
||||
# TODO
|
||||
### Note: This takes a bit, so moving from unit to slow
|
||||
### Tip: If you want to see the print output, run with '-s' flag:
|
||||
### pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
|
||||
#@pytest.mark.unitslow
|
||||
#@pytest.mark.usefixtures("reset_globals")
|
||||
#def test_sendToRadioImpl(caplog):
|
||||
# """Test _sendToRadioImpl()"""
|
||||
#
|
||||
## def add_header(b):
|
||||
## """Add header stuffs for radio"""
|
||||
## bufLen = len(b)
|
||||
## header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
||||
## return header + b
|
||||
#
|
||||
# # captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
|
||||
# raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
|
||||
# raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
|
||||
# # pylint: disable=C0301
|
||||
# raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
|
||||
# raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
|
||||
# # pylint: disable=C0301
|
||||
# raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
|
||||
# # pylint: disable=C0301
|
||||
# raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
|
||||
# # pylint: disable=C0301
|
||||
# raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
|
||||
# raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
|
||||
# raw_blank = b''
|
||||
#
|
||||
# test_data = b'hello'
|
||||
# stream = MagicMock()
|
||||
# #stream.read.return_value = add_header(test_data)
|
||||
# stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
|
||||
# raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
|
||||
# raw_blank, raw_blank]
|
||||
# toRadio = MagicMock()
|
||||
# toRadio.SerializeToString.return_value = test_data
|
||||
# with caplog.at_level(logging.DEBUG):
|
||||
# iface = StreamInterface(noProto=True, connectNow=False)
|
||||
# iface.stream = stream
|
||||
# iface.connect()
|
||||
# iface._sendToRadioImpl(toRadio)
|
||||
# assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
||||
# assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
||||
# assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
||||
|
||||
@@ -11,7 +11,10 @@ from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
|
||||
quoteBooleans, catchAndIgnore,
|
||||
remove_keys_from_dict, Timeout, hexstr,
|
||||
ipstr, readnet_u16, findPorts, convert_mac_addr,
|
||||
snake_to_camel, camel_to_snake)
|
||||
snake_to_camel, camel_to_snake, eliminate_duplicate_port,
|
||||
is_windows11, active_ports_on_supported_devices)
|
||||
|
||||
from meshtastic.supported_device import SupportedDevice
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -247,6 +250,54 @@ def test_findPorts_when_none_found(patch_comports):
|
||||
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
|
||||
def test_convert_mac_addr():
|
||||
"""Test convert_mac_addr()"""
|
||||
@@ -272,3 +323,136 @@ def test_camel_to_snake():
|
||||
assert camel_to_snake('Foo') == 'foo'
|
||||
assert camel_to_snake('fooBar') == 'foo_bar'
|
||||
assert camel_to_snake('fooBarBaz') == 'foo_bar_baz'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_eliminate_duplicate_port():
|
||||
"""Test eliminate_duplicate_port()"""
|
||||
assert not eliminate_duplicate_port([])
|
||||
assert eliminate_duplicate_port(['/dev/fake']) == ['/dev/fake']
|
||||
assert eliminate_duplicate_port(['/dev/fake', '/dev/fake1']) == ['/dev/fake', '/dev/fake1']
|
||||
assert eliminate_duplicate_port(['/dev/fake', '/dev/fake1', '/dev/fake2']) == ['/dev/fake', '/dev/fake1', '/dev/fake2']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430']) == ['/dev/cu.wchusbserial1430']
|
||||
assert eliminate_duplicate_port(['/dev/cu.wchusbserial1430', '/dev/cu.usbserial-1430']) == ['/dev/cu.wchusbserial1430']
|
||||
assert eliminate_duplicate_port(['/dev/cu.SLAB_USBtoUART', '/dev/cu.usbserial-0001']) == ['/dev/cu.usbserial-0001']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbserial-0001', '/dev/cu.SLAB_USBtoUART']) == ['/dev/cu.usbserial-0001']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbmodem11301', '/dev/cu.wchusbserial11301']) == ['/dev/cu.wchusbserial11301']
|
||||
assert eliminate_duplicate_port(['/dev/cu.wchusbserial11301', '/dev/cu.usbmodem11301']) == ['/dev/cu.wchusbserial11301']
|
||||
assert eliminate_duplicate_port(['/dev/cu.usbmodem53230051441', '/dev/cu.wchusbserial53230051441']) == ['/dev/cu.wchusbserial53230051441']
|
||||
assert eliminate_duplicate_port(['/dev/cu.wchusbserial53230051441', '/dev/cu.usbmodem53230051441']) == ['/dev/cu.wchusbserial53230051441']
|
||||
|
||||
@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()
|
||||
|
||||
@@ -14,7 +14,8 @@ import subprocess
|
||||
import serial
|
||||
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"""
|
||||
blacklistVids = dict.fromkeys([0x1366])
|
||||
@@ -63,6 +64,8 @@ def fromStr(valstr):
|
||||
elif valstr.startswith('0x'):
|
||||
# if needed convert to string with asBytes.decode('utf-8')
|
||||
val = bytes.fromhex(valstr[2:])
|
||||
elif valstr.startswith('base64:'):
|
||||
val = base64.b64decode(valstr[7:])
|
||||
elif valstr.lower() in {"t", "true", "yes"}:
|
||||
val = True
|
||||
elif valstr.lower() in {"f", "false", "no"}:
|
||||
@@ -113,8 +116,9 @@ def catchAndIgnore(reason, closure):
|
||||
logging.error(f"Exception thrown in {reason}: {ex}")
|
||||
|
||||
|
||||
def findPorts():
|
||||
def findPorts(eliminate_duplicates=False):
|
||||
"""Find all ports that might have meshtastic devices
|
||||
eliminate_duplicates will run the eliminate_duplicate_port() on the collection
|
||||
|
||||
Returns:
|
||||
list -- a list of device paths
|
||||
@@ -123,6 +127,8 @@ def findPorts():
|
||||
filter(lambda port: port.vid is not None and port.vid not in blacklistVids,
|
||||
serial.tools.list_ports.comports())))
|
||||
l.sort()
|
||||
if eliminate_duplicates:
|
||||
l = eliminate_duplicate_port(l)
|
||||
return l
|
||||
|
||||
|
||||
@@ -262,7 +268,7 @@ def camel_to_snake(a_string):
|
||||
|
||||
|
||||
def detect_supported_devices():
|
||||
"""detect supported devices"""
|
||||
"""detect supported devices based on vendor id"""
|
||||
system = platform.system()
|
||||
#print(f'system:{system}')
|
||||
|
||||
@@ -281,19 +287,8 @@ def detect_supported_devices():
|
||||
if re.search(search, lsusb_output, re.MULTILINE):
|
||||
#print(f'Found vendor id that matches')
|
||||
devices = get_devices_with_vendor_id(vid)
|
||||
# check device id
|
||||
for device in devices:
|
||||
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
|
||||
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)
|
||||
possible_devices.add(device)
|
||||
|
||||
elif system == "Windows":
|
||||
# if windows, run Get-PnpDevice
|
||||
@@ -309,22 +304,8 @@ def detect_supported_devices():
|
||||
if re.search(search, sp_output, re.MULTILINE):
|
||||
#print(f'Found vendor id that matches')
|
||||
devices = get_devices_with_vendor_id(vid)
|
||||
# check device id
|
||||
for device in devices:
|
||||
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
|
||||
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)
|
||||
possible_devices.add(device)
|
||||
|
||||
elif system == "Darwin":
|
||||
# run: system_profiler SPUSBDataType
|
||||
@@ -339,19 +320,8 @@ def detect_supported_devices():
|
||||
if re.search(search, sp_output, re.MULTILINE):
|
||||
#print(f'Found vendor id that matches')
|
||||
devices = get_devices_with_vendor_id(vid)
|
||||
# check device id
|
||||
for device in devices:
|
||||
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
|
||||
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)
|
||||
possible_devices.add(device)
|
||||
return possible_devices
|
||||
|
||||
|
||||
@@ -381,3 +351,161 @@ def detect_windows_needs_driver(sd, print_reason=False):
|
||||
if print_reason:
|
||||
print(sp_output)
|
||||
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
|
||||
|
||||
2
proto
2
proto
Submodule proto updated: 792027be93...c473ed7a6a
7
setup.py
7
setup.py
@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
|
||||
# This call to setup() does all the work
|
||||
setup(
|
||||
name="meshtastic",
|
||||
version="1.2.80",
|
||||
version="1.3alpha.21",
|
||||
description="Python API & client shell for talking to Meshtastic devices",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
@@ -22,8 +22,7 @@ setup(
|
||||
license="MIT",
|
||||
classifiers=[
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Development Status :: 4 - Beta",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
@@ -38,7 +37,7 @@ setup(
|
||||
extras_require={
|
||||
'tunnel': ["pytap2>=2.0.0"]
|
||||
},
|
||||
python_requires='>=3.6',
|
||||
python_requires='>=3.7',
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"meshtastic=meshtastic.__main__:main",
|
||||
|
||||
@@ -2,6 +2,6 @@ readme.txt for single standalone executable zip files that can be
|
||||
downloaded from https://github.com/meshtastic/Meshtastic-python/releases
|
||||
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user