mirror of
https://github.com/meshtastic/python.git
synced 2026-01-17 04:08:13 -05:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
069056edad | ||
|
|
19bd510975 | ||
|
|
7979efc0a1 | ||
|
|
66866a4c65 | ||
|
|
e6fb066fe5 | ||
|
|
5841979566 | ||
|
|
529f50edc6 | ||
|
|
5895e8fb4d | ||
|
|
49dcf71116 | ||
|
|
5778552380 | ||
|
|
592ecc9997 | ||
|
|
1aaa205cc9 | ||
|
|
ff5a0927fa | ||
|
|
607127d46e | ||
|
|
cf61a5d39d | ||
|
|
69b2599abb | ||
|
|
533e50de9d | ||
|
|
12b99f80dc | ||
|
|
0193ca052f | ||
|
|
e0af620cbe | ||
|
|
80367232ae | ||
|
|
1c480bd74f | ||
|
|
ba05de263c | ||
|
|
f045c3bc2b | ||
|
|
9ec5f33a85 | ||
|
|
e1cc657059 | ||
|
|
9c3dce4bbb | ||
|
|
5418539275 | ||
|
|
3103640688 | ||
|
|
86beb2f212 | ||
|
|
dd1c96fcba | ||
|
|
b282baadb7 | ||
|
|
fccb38fced | ||
|
|
700a7e27d3 | ||
|
|
bb1c40c7f8 | ||
|
|
407490c654 | ||
|
|
dfd61af346 | ||
|
|
a85b4ad985 | ||
|
|
eacffa79fa | ||
|
|
ca5449f1b8 | ||
|
|
6751f5bd53 | ||
|
|
c5c70fe83e | ||
|
|
0c0cd81253 | ||
|
|
553a03e40f | ||
|
|
b50190aeb0 | ||
|
|
bc231a8a9d | ||
|
|
c1c9cb8139 | ||
|
|
0a915e305d | ||
|
|
308eb1f321 | ||
|
|
d576c0e30b | ||
|
|
e63d4c3a30 | ||
|
|
2041e81101 | ||
|
|
f396374fa8 | ||
|
|
1a0db63f32 | ||
|
|
0d32a83e66 | ||
|
|
7ac3328db8 | ||
|
|
4f72987a29 | ||
|
|
aa5af53348 | ||
|
|
030ee8554c |
90
.github/workflows/build_executables.yml
vendored
90
.github/workflows/build_executables.yml
vendored
@@ -1,90 +0,0 @@
|
|||||||
name: Build and publish standalone executables
|
|
||||||
|
|
||||||
on: workflow_dispatch
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build-and-publish-mac:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.9
|
|
||||||
|
|
||||||
- name: Setup code signing
|
|
||||||
env:
|
|
||||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
|
||||||
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
|
|
||||||
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
|
|
||||||
security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
|
|
||||||
security default-keychain -s meshtastic.keychain
|
|
||||||
security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
|
|
||||||
security import certificate.p12 -k meshtastic.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
|
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
env:
|
|
||||||
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
|
|
||||||
run: |
|
|
||||||
pip install pyinstaller
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install .
|
|
||||||
pyinstaller -F -n meshtastic --collect-all meshtastic --codesign-identity "$MACOS_SIGNING_IDENTITY" meshtastic/__main__.py
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: meshtastic_mac
|
|
||||||
path: dist
|
|
||||||
|
|
||||||
build-and-publish-ubuntu:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.9
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
pip install pyinstaller
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install .
|
|
||||||
pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: meshtastic_ubuntu
|
|
||||||
path: dist
|
|
||||||
|
|
||||||
|
|
||||||
build-and-publish-windows:
|
|
||||||
runs-on: windows-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.9
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
pip install pyinstaller
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install .
|
|
||||||
pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: meshtastic_windows
|
|
||||||
path: dist
|
|
||||||
35
.github/workflows/publish_to_pypi.yml
vendored
35
.github/workflows/publish_to_pypi.yml
vendored
@@ -1,35 +0,0 @@
|
|||||||
name: Publish PyPI
|
|
||||||
|
|
||||||
on: workflow_dispatch
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.9
|
|
||||||
- name: Install pypa/build
|
|
||||||
run: >-
|
|
||||||
python -m
|
|
||||||
pip install
|
|
||||||
build
|
|
||||||
--user
|
|
||||||
- name: Build a binary wheel and a source tarball
|
|
||||||
run: >-
|
|
||||||
python -m
|
|
||||||
build
|
|
||||||
--sdist
|
|
||||||
--wheel
|
|
||||||
--outdir dist/
|
|
||||||
.
|
|
||||||
- name: Publish to PyPI
|
|
||||||
uses: pypa/gh-action-pypi-publish@master
|
|
||||||
with:
|
|
||||||
username: __token__
|
|
||||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
||||||
179
.github/workflows/release.yml
vendored
Normal file
179
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
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'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release_create:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Create GitHub release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
id: create_release
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
prerelease: true
|
||||||
|
release_name: ${{ github.event.inputs.version}}
|
||||||
|
tag_name: ${{ github.event.inputs.version}}
|
||||||
|
body: |
|
||||||
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
|
||||||
|
publish_to_pypi:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
|
||||||
|
- name: Install pypa/build
|
||||||
|
run: >-
|
||||||
|
python -m
|
||||||
|
pip install
|
||||||
|
build
|
||||||
|
--user
|
||||||
|
|
||||||
|
- name: Build a binary wheel and a source tarball
|
||||||
|
run: >-
|
||||||
|
python -m
|
||||||
|
build
|
||||||
|
--sdist
|
||||||
|
--wheel
|
||||||
|
--outdir dist/
|
||||||
|
.
|
||||||
|
|
||||||
|
- name: Publish to PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
|
with:
|
||||||
|
user: __token__
|
||||||
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
build-and-publish-mac:
|
||||||
|
runs-on: macos-latest
|
||||||
|
needs: release_create
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
|
||||||
|
- name: Setup code signing
|
||||||
|
env:
|
||||||
|
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
||||||
|
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
|
||||||
|
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
|
||||||
|
security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
|
||||||
|
security default-keychain -s meshtastic.keychain
|
||||||
|
security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
|
||||||
|
security import certificate.p12 -k meshtastic.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
|
||||||
|
run: |
|
||||||
|
pip install pyinstaller
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install .
|
||||||
|
pyinstaller -F -n meshtastic --collect-all meshtastic --codesign-identity "$MACOS_SIGNING_IDENTITY" meshtastic/__main__.py
|
||||||
|
|
||||||
|
- name: Add mac to release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.release_create.outputs.upload_url }}
|
||||||
|
asset_path: dist/meshtastic
|
||||||
|
asset_name: meshtastic_mac
|
||||||
|
asset_content_type: application/zip
|
||||||
|
|
||||||
|
build-and-publish-ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: release_create
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
pip install pyinstaller
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install .
|
||||||
|
pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py
|
||||||
|
|
||||||
|
- name: Add ubuntu to release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.release_create.outputs.upload_url }}
|
||||||
|
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:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.release_create.outputs.upload_url }}
|
||||||
|
asset_path: standalone_readme.txt
|
||||||
|
asset_name: readme.txt
|
||||||
|
asset_content_type: text/plain
|
||||||
|
|
||||||
|
build-and-publish-windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
needs: release_create
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
pip install pyinstaller
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install .
|
||||||
|
pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py
|
||||||
|
|
||||||
|
- name: Add windows to release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.release_create.outputs.upload_url }}
|
||||||
|
asset_path: dist/meshtastic.exe
|
||||||
|
asset_name: meshtastic_windows
|
||||||
|
asset_content_type: application/zip
|
||||||
11
.github/workflows/update_protobufs.yml
vendored
11
.github/workflows/update_protobufs.yml
vendored
@@ -15,6 +15,17 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
git pull --recurse-submodules
|
git pull --recurse-submodules
|
||||||
git submodule update --remote --recursive
|
git submodule update --remote --recursive
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- name: Re-generate protocol buffers
|
||||||
|
run: |
|
||||||
|
./bin/regen-protos.sh
|
||||||
|
|
||||||
- name: Commit update
|
- name: Commit update
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name 'github-actions'
|
git config --global user.name 'github-actions'
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
# workaround for import bug in protoc https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-690618628
|
# workaround for import bug in protoc https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-690618628
|
||||||
|
|
||||||
if [[ $OSTYPE == 'darwin'* ]]; then
|
if [[ $OSTYPE == 'darwin'* ]]; then
|
||||||
sed -i -E 's/^\(import.*_pb2\)/from . \1/' meshtastic/*.py
|
sed -i '' -E 's/^\(import.*_pb2\)/from . \1/' meshtastic/*.py
|
||||||
|
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
|
||||||
|
sed -i '' -E "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
|
||||||
else
|
else
|
||||||
sed -i -E 's/^import.*_pb2/from . \0/' meshtastic/*.py
|
sed -i -e 's/^import.*_pb2/from . \0/' meshtastic/*.py
|
||||||
|
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
|
||||||
|
sed -i -e "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
|
|
||||||
sed -i '' -e "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# example config using camelCase keys
|
# example config using camelCase keys
|
||||||
owner: Bob TBeam
|
owner: Bob TBeam
|
||||||
|
ownerShort: BOB
|
||||||
|
|
||||||
channelUrl: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
channelUrl: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# example configuration file with snake_case keys
|
# example configuration file with snake_case keys
|
||||||
owner: Bob TBeam
|
owner: Bob TBeam
|
||||||
|
owner_short: BOB
|
||||||
|
|
||||||
channel_url: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
channel_url: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,11 @@ def onConnected(interface):
|
|||||||
print(f"Setting device owner to {args.set_owner}")
|
print(f"Setting device owner to {args.set_owner}")
|
||||||
interface.getNode(args.dest).setOwner(args.set_owner)
|
interface.getNode(args.dest).setOwner(args.set_owner)
|
||||||
|
|
||||||
|
if args.set_owner_short:
|
||||||
|
closeNow = True
|
||||||
|
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)
|
||||||
|
|
||||||
if args.pos_fields:
|
if args.pos_fields:
|
||||||
# If --pos-fields invoked with args, set position fields
|
# If --pos-fields invoked with args, set position fields
|
||||||
closeNow = True
|
closeNow = True
|
||||||
@@ -296,12 +301,11 @@ def onConnected(interface):
|
|||||||
print(f"Reading GPIO mask 0x{bitmask:x} from {args.dest}")
|
print(f"Reading GPIO mask 0x{bitmask:x} from {args.dest}")
|
||||||
interface.mask = bitmask
|
interface.mask = bitmask
|
||||||
rhc.readGPIOs(args.dest, bitmask, None)
|
rhc.readGPIOs(args.dest, bitmask, None)
|
||||||
if not interface.noProto:
|
# wait up to X seconds for a response
|
||||||
# wait up to X seconds for a response
|
for _ in range(10):
|
||||||
for _ in range(10):
|
time.sleep(1)
|
||||||
time.sleep(1)
|
if interface.gotResponse:
|
||||||
if interface.gotResponse:
|
break
|
||||||
break
|
|
||||||
logging.debug(f'end of gpio_rd')
|
logging.debug(f'end of gpio_rd')
|
||||||
|
|
||||||
if args.gpio_watch:
|
if args.gpio_watch:
|
||||||
@@ -332,6 +336,14 @@ def onConnected(interface):
|
|||||||
print(f"Setting device owner to {configuration['owner']}")
|
print(f"Setting device owner to {configuration['owner']}")
|
||||||
interface.getNode(args.dest).setOwner(configuration['owner'])
|
interface.getNode(args.dest).setOwner(configuration['owner'])
|
||||||
|
|
||||||
|
if 'owner_short' in configuration:
|
||||||
|
print(f"Setting device owner short to {configuration['owner_short']}")
|
||||||
|
interface.getNode(args.dest).setOwner(long_name=None, short_owner=configuration['owner_short'])
|
||||||
|
|
||||||
|
if 'ownerShort' in configuration:
|
||||||
|
print(f"Setting device owner short to {configuration['ownerShort']}")
|
||||||
|
interface.getNode(args.dest).setOwner(long_name=None, short_owner=configuration['ownerShort'])
|
||||||
|
|
||||||
if 'channel_url' in configuration:
|
if 'channel_url' in configuration:
|
||||||
print("Setting channel url to", configuration['channel_url'])
|
print("Setting channel url to", configuration['channel_url'])
|
||||||
interface.getNode(args.dest).setURL(configuration['channel_url'])
|
interface.getNode(args.dest).setURL(configuration['channel_url'])
|
||||||
@@ -569,6 +581,7 @@ def subscribe():
|
|||||||
def export_config(interface):
|
def export_config(interface):
|
||||||
"""used in--export-config"""
|
"""used in--export-config"""
|
||||||
owner = interface.getLongName()
|
owner = interface.getLongName()
|
||||||
|
owner_short = interface.getShortName()
|
||||||
channel_url = interface.localNode.getURL()
|
channel_url = interface.localNode.getURL()
|
||||||
myinfo = interface.getMyNodeInfo()
|
myinfo = interface.getMyNodeInfo()
|
||||||
pos = myinfo.get('position')
|
pos = myinfo.get('position')
|
||||||
@@ -583,6 +596,8 @@ def export_config(interface):
|
|||||||
config = "# start of Meshtastic configure yaml\n"
|
config = "# start of Meshtastic configure yaml\n"
|
||||||
if owner:
|
if owner:
|
||||||
config += f"owner: {owner}\n\n"
|
config += f"owner: {owner}\n\n"
|
||||||
|
if owner_short:
|
||||||
|
config += f"owner_short: {owner_short}\n\n"
|
||||||
if channel_url:
|
if channel_url:
|
||||||
if Globals.getInstance().get_camel_case():
|
if Globals.getInstance().get_camel_case():
|
||||||
config += f"channelUrl: {channel_url}\n\n"
|
config += f"channelUrl: {channel_url}\n\n"
|
||||||
@@ -765,7 +780,12 @@ def initParser():
|
|||||||
"--ch-disable", help="Disable the specified channel", action="store_true", dest="ch_disable", default=False)
|
"--ch-disable", help="Disable the specified channel", action="store_true", dest="ch_disable", default=False)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--ch-set", help="Set a channel parameter", nargs=2, action='append')
|
"--ch-set", help=("Set a channel parameter. To see channel settings available:'--ch-set all all --ch-index 0'. "
|
||||||
|
"Can set the 'psk' using this command. To disable encryption on primary channel:'--ch-set psk none --ch-index 0'. "
|
||||||
|
"To set encryption with a new random key on second channel:'--ch-set psk random --ch-index 1'. "
|
||||||
|
"To set encryption back to the default:'--ch-set default --ch-index 0'. To set encryption with your "
|
||||||
|
"own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."),
|
||||||
|
nargs=2, action='append')
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--ch-longslow", help="Change to the long-range and slow channel", action='store_true')
|
"--ch-longslow", help="Change to the long-range and slow channel", action='store_true')
|
||||||
@@ -785,10 +805,12 @@ def initParser():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--ch-shortfast", help="Change to the short-range and fast channel", action='store_true')
|
"--ch-shortfast", help="Change to the short-range and fast channel", action='store_true')
|
||||||
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--set-owner", help="Set device owner name", action="store")
|
"--set-owner", help="Set device owner name", action="store")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--set-owner-short", help="Set device owner short name", action="store")
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--set-team", help="Set team affiliation (an invalid team will list valid values)", action="store")
|
"--set-team", help="Set team affiliation (an invalid team will list valid values)", action="store")
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,17 @@ def onGPIOreceive(packet, interface):
|
|||||||
"""Callback for received GPIO responses
|
"""Callback for received GPIO responses
|
||||||
"""
|
"""
|
||||||
logging.debug(f"packet:{packet} interface:{interface}")
|
logging.debug(f"packet:{packet} interface:{interface}")
|
||||||
|
gpioValue = 0
|
||||||
hw = packet["decoded"]["remotehw"]
|
hw = packet["decoded"]["remotehw"]
|
||||||
gpioValue = hw["gpioValue"]
|
if "gpioValue" in hw:
|
||||||
|
gpioValue = hw["gpioValue"]
|
||||||
|
else:
|
||||||
|
if not "gpioMask" in hw:
|
||||||
|
# we did get a reply, but due to protobufs, 0 for numeric value is not sent
|
||||||
|
# see https://developers.google.com/protocol-buffers/docs/proto3#default
|
||||||
|
# so, we set it here
|
||||||
|
gpioValue = 0
|
||||||
|
|
||||||
#print(f'mask:{interface.mask}')
|
#print(f'mask:{interface.mask}')
|
||||||
value = int(gpioValue) & int(interface.mask)
|
value = int(gpioValue) & int(interface.mask)
|
||||||
print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')
|
print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')
|
||||||
|
|||||||
@@ -425,6 +425,23 @@ def test_main_set_owner_to_bob(capsys):
|
|||||||
mo.assert_called()
|
mo.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
|
def test_main_set_owner_short_to_bob(capsys):
|
||||||
|
"""Test --set-owner-short bob"""
|
||||||
|
sys.argv = ['', '--set-owner-short', 'bob']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
|
main()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Setting device owner short to bob', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
mo.assert_called()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_globals")
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
def test_main_set_ham_to_KI123(capsys):
|
def test_main_set_ham_to_KI123(capsys):
|
||||||
@@ -913,6 +930,7 @@ def test_main_configure_with_snake_case(capsys):
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
assert re.search(r'Setting device owner', out, re.MULTILINE)
|
assert re.search(r'Setting device owner', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Setting device owner short', out, re.MULTILINE)
|
||||||
assert re.search(r'Setting channel url', out, re.MULTILINE)
|
assert re.search(r'Setting channel url', out, re.MULTILINE)
|
||||||
assert re.search(r'Fixing altitude', out, re.MULTILINE)
|
assert re.search(r'Fixing altitude', out, re.MULTILINE)
|
||||||
assert re.search(r'Fixing latitude', out, re.MULTILINE)
|
assert re.search(r'Fixing latitude', out, re.MULTILINE)
|
||||||
@@ -940,6 +958,7 @@ def test_main_configure_with_camel_case_keys(capsys):
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
assert re.search(r'Setting device owner', out, re.MULTILINE)
|
assert re.search(r'Setting device owner', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Setting device owner short', out, re.MULTILINE)
|
||||||
assert re.search(r'Setting channel url', out, re.MULTILINE)
|
assert re.search(r'Setting channel url', out, re.MULTILINE)
|
||||||
assert re.search(r'Fixing altitude', out, re.MULTILINE)
|
assert re.search(r'Fixing altitude', out, re.MULTILINE)
|
||||||
assert re.search(r'Fixing latitude', out, re.MULTILINE)
|
assert re.search(r'Fixing latitude', out, re.MULTILINE)
|
||||||
@@ -1604,6 +1623,7 @@ def test_main_export_config(capsys):
|
|||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
mo.getLongName.return_value = 'foo'
|
mo.getLongName.return_value = 'foo'
|
||||||
|
mo.getShortName.return_value = 'oof'
|
||||||
mo.localNode.getURL.return_value = 'bar'
|
mo.localNode.getURL.return_value = 'bar'
|
||||||
mo.getMyNodeInfo().get.return_value = { 'latitudeI': 1100000000, 'longitudeI': 1200000000,
|
mo.getMyNodeInfo().get.return_value = { 'latitudeI': 1100000000, 'longitudeI': 1200000000,
|
||||||
'altitude': 100, 'batteryLevel': 34, 'latitude': 110.0,
|
'altitude': 100, 'batteryLevel': 34, 'latitude': 110.0,
|
||||||
@@ -1620,6 +1640,7 @@ position_flags: 35"""
|
|||||||
assert not re.search(r'Connected to radio', out, re.MULTILINE)
|
assert not re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
|
||||||
assert re.search(r'owner: foo', out, re.MULTILINE)
|
assert re.search(r'owner: foo', out, re.MULTILINE)
|
||||||
|
assert re.search(r'owner_short: oof', out, re.MULTILINE)
|
||||||
assert re.search(r'channel_url: bar', out, re.MULTILINE)
|
assert re.search(r'channel_url: bar', out, re.MULTILINE)
|
||||||
assert re.search(r'location:', out, re.MULTILINE)
|
assert re.search(r'location:', out, re.MULTILINE)
|
||||||
assert re.search(r'lat: 110.0', out, re.MULTILINE)
|
assert re.search(r'lat: 110.0', out, re.MULTILINE)
|
||||||
@@ -1734,18 +1755,18 @@ def test_main_gpio_rd_no_dest(capsys):
|
|||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_globals")
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
|
@patch('time.sleep')
|
||||||
def test_main_gpio_rd(caplog, capsys):
|
def test_main_gpio_rd(caplog, capsys):
|
||||||
"""Test --gpio_rd with a named gpio channel"""
|
"""Test --gpio_rd with a named gpio channel"""
|
||||||
# Note: On the Heltec v2.1, there is a GPIO pin GPIO 13 that does not have a
|
# Note: On the Heltec v2.1, there is a GPIO pin GPIO 13 that does not have a
|
||||||
# red arrow (meaning ok to use for our purposes)
|
# red arrow (meaning ok to use for our purposes)
|
||||||
# See https://resource.heltec.cn/download/WiFi_LoRa_32/WIFI_LoRa_32_V2.pdf
|
# See https://resource.heltec.cn/download/WiFi_LoRa_32/WIFI_LoRa_32_V2.pdf
|
||||||
# To find out the mask for GPIO 13, let us assign n as 13.
|
# To find out the mask for GPIO 13, let us assign n as 13.
|
||||||
# 1. Subtract 1 from n (n is now 12)
|
# 1. Find the 2^n or 2^13 (8192)
|
||||||
# 2. Find the 2^n or 2^12 (4096)
|
# 2. Convert 8192 decimal to hex (0x2000)
|
||||||
# 3. Convert 4096 decimal to hex (0x1000)
|
|
||||||
# You can use python:
|
# You can use python:
|
||||||
# >>> print(hex(2**12))
|
# >>> print(hex(2**13))
|
||||||
# 0x1000
|
# 0x2000
|
||||||
sys.argv = ['', '--gpio-rd', '0x1000', '--dest', '!1234']
|
sys.argv = ['', '--gpio-rd', '0x1000', '--dest', '!1234']
|
||||||
Globals.getInstance().set_args(sys.argv)
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
@@ -1775,6 +1796,52 @@ def test_main_gpio_rd(caplog, capsys):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
|
iface.localNode.getChannelByName.return_value = channel
|
||||||
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
main()
|
||||||
|
onGPIOreceive(packet, mo)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Reading GPIO mask 0x1000 ', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Received RemoteHardware typ=READ_GPIOS_REPLY, gpio_value=4096', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
|
@patch('time.sleep')
|
||||||
|
def test_main_gpio_rd_with_no_gpioMask(caplog, capsys):
|
||||||
|
"""Test --gpio_rd with a named gpio channel"""
|
||||||
|
sys.argv = ['', '--gpio-rd', '0x1000', '--dest', '!1234']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
channel = Channel(index=1, role=1)
|
||||||
|
channel.settings.modem_config = 3
|
||||||
|
channel.settings.psk = b'\x01'
|
||||||
|
|
||||||
|
# Note: Intentionally do not have gpioValue in response as that is the
|
||||||
|
# default value
|
||||||
|
packet = {
|
||||||
|
'from': 682968668,
|
||||||
|
'to': 682968612,
|
||||||
|
'channel': 1,
|
||||||
|
'decoded': {
|
||||||
|
'portnum': 'REMOTE_HARDWARE_APP',
|
||||||
|
'payload': b'\x08\x05\x18\x80 ',
|
||||||
|
'requestId': 1629980484,
|
||||||
|
'remotehw': {
|
||||||
|
'typ': 'READ_GPIOS_REPLY',
|
||||||
|
'raw': 'faked',
|
||||||
|
'id': 1693085229,
|
||||||
|
'rxTime': 1640294262,
|
||||||
|
'rxSnr': 4.75,
|
||||||
|
'hopLimit': 3,
|
||||||
|
'wantAck': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
iface.localNode.getChannelByName.return_value = channel
|
iface.localNode.getChannelByName.return_value = channel
|
||||||
@@ -1782,14 +1849,112 @@ def test_main_gpio_rd(caplog, capsys):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
main()
|
main()
|
||||||
onGPIOreceive(packet, mo)
|
onGPIOreceive(packet, mo)
|
||||||
assert re.search(r'readGPIOs nodeid:!1234 mask:4096', caplog.text, re.MULTILINE)
|
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
assert re.search(r'Reading GPIO mask 0x1000 ', out, re.MULTILINE)
|
assert re.search(r'Reading GPIO mask 0x1000 ', out, re.MULTILINE)
|
||||||
assert re.search(r'Received RemoteHardware typ=READ_GPIOS_REPLY, gpio_value=4096', out, re.MULTILINE)
|
assert re.search(r'Received RemoteHardware typ=READ_GPIOS_REPLY, gpio_value=0', out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
|
def test_main_gpio_watch(caplog, capsys):
|
||||||
|
"""Test --gpio_watch with a named gpio channel"""
|
||||||
|
sys.argv = ['', '--gpio-watch', '0x1000', '--dest', '!1234']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
def my_sleep(amount):
|
||||||
|
print(f'{amount}')
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
channel = Channel(index=1, role=1)
|
||||||
|
channel.settings.modem_config = 3
|
||||||
|
channel.settings.psk = b'\x01'
|
||||||
|
|
||||||
|
packet = {
|
||||||
|
|
||||||
|
'from': 682968668,
|
||||||
|
'to': 682968612,
|
||||||
|
'channel': 1,
|
||||||
|
'decoded': {
|
||||||
|
'portnum': 'REMOTE_HARDWARE_APP',
|
||||||
|
'payload': b'\x08\x05\x18\x80 ',
|
||||||
|
'requestId': 1629980484,
|
||||||
|
'remotehw': {
|
||||||
|
'typ': 'READ_GPIOS_REPLY',
|
||||||
|
'gpioValue': '4096',
|
||||||
|
'raw': 'faked',
|
||||||
|
'id': 1693085229,
|
||||||
|
'rxTime': 1640294262,
|
||||||
|
'rxSnr': 4.75,
|
||||||
|
'hopLimit': 3,
|
||||||
|
'wantAck': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch('time.sleep', side_effect=my_sleep):
|
||||||
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
|
iface.localNode.getChannelByName.return_value = channel
|
||||||
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
main()
|
||||||
|
onGPIOreceive(packet, mo)
|
||||||
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
|
assert pytest_wrapped_e.value.code == 3
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Watching GPIO mask 0x1000 ', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
|
def test_main_gpio_wrb(caplog, capsys):
|
||||||
|
"""Test --gpio_wrb with a named gpio channel"""
|
||||||
|
sys.argv = ['', '--gpio-wrb', '4', '1', '--dest', '!1234']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
channel = Channel(index=1, role=1)
|
||||||
|
channel.settings.modem_config = 3
|
||||||
|
channel.settings.psk = b'\x01'
|
||||||
|
|
||||||
|
packet = {
|
||||||
|
|
||||||
|
'from': 682968668,
|
||||||
|
'to': 682968612,
|
||||||
|
'channel': 1,
|
||||||
|
'decoded': {
|
||||||
|
'portnum': 'REMOTE_HARDWARE_APP',
|
||||||
|
'payload': b'\x08\x05\x18\x80 ',
|
||||||
|
'requestId': 1629980484,
|
||||||
|
'remotehw': {
|
||||||
|
'typ': 'READ_GPIOS_REPLY',
|
||||||
|
'gpioValue': '16',
|
||||||
|
'raw': 'faked',
|
||||||
|
'id': 1693085229,
|
||||||
|
'rxTime': 1640294262,
|
||||||
|
'rxSnr': 4.75,
|
||||||
|
'hopLimit': 3,
|
||||||
|
'wantAck': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
|
iface.localNode.getChannelByName.return_value = channel
|
||||||
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
main()
|
||||||
|
onGPIOreceive(packet, mo)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Writing GPIO mask 0x10 with value 0x10 to !1234', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Received RemoteHardware typ=READ_GPIOS_REPLY, gpio_value=16 value=0', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_globals")
|
@pytest.mark.usefixtures("reset_globals")
|
||||||
def test_main_getPref_valid_field(capsys):
|
def test_main_getPref_valid_field(capsys):
|
||||||
|
|||||||
@@ -55,6 +55,15 @@ def test_setOwner_and_team(caplog):
|
|||||||
assert re.search(r'p.set_owner.team:1', caplog.text, re.MULTILINE)
|
assert re.search(r'p.set_owner.team:1', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_setOwnerShort(caplog):
|
||||||
|
"""Test setOwner"""
|
||||||
|
anode = Node('foo', 'bar', noProto=True)
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
anode.setOwner(long_name=None, short_name='123')
|
||||||
|
assert re.search(r'p.set_owner.short_name:123:', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_setOwner_no_short_name(caplog):
|
def test_setOwner_no_short_name(caplog):
|
||||||
"""Test setOwner"""
|
"""Test setOwner"""
|
||||||
|
|||||||
2
proto
2
proto
Submodule proto updated: 62cb78fcbe...07ed86d8b4
2
setup.py
2
setup.py
@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
|
|||||||
# This call to setup() does all the work
|
# This call to setup() does all the work
|
||||||
setup(
|
setup(
|
||||||
name="meshtastic",
|
name="meshtastic",
|
||||||
version="1.2.58",
|
version="1.2.77",
|
||||||
description="Python API & client shell for talking to Meshtastic devices",
|
description="Python API & client shell for talking to Meshtastic devices",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
|
|||||||
7
standalone_readme.txt
Normal file
7
standalone_readme.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
See https://meshtastic.org/docs/software/python/python-standalone for more info.
|
||||||
Reference in New Issue
Block a user